| package org.bouncycastle.jce.provider; |
| |
| import java.io.IOException; |
| import java.security.InvalidAlgorithmParameterException; |
| import java.security.PublicKey; |
| import java.security.cert.*; |
| 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; |
| |
| /** |
| * Implements the PKIX CertPathBuilding algorithem for BouncyCastle. |
| * <br /> |
| * <b>MAYBE: implement more CertPath validation whil build path to omit invalid pathes</b> |
| * |
| * @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 |
| { |
| if (!(params instanceof PKIXBuilderParameters)) |
| { |
| throw new InvalidAlgorithmParameterException("params must be a PKIXBuilderParameters instance"); |
| } |
| |
| PKIXBuilderParameters pkixParams = (PKIXBuilderParameters)params; |
| |
| Collection targets; |
| Iterator targetIter; |
| List certPathList = new ArrayList(); |
| X509Certificate cert; |
| Collection certs; |
| CertPath certPath = null; |
| Exception certPathException = null; |
| |
| // search target certificates |
| CertSelector certSelect = pkixParams.getTargetCertConstraints(); |
| if (certSelect == null) |
| { |
| throw new CertPathBuilderException("targetCertConstraints must be non-null for CertPath building"); |
| } |
| |
| try |
| { |
| targets = findCertificates(certSelect, pkixParams.getCertStores()); |
| } |
| catch (CertStoreException e) |
| { |
| throw new CertPathBuilderException(e); |
| } |
| |
| if (targets.isEmpty()) |
| { |
| throw new CertPathBuilderException("no certificate found matching targetCertContraints"); |
| } |
| |
| CertificateFactory cFact; |
| CertPathValidator validator; |
| |
| try |
| { |
| cFact = CertificateFactory.getInstance("X.509", "BC"); |
| validator = CertPathValidator.getInstance("PKIX", "BC"); |
| } |
| catch (Exception e) |
| { |
| throw new CertPathBuilderException("exception creating support classes: " + e); |
| } |
| |
| // |
| // check all potential target certificates |
| targetIter = targets.iterator(); |
| while (targetIter.hasNext()) |
| { |
| cert = (X509Certificate)targetIter.next(); |
| certPathList.clear(); |
| while (cert != null) |
| { |
| // add cert to the certpath |
| certPathList.add(cert); |
| |
| // check wether the issuer of <cert> is a TrustAnchor |
| if (findTrustAnchor(cert, pkixParams.getTrustAnchors()) != null) |
| { |
| try |
| { |
| certPath = cFact.generateCertPath(certPathList); |
| |
| PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult)validator.validate(certPath, pkixParams); |
| |
| return new PKIXCertPathBuilderResult(certPath, |
| result.getTrustAnchor(), |
| result.getPolicyTree(), |
| result.getPublicKey()); |
| } |
| catch (CertificateException ex) |
| { |
| certPathException = ex; |
| } |
| catch (CertPathValidatorException ex) |
| { |
| certPathException = ex; |
| } |
| // if validation failed go to next certificate |
| cert = null; |
| } |
| else |
| { |
| // try to get the issuer certificate from one |
| // of the CertStores |
| try |
| { |
| X509Certificate issuer = findIssuer(cert, pkixParams.getCertStores()); |
| if (issuer.equals(cert)) |
| { |
| cert = null; |
| } |
| else |
| { |
| cert = issuer; |
| } |
| } |
| catch (CertPathValidatorException ex) |
| { |
| certPathException = ex; |
| cert = null; |
| } |
| } |
| } |
| } |
| |
| if (certPath != null) |
| { |
| throw new CertPathBuilderException("found certificate chain, but could not be validated", certPathException); |
| } |
| |
| throw new CertPathBuilderException("unable to find certificate chain"); |
| } |
| |
| /** |
| * Search the given Set of TrustAnchor's for one that is the |
| * issuer of the fiven X509 certificate. |
| * |
| * @param cert the X509 certificate |
| * @param trustAnchors a Set of TrustAnchor's |
| * |
| * @return the <code>TrustAnchor</code> object if found or |
| * <code>null</code> if not. |
| * |
| * @exception CertPathValidatorException if a TrustAnchor was |
| * found but the signature verificytion on the given certificate |
| * has thrown an exception. This Exception can be obtainted with |
| * <code>getCause()</code> method. |
| **/ |
| final TrustAnchor findTrustAnchor( |
| X509Certificate cert, |
| Set trustAnchors) |
| throws CertPathBuilderException |
| { |
| Iterator iter = trustAnchors.iterator(); |
| TrustAnchor trust = null; |
| PublicKey trustPublicKey = null; |
| Exception invalidKeyEx = null; |
| |
| X509CertSelector certSelectX509 = new X509CertSelector(); |
| |
| try |
| { |
| certSelectX509.setSubject(cert.getIssuerX500Principal().getEncoded()); |
| } |
| catch (IOException ex) |
| { |
| throw new CertPathBuilderException("can't get trust anchor principal",null); |
| } |
| |
| while (iter.hasNext() && trust == null) |
| { |
| trust = (TrustAnchor)iter.next(); |
| if (trust.getTrustedCert() != null) |
| { |
| if (certSelectX509.match(trust.getTrustedCert())) |
| { |
| trustPublicKey = trust.getTrustedCert().getPublicKey(); |
| } |
| else |
| { |
| trust = null; |
| } |
| } |
| else if (trust.getCAName() != null |
| && trust.getCAPublicKey() != null) |
| { |
| try |
| { |
| X500Principal certIssuer = cert.getIssuerX500Principal(); |
| X500Principal caName = new X500Principal(trust.getCAName()); |
| if (certIssuer.equals(caName)) |
| { |
| trustPublicKey = trust.getCAPublicKey(); |
| } |
| else |
| { |
| trust = null; |
| } |
| } |
| catch (IllegalArgumentException ex) |
| { |
| trust = null; |
| } |
| } |
| else |
| { |
| trust = null; |
| } |
| |
| if (trustPublicKey != null) |
| { |
| try |
| { |
| cert.verify(trustPublicKey); |
| } |
| catch (Exception ex) |
| { |
| invalidKeyEx = ex; |
| trust = null; |
| } |
| } |
| } |
| |
| if (trust == null && invalidKeyEx != null) |
| { |
| throw new CertPathBuilderException("TrustAnchor found put certificate validation failed",invalidKeyEx); |
| } |
| |
| return trust; |
| } |
| |
| /** |
| * Return a Collection of all certificates found in the |
| * CertStore's that are matching the certSelect criteriums. |
| * |
| * @param certSelector a {@link CertSelector CertSelector} |
| * object that will be used to select the certificates |
| * @param certStores a List containing only {@link CertStore |
| * CertStore} objects. These are used to search for |
| * certificates |
| * |
| * @return a Collection of all found {@link Certificate Certificate} |
| * objects. May be empty but never <code>null</code>. |
| **/ |
| private final Collection findCertificates( |
| CertSelector certSelect, |
| List certStores) |
| throws CertStoreException |
| { |
| Set certs = new HashSet(); |
| Iterator iter = certStores.iterator(); |
| |
| while (iter.hasNext()) |
| { |
| CertStore certStore = (CertStore)iter.next(); |
| |
| certs.addAll(certStore.getCertificates(certSelect)); |
| } |
| |
| return certs; |
| } |
| |
| /** |
| * Find the issuer certificate of the given certificate. |
| * |
| * @param cert the certificate hows issuer certificate should |
| * be found. |
| * @param certStores a list of <code>CertStore</code> object |
| * that will be searched |
| * |
| * @return then <code>X509Certificate</code> object containing |
| * the issuer certificate or <code>null</code> if not found |
| * |
| * @exception CertPathValidatorException if a TrustAnchor was |
| * found but the signature verificytion on the given certificate |
| * has thrown an exception. This Exception can be obtainted with |
| * <code>getCause()</code> method. |
| **/ |
| private final X509Certificate findIssuer( |
| X509Certificate cert, |
| List certStores) |
| throws CertPathValidatorException |
| { |
| Exception invalidKeyEx = null; |
| X509CertSelector certSelect = new X509CertSelector(); |
| try |
| { |
| certSelect.setSubject(cert.getIssuerX500Principal().getEncoded()); |
| } |
| catch (IOException ex) |
| { |
| throw new CertPathValidatorException("Issuer not found", null, null, -1); |
| } |
| |
| Iterator iter; |
| try |
| { |
| iter = findCertificates(certSelect, certStores).iterator(); |
| } |
| catch (CertStoreException e) |
| { |
| throw new CertPathValidatorException(e); |
| } |
| |
| X509Certificate issuer = null; |
| while (iter.hasNext() && issuer == null) |
| { |
| issuer = (X509Certificate)iter.next(); |
| try |
| { |
| cert.verify(issuer.getPublicKey()); |
| } |
| catch (Exception ex) |
| { |
| invalidKeyEx = ex; |
| issuer = null; |
| } |
| } |
| |
| if (issuer == null && invalidKeyEx == null) |
| { |
| throw new CertPathValidatorException("Issuer not found", null, null, -1); |
| } |
| |
| if (issuer == null && invalidKeyEx != null) |
| { |
| throw new CertPathValidatorException("issuer found but certificate validation failed",invalidKeyEx,null,-1); |
| } |
| |
| return issuer; |
| } |
| } |