package org.bouncycastle.jce.provider; import java.security.InvalidAlgorithmParameterException; import java.security.cert.CertPath; import java.security.cert.CertPathBuilderException; import java.security.cert.CertPathBuilderResult; import java.security.cert.CertPathBuilderSpi; import java.security.cert.CertPathParameters; import java.security.cert.CertificateParsingException; import java.security.cert.PKIXBuilderParameters; import java.security.cert.PKIXCertPathBuilderResult; import java.security.cert.PKIXCertPathValidatorResult; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import org.bouncycastle.asn1.x509.Extension; import org.bouncycastle.jcajce.PKIXCertStore; import org.bouncycastle.jcajce.PKIXCertStoreSelector; import org.bouncycastle.jcajce.PKIXExtendedBuilderParameters; import org.bouncycastle.jcajce.PKIXExtendedParameters; import org.bouncycastle.jcajce.provider.asymmetric.x509.CertificateFactory; import org.bouncycastle.jce.exception.ExtCertPathBuilderException; import org.bouncycastle.x509.ExtendedPKIXBuilderParameters; import org.bouncycastle.x509.ExtendedPKIXParameters; /** * Implements the PKIX CertPathBuilding algorithm for BouncyCastle. * * @see CertPathBuilderSpi */ public class PKIXCertPathBuilderSpi extends CertPathBuilderSpi { /** * Build and validate a CertPath using the given parameter. * * @param params PKIXBuilderParameters object containing all information to * build the CertPath */ public CertPathBuilderResult engineBuild(CertPathParameters params) throws CertPathBuilderException, InvalidAlgorithmParameterException { PKIXExtendedBuilderParameters paramsPKIX; if (params instanceof PKIXBuilderParameters) { PKIXExtendedParameters.Builder paramsPKIXBldr = new PKIXExtendedParameters.Builder((PKIXBuilderParameters)params); PKIXExtendedBuilderParameters.Builder paramsBldrPKIXBldr; if (params instanceof ExtendedPKIXParameters) { ExtendedPKIXBuilderParameters extPKIX = (ExtendedPKIXBuilderParameters)params; ; for (Iterator it = extPKIX.getAdditionalStores().iterator(); it.hasNext();) { paramsPKIXBldr.addCertificateStore((PKIXCertStore)it.next()); } paramsBldrPKIXBldr = new PKIXExtendedBuilderParameters.Builder(paramsPKIXBldr.build()); paramsBldrPKIXBldr.addExcludedCerts(extPKIX.getExcludedCerts()); paramsBldrPKIXBldr.setMaxPathLength(extPKIX.getMaxPathLength()); } else { paramsBldrPKIXBldr = new PKIXExtendedBuilderParameters.Builder((PKIXBuilderParameters)params); } paramsPKIX = paramsBldrPKIXBldr.build(); } else if (params instanceof PKIXExtendedBuilderParameters) { paramsPKIX = (PKIXExtendedBuilderParameters)params; } else { throw new InvalidAlgorithmParameterException( "Parameters must be an instance of " + PKIXBuilderParameters.class.getName() + " or " + PKIXExtendedBuilderParameters.class.getName() + "."); } Collection targets; Iterator targetIter; List certPathList = new ArrayList(); X509Certificate cert; // search target certificates PKIXCertStoreSelector certSelect = paramsPKIX.getBaseParameters().getTargetConstraints(); try { targets = CertPathValidatorUtilities.findCertificates(certSelect, paramsPKIX.getBaseParameters().getCertificateStores()); targets.addAll(CertPathValidatorUtilities.findCertificates(certSelect, paramsPKIX.getBaseParameters().getCertStores())); } catch (AnnotatedException e) { throw new ExtCertPathBuilderException( "Error finding target certificate.", e); } if (targets.isEmpty()) { throw new CertPathBuilderException( "No certificate found matching targetContraints."); } CertPathBuilderResult result = null; // check all potential target certificates targetIter = targets.iterator(); while (targetIter.hasNext() && result == null) { cert = (X509Certificate) targetIter.next(); result = build(cert, paramsPKIX, certPathList); } if (result == null && certPathException != null) { if (certPathException instanceof AnnotatedException) { throw new CertPathBuilderException(certPathException.getMessage(), certPathException.getCause()); } throw new CertPathBuilderException( "Possible certificate chain could not be validated.", certPathException); } if (result == null && certPathException == null) { throw new CertPathBuilderException( "Unable to find certificate chain."); } return result; } private Exception certPathException; protected CertPathBuilderResult build(X509Certificate tbvCert, PKIXExtendedBuilderParameters pkixParams, List tbvPath) { // If tbvCert is readily present in tbvPath, it indicates having run // into a cycle in the // PKI graph. if (tbvPath.contains(tbvCert)) { return null; } // step out, the certificate is not allowed to appear in a certification // chain. if (pkixParams.getExcludedCerts().contains(tbvCert)) { return null; } // test if certificate path exceeds maximum length if (pkixParams.getMaxPathLength() != -1) { if (tbvPath.size() - 1 > pkixParams.getMaxPathLength()) { return null; } } tbvPath.add(tbvCert); CertificateFactory cFact; PKIXCertPathValidatorSpi validator; CertPathBuilderResult builderResult = null; try { cFact = new CertificateFactory(); validator = new PKIXCertPathValidatorSpi(); } catch (Exception e) { // cannot happen throw new RuntimeException("Exception creating support classes."); } try { // check whether the issuer of <tbvCert> is a TrustAnchor if (CertPathValidatorUtilities.findTrustAnchor(tbvCert, pkixParams.getBaseParameters().getTrustAnchors(), pkixParams.getBaseParameters().getSigProvider()) != null) { // exception message from possibly later tried certification // chains CertPath certPath = null; PKIXCertPathValidatorResult result = null; try { certPath = cFact.engineGenerateCertPath(tbvPath); } catch (Exception e) { throw new AnnotatedException( "Certification path could not be constructed from certificate list.", e); } try { result = (PKIXCertPathValidatorResult) validator.engineValidate( certPath, pkixParams); } catch (Exception e) { throw new AnnotatedException( "Certification path could not be validated.", e); } return new PKIXCertPathBuilderResult(certPath, result .getTrustAnchor(), result.getPolicyTree(), result .getPublicKey()); } else { List stores = new ArrayList(); stores.addAll(pkixParams.getBaseParameters().getCertificateStores()); // add additional X.509 stores from locations in certificate try { stores.addAll(CertPathValidatorUtilities.getAdditionalStoresFromAltNames( tbvCert.getExtensionValue(Extension.issuerAlternativeName.getId()), pkixParams.getBaseParameters().getNamedCertificateStoreMap())); } catch (CertificateParsingException e) { throw new AnnotatedException( "No additional X.509 stores can be added from certificate locations.", e); } Collection issuers = new HashSet(); // try to get the issuer certificate from one // of the stores try { issuers.addAll(CertPathValidatorUtilities.findIssuerCerts(tbvCert, pkixParams.getBaseParameters().getCertStores(), stores)); } catch (AnnotatedException e) { throw new AnnotatedException( "Cannot find issuer certificate for certificate in certification path.", e); } if (issuers.isEmpty()) { throw new AnnotatedException( "No issuer certificate for certificate in certification path found."); } Iterator it = issuers.iterator(); while (it.hasNext() && builderResult == null) { X509Certificate issuer = (X509Certificate) it.next(); builderResult = build(issuer, pkixParams, tbvPath); } } } catch (AnnotatedException e) { certPathException = e; } if (builderResult == null) { tbvPath.remove(tbvCert); } return builderResult; } }