1 package org.bouncycastle.jce.provider; 2 3 import java.security.InvalidAlgorithmParameterException; 4 import java.security.cert.CertPath; 5 import java.security.cert.CertPathBuilderException; 6 import java.security.cert.CertPathBuilderResult; 7 import java.security.cert.CertPathBuilderSpi; 8 import java.security.cert.CertPathParameters; 9 import java.security.cert.CertPathValidator; 10 import java.security.cert.CertificateFactory; 11 import java.security.cert.CertificateParsingException; 12 import java.security.cert.PKIXBuilderParameters; 13 import java.security.cert.PKIXCertPathBuilderResult; 14 import java.security.cert.PKIXCertPathValidatorResult; 15 import java.security.cert.X509Certificate; 16 import java.util.ArrayList; 17 import java.util.Collection; 18 import java.util.HashSet; 19 import java.util.Iterator; 20 import java.util.List; 21 22 import org.bouncycastle.jce.exception.ExtCertPathBuilderException; 23 import org.bouncycastle.util.Selector; 24 import org.bouncycastle.x509.ExtendedPKIXBuilderParameters; 25 import org.bouncycastle.x509.X509CertStoreSelector; 26 27 /** 28 * Implements the PKIX CertPathBuilding algorithm for BouncyCastle. 29 * 30 * @see CertPathBuilderSpi 31 */ 32 public class PKIXCertPathBuilderSpi 33 extends CertPathBuilderSpi 34 { 35 /** 36 * Build and validate a CertPath using the given parameter. 37 * 38 * @param params PKIXBuilderParameters object containing all information to 39 * build the CertPath 40 */ 41 public CertPathBuilderResult engineBuild(CertPathParameters params) 42 throws CertPathBuilderException, InvalidAlgorithmParameterException 43 { 44 if (!(params instanceof PKIXBuilderParameters) 45 && !(params instanceof ExtendedPKIXBuilderParameters)) 46 { 47 throw new InvalidAlgorithmParameterException( 48 "Parameters must be an instance of " 49 + PKIXBuilderParameters.class.getName() + " or " 50 + ExtendedPKIXBuilderParameters.class.getName() + "."); 51 } 52 53 ExtendedPKIXBuilderParameters pkixParams = null; 54 if (params instanceof ExtendedPKIXBuilderParameters) 55 { 56 pkixParams = (ExtendedPKIXBuilderParameters) params; 57 } 58 else 59 { 60 pkixParams = (ExtendedPKIXBuilderParameters) ExtendedPKIXBuilderParameters 61 .getInstance((PKIXBuilderParameters) params); 62 } 63 64 Collection targets; 65 Iterator targetIter; 66 List certPathList = new ArrayList(); 67 X509Certificate cert; 68 69 // search target certificates 70 71 Selector certSelect = pkixParams.getTargetConstraints(); 72 if (!(certSelect instanceof X509CertStoreSelector)) 73 { 74 throw new CertPathBuilderException( 75 "TargetConstraints must be an instance of " 76 + X509CertStoreSelector.class.getName() + " for " 77 + this.getClass().getName() + " class."); 78 } 79 80 try 81 { 82 targets = CertPathValidatorUtilities.findCertificates((X509CertStoreSelector)certSelect, pkixParams.getStores()); 83 targets.addAll(CertPathValidatorUtilities.findCertificates((X509CertStoreSelector)certSelect, pkixParams.getCertStores())); 84 } 85 catch (AnnotatedException e) 86 { 87 throw new ExtCertPathBuilderException( 88 "Error finding target certificate.", e); 89 } 90 91 if (targets.isEmpty()) 92 { 93 94 throw new CertPathBuilderException( 95 "No certificate found matching targetContraints."); 96 } 97 98 CertPathBuilderResult result = null; 99 100 // check all potential target certificates 101 targetIter = targets.iterator(); 102 while (targetIter.hasNext() && result == null) 103 { 104 cert = (X509Certificate) targetIter.next(); 105 result = build(cert, pkixParams, certPathList); 106 } 107 108 if (result == null && certPathException != null) 109 { 110 if (certPathException instanceof AnnotatedException) 111 { 112 throw new CertPathBuilderException(certPathException.getMessage(), certPathException.getCause()); 113 } 114 throw new CertPathBuilderException( 115 "Possible certificate chain could not be validated.", 116 certPathException); 117 } 118 119 if (result == null && certPathException == null) 120 { 121 throw new CertPathBuilderException( 122 "Unable to find certificate chain."); 123 } 124 125 return result; 126 } 127 128 private Exception certPathException; 129 130 protected CertPathBuilderResult build(X509Certificate tbvCert, 131 ExtendedPKIXBuilderParameters pkixParams, List tbvPath) 132 { 133 // If tbvCert is readily present in tbvPath, it indicates having run 134 // into a cycle in the 135 // PKI graph. 136 if (tbvPath.contains(tbvCert)) 137 { 138 return null; 139 } 140 // step out, the certificate is not allowed to appear in a certification 141 // chain. 142 if (pkixParams.getExcludedCerts().contains(tbvCert)) 143 { 144 return null; 145 } 146 // test if certificate path exceeds maximum length 147 if (pkixParams.getMaxPathLength() != -1) 148 { 149 if (tbvPath.size() - 1 > pkixParams.getMaxPathLength()) 150 { 151 return null; 152 } 153 } 154 155 tbvPath.add(tbvCert); 156 157 CertificateFactory cFact; 158 CertPathValidator validator; 159 CertPathBuilderResult builderResult = null; 160 161 try 162 { 163 cFact = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME); 164 validator = CertPathValidator.getInstance("PKIX", BouncyCastleProvider.PROVIDER_NAME); 165 } 166 catch (Exception e) 167 { 168 // cannot happen 169 throw new RuntimeException("Exception creating support classes."); 170 } 171 172 try 173 { 174 // check whether the issuer of <tbvCert> is a TrustAnchor 175 if (CertPathValidatorUtilities.findTrustAnchor(tbvCert, pkixParams.getTrustAnchors(), 176 pkixParams.getSigProvider()) != null) 177 { 178 // exception message from possibly later tried certification 179 // chains 180 CertPath certPath = null; 181 PKIXCertPathValidatorResult result = null; 182 try 183 { 184 certPath = cFact.generateCertPath(tbvPath); 185 } 186 catch (Exception e) 187 { 188 throw new AnnotatedException( 189 "Certification path could not be constructed from certificate list.", 190 e); 191 } 192 193 try 194 { 195 result = (PKIXCertPathValidatorResult) validator.validate( 196 certPath, pkixParams); 197 } 198 catch (Exception e) 199 { 200 throw new AnnotatedException( 201 "Certification path could not be validated.", e); 202 } 203 204 return new PKIXCertPathBuilderResult(certPath, result 205 .getTrustAnchor(), result.getPolicyTree(), result 206 .getPublicKey()); 207 208 } 209 else 210 { 211 // add additional X.509 stores from locations in certificate 212 try 213 { 214 CertPathValidatorUtilities.addAdditionalStoresFromAltNames( 215 tbvCert, pkixParams); 216 } 217 catch (CertificateParsingException e) 218 { 219 throw new AnnotatedException( 220 "No additiontal X.509 stores can be added from certificate locations.", 221 e); 222 } 223 Collection issuers = new HashSet(); 224 // try to get the issuer certificate from one 225 // of the stores 226 try 227 { 228 issuers.addAll(CertPathValidatorUtilities.findIssuerCerts(tbvCert, pkixParams)); 229 } 230 catch (AnnotatedException e) 231 { 232 throw new AnnotatedException( 233 "Cannot find issuer certificate for certificate in certification path.", 234 e); 235 } 236 if (issuers.isEmpty()) 237 { 238 throw new AnnotatedException( 239 "No issuer certificate for certificate in certification path found."); 240 } 241 Iterator it = issuers.iterator(); 242 243 while (it.hasNext() && builderResult == null) 244 { 245 X509Certificate issuer = (X509Certificate) it.next(); 246 builderResult = build(issuer, pkixParams, tbvPath); 247 } 248 } 249 } 250 catch (AnnotatedException e) 251 { 252 certPathException = e; 253 } 254 if (builderResult == null) 255 { 256 tbvPath.remove(tbvCert); 257 } 258 return builderResult; 259 } 260 261 } 262