package org.bouncycastle.jce.provider;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.Principal;
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.CertPathValidator;
import java.security.cert.CertStore;
import java.security.cert.CertStoreException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
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 java.util.Set;

import javax.security.auth.x500.X500Principal;

import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.jcajce.PKIXCertStoreSelector;
import org.bouncycastle.jcajce.PKIXExtendedBuilderParameters;
import org.bouncycastle.jce.exception.ExtCertPathBuilderException;
import org.bouncycastle.util.Encodable;
import org.bouncycastle.util.Selector;
import org.bouncycastle.util.Store;
import org.bouncycastle.util.StoreException;
import org.bouncycastle.x509.ExtendedPKIXBuilderParameters;
import org.bouncycastle.x509.ExtendedPKIXParameters;
import org.bouncycastle.x509.X509AttributeCertStoreSelector;
import org.bouncycastle.x509.X509AttributeCertificate;
import org.bouncycastle.x509.X509CertStoreSelector;
import org.bouncycastle.x509.X509Store;

public class PKIXAttrCertPathBuilderSpi
    extends CertPathBuilderSpi
{

    
Build and validate a CertPath using the given parameter.
Params:
  • params – PKIXBuilderParameters object containing all information to build the CertPath
/** * 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 { if (!(params instanceof PKIXBuilderParameters) && !(params instanceof ExtendedPKIXBuilderParameters) && !(params instanceof PKIXExtendedBuilderParameters)) { throw new InvalidAlgorithmParameterException( "Parameters must be an instance of " + PKIXBuilderParameters.class.getName() + " or " + PKIXExtendedBuilderParameters.class.getName() + "."); } List targetStores = new ArrayList(); PKIXExtendedBuilderParameters paramsPKIX; if (params instanceof PKIXBuilderParameters) { PKIXExtendedBuilderParameters.Builder paramsPKIXBldr = new PKIXExtendedBuilderParameters.Builder((PKIXBuilderParameters)params); if (params instanceof ExtendedPKIXParameters) { ExtendedPKIXBuilderParameters extPKIX = (ExtendedPKIXBuilderParameters)params; paramsPKIXBldr.addExcludedCerts(extPKIX.getExcludedCerts()); paramsPKIXBldr.setMaxPathLength(extPKIX.getMaxPathLength()); targetStores = extPKIX.getStores(); } paramsPKIX = paramsPKIXBldr.build(); } else { paramsPKIX = (PKIXExtendedBuilderParameters)params; } Collection targets; Iterator targetIter; List certPathList = new ArrayList(); X509AttributeCertificate cert; // search target certificates Selector certSelect = paramsPKIX.getBaseParameters().getTargetConstraints(); if (!(certSelect instanceof X509AttributeCertStoreSelector)) { throw new CertPathBuilderException( "TargetConstraints must be an instance of " + X509AttributeCertStoreSelector.class.getName() + " for "+this.getClass().getName()+" class."); } try { targets = findCertificates((X509AttributeCertStoreSelector)certSelect, targetStores); } catch (AnnotatedException e) { throw new ExtCertPathBuilderException("Error finding target attribute certificate.", e); } if (targets.isEmpty()) { throw new CertPathBuilderException( "No attribute certificate found matching targetContraints."); } CertPathBuilderResult result = null; // check all potential target certificates targetIter = targets.iterator(); while (targetIter.hasNext() && result == null) { cert = (X509AttributeCertificate) targetIter.next(); X509CertStoreSelector selector = new X509CertStoreSelector(); Principal[] principals = cert.getIssuer().getPrincipals(); Set issuers = new HashSet(); for (int i = 0; i < principals.length; i++) { try { if (principals[i] instanceof X500Principal) { selector.setSubject(((X500Principal)principals[i]).getEncoded()); } PKIXCertStoreSelector certStoreSelector = new PKIXCertStoreSelector.Builder(selector).build(); issuers.addAll(CertPathValidatorUtilities.findCertificates(certStoreSelector, paramsPKIX.getBaseParameters().getCertStores())); issuers.addAll(CertPathValidatorUtilities.findCertificates(certStoreSelector, paramsPKIX.getBaseParameters().getCertificateStores())); } catch (AnnotatedException e) { throw new ExtCertPathBuilderException( "Public key certificate for attribute certificate cannot be searched.", e); } catch (IOException e) { throw new ExtCertPathBuilderException( "cannot encode X500Principal.", e); } } if (issuers.isEmpty()) { throw new CertPathBuilderException( "Public key certificate for attribute certificate cannot be found."); } Iterator it = issuers.iterator(); while (it.hasNext() && result == null) { result = build(cert, (X509Certificate)it.next(), paramsPKIX, certPathList); } } if (result == null && certPathException != null) { throw new ExtCertPathBuilderException( "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; private CertPathBuilderResult build(X509AttributeCertificate attrCert, 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; CertPathValidator validator; CertPathBuilderResult builderResult = null; try { cFact = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME); validator = CertPathValidator.getInstance("RFC3281", BouncyCastleProvider.PROVIDER_NAME); } catch (Exception e) { // cannot happen throw new RuntimeException( "Exception creating support classes."); } try { // check whether the issuer of <tbvCert> is a TrustAnchor if (CertPathValidatorUtilities.isIssuerTrustAnchor(tbvCert, pkixParams.getBaseParameters().getTrustAnchors(), pkixParams.getBaseParameters().getSigProvider())) { CertPath certPath; PKIXCertPathValidatorResult result; try { certPath = cFact.generateCertPath(tbvPath); } catch (Exception e) { throw new AnnotatedException( "Certification path could not be constructed from certificate list.", e); } try { result = (PKIXCertPathValidatorResult) validator.validate( 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(); // TODO Use CertPathValidatorUtilities.isSelfIssued(issuer)? // if untrusted self signed certificate continue if (issuer.getIssuerX500Principal().equals( issuer.getSubjectX500Principal())) { continue; } builderResult = build(attrCert, issuer, pkixParams, tbvPath); } } } catch (AnnotatedException e) { certPathException = new AnnotatedException( "No valid certification path could be build.", e); } if (builderResult == null) { tbvPath.remove(tbvCert); } return builderResult; } protected static Collection findCertificates(X509AttributeCertStoreSelector certSelect, List certStores) throws AnnotatedException { Set certs = new HashSet(); Iterator iter = certStores.iterator(); while (iter.hasNext()) { Object obj = iter.next(); if (obj instanceof Store) { Store certStore = (Store)obj; try { certs.addAll(certStore.getMatches(certSelect)); } catch (StoreException e) { throw new AnnotatedException( "Problem while picking certificates from X.509 store.", e); } } } return certs; } }