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