001: package org.bouncycastle.jce.provider;
002:
003: import org.bouncycastle.asn1.x509.GeneralName;
004: import org.bouncycastle.jce.exception.ExtCertPathBuilderException;
005: import org.bouncycastle.util.Selector;
006: import org.bouncycastle.x509.ExtendedPKIXBuilderParameters;
007: import org.bouncycastle.x509.ExtendedPKIXParameters;
008: import org.bouncycastle.x509.X509AttributeCertStoreSelector;
009: import org.bouncycastle.x509.X509AttributeCertificate;
010: import org.bouncycastle.x509.X509CertStoreSelector;
011:
012: import javax.security.auth.x500.X500Principal;
013: import java.io.IOException;
014: import java.security.InvalidAlgorithmParameterException;
015: import java.security.Principal;
016: import java.security.PublicKey;
017: import java.security.cert.CertPath;
018: import java.security.cert.CertPathBuilderException;
019: import java.security.cert.CertPathBuilderResult;
020: import java.security.cert.CertPathBuilderSpi;
021: import java.security.cert.CertPathParameters;
022: import java.security.cert.CertPathValidator;
023: import java.security.cert.CertificateFactory;
024: import java.security.cert.CertificateParsingException;
025: import java.security.cert.PKIXBuilderParameters;
026: import java.security.cert.PKIXCertPathBuilderResult;
027: import java.security.cert.PKIXCertPathValidatorResult;
028: import java.security.cert.TrustAnchor;
029: import java.security.cert.X509CertSelector;
030: import java.security.cert.X509Certificate;
031: import java.util.ArrayList;
032: import java.util.Collection;
033: import java.util.HashSet;
034: import java.util.Iterator;
035: import java.util.List;
036: import java.util.Set;
037:
038: public class PKIXAttrCertPathBuilderSpi extends CertPathBuilderSpi {
039:
040: /**
041: * Build and validate a CertPath using the given parameter.
042: *
043: * @param params PKIXBuilderParameters object containing all information to
044: * build the CertPath
045: */
046: public CertPathBuilderResult engineBuild(CertPathParameters params)
047: throws CertPathBuilderException,
048: InvalidAlgorithmParameterException {
049: if (!(params instanceof PKIXBuilderParameters)
050: && !(params instanceof ExtendedPKIXBuilderParameters)) {
051: throw new InvalidAlgorithmParameterException(
052: "Parameters must be an instance of "
053: + PKIXBuilderParameters.class.getName()
054: + " or "
055: + ExtendedPKIXBuilderParameters.class
056: .getName() + ".");
057: }
058:
059: ExtendedPKIXBuilderParameters pkixParams;
060: if (params instanceof ExtendedPKIXBuilderParameters) {
061: pkixParams = (ExtendedPKIXBuilderParameters) params;
062: } else {
063: pkixParams = (ExtendedPKIXBuilderParameters) ExtendedPKIXBuilderParameters
064: .getInstance((PKIXBuilderParameters) params);
065: }
066:
067: Collection targets;
068: Iterator targetIter;
069: List certPathList = new ArrayList();
070: X509AttributeCertificate cert;
071:
072: // search target certificates
073:
074: Selector certSelect = pkixParams.getTargetConstraints();
075: if (!(certSelect instanceof X509AttributeCertStoreSelector)) {
076: throw new CertPathBuilderException(
077: "TargetConstraints must be an instance of "
078: + X509AttributeCertStoreSelector.class
079: .getName() + " for "
080: + this .getClass().getName() + " class.");
081: }
082:
083: try {
084: targets = CertPathValidatorUtilities.findCertificates(
085: certSelect, pkixParams.getStores());
086: } catch (AnnotatedException e) {
087: throw new ExtCertPathBuilderException(
088: "Error finding target attribute certificate.", e);
089: }
090:
091: if (targets.isEmpty()) {
092: throw new CertPathBuilderException(
093: "No attribute certificate found matching targetContraints.");
094: }
095:
096: CertPathBuilderResult result = null;
097:
098: // check all potential target certificates
099: targetIter = targets.iterator();
100: while (targetIter.hasNext() && result == null) {
101: cert = (X509AttributeCertificate) targetIter.next();
102:
103: X509CertStoreSelector selector = new X509CertStoreSelector();
104: Principal[] principals = cert.getIssuer().getPrincipals();
105: Set issuers = new HashSet();
106: for (int i = 0; i < principals.length; i++) {
107: try {
108: if (principals[i] instanceof X500Principal) {
109: selector
110: .setSubject(((X500Principal) principals[i])
111: .getEncoded());
112: }
113: issuers.addAll(CertPathValidatorUtilities
114: .findCertificates((Selector) selector,
115: pkixParams.getStores()));
116: } catch (AnnotatedException e) {
117: throw new ExtCertPathBuilderException(
118: "Public key certificate for attribute certificate cannot be searched.",
119: e);
120: } catch (IOException e) {
121: throw new ExtCertPathBuilderException(
122: "cannot encode X500Proncipal.", e);
123: }
124: }
125: if (issuers.isEmpty()) {
126: throw new CertPathBuilderException(
127: "Public key certificate for attribute certificate cannot be found.");
128: }
129: Iterator it = issuers.iterator();
130: while (it.hasNext() && result == null) {
131: result = build(cert, (X509Certificate) it.next(),
132: pkixParams, certPathList);
133: }
134: }
135:
136: if (result == null && certPathException != null) {
137: throw new ExtCertPathBuilderException(
138: "Possible certificate chain could not be validated.",
139: certPathException);
140: }
141:
142: if (result == null && certPathException == null) {
143: throw new CertPathBuilderException(
144: "Unable to find certificate chain.");
145: }
146:
147: return result;
148: }
149:
150: private Exception certPathException;
151:
152: private CertPathBuilderResult build(
153: X509AttributeCertificate attrCert, X509Certificate tbvCert,
154: ExtendedPKIXBuilderParameters pkixParams, List tbvPath)
155:
156: {
157: // If tbvCert is readily present in tbvPath, it indicates having run
158: // into a cycle in the
159: // PKI graph.
160: if (tbvPath.contains(tbvCert)) {
161: return null;
162: }
163: // step out, the certificate is not allowed to appear in a certification
164: // chain
165: if (pkixParams.getExcludedCerts().contains(tbvCert)) {
166: return null;
167: }
168: // test if certificate path exceeds maximum length
169: if (pkixParams.getMaxPathLength() != -1) {
170: if (tbvPath.size() - 1 > pkixParams.getMaxPathLength()) {
171: return null;
172: }
173: }
174:
175: tbvPath.add(tbvCert);
176:
177: CertificateFactory cFact;
178: CertPathValidator validator;
179: CertPathBuilderResult builderResult = null;
180:
181: try {
182: cFact = CertificateFactory.getInstance("X.509", "BC");
183: validator = CertPathValidator.getInstance("PKIX", "BC");
184: } catch (Exception e) {
185: // cannot happen
186: throw new RuntimeException(
187: "Exception creating support classes.");
188: }
189:
190: try {
191: // check wether the issuer of <tbvCert> is a TrustAnchor
192: if (findTrustAnchor(tbvCert, pkixParams.getTrustAnchors()) != null) {
193: CertPath certPath;
194: PKIXCertPathValidatorResult result;
195: try {
196: certPath = cFact.generateCertPath(tbvPath);
197: } catch (Exception e) {
198: throw new AnnotatedException(
199: "Certification path could not be constructed from certificate list.",
200: e);
201: }
202:
203: try {
204: result = (PKIXCertPathValidatorResult) validator
205: .validate(certPath, pkixParams);
206: } catch (Exception e) {
207: throw new AnnotatedException(
208: "Certification path could not be validated.",
209: e);
210: }
211:
212: return new PKIXCertPathBuilderResult(certPath, result
213: .getTrustAnchor(), result.getPolicyTree(),
214: result.getPublicKey());
215:
216: } else {
217: // add additional X.509 stores from locations in certificate
218: try {
219: addAdditionalStoresFromAltNames(tbvCert, pkixParams);
220: } catch (CertificateParsingException e) {
221: throw new AnnotatedException(
222: "No additiontal X.509 stores can be added from certificate locations.",
223: e);
224: }
225: Collection issuers = new HashSet();
226: // try to get the issuer certificate from one
227: // of the stores
228: try {
229: issuers.addAll(findIssuerCerts(tbvCert, pkixParams
230: .getStores()));
231: if (issuers.isEmpty()) {
232: issuers.addAll(findIssuerCerts(tbvCert,
233: pkixParams.getAddionalStores()));
234: }
235: } catch (AnnotatedException e) {
236: throw new AnnotatedException(
237: "Cannot find issuer certificate for certificate in certification path.",
238: e);
239: }
240: if (issuers.isEmpty()) {
241: throw new AnnotatedException(
242: "No issuer certificate for certificate in certification path found.");
243: }
244: Iterator it = issuers.iterator();
245:
246: while (it.hasNext() && builderResult == null) {
247: X509Certificate issuer = (X509Certificate) it
248: .next();
249: // if untrusted self signed certificate continue
250: if (issuer.getIssuerX500Principal().equals(
251: issuer.getSubjectX500Principal())) {
252: continue;
253: }
254: builderResult = build(attrCert, issuer, pkixParams,
255: tbvPath);
256: }
257: }
258: } catch (AnnotatedException e) {
259: certPathException = new AnnotatedException(
260: "No valid certification path could be build.", e);
261: }
262: if (builderResult == null) {
263: tbvPath.remove(tbvCert);
264: }
265: return builderResult;
266: }
267:
268: private void addAdditionalStoresFromAltNames(X509Certificate cert,
269: ExtendedPKIXParameters pkixParams)
270: throws CertificateParsingException {
271: // if in the IssuerAltName extension an URI
272: // is given, add an additinal X.509 store
273: if (cert.getIssuerAlternativeNames() != null) {
274: Iterator it = cert.getIssuerAlternativeNames().iterator();
275: while (it.hasNext()) {
276: // look for URI
277: List list = (List) it.next();
278: if (list.get(0).equals(
279: new Integer(
280: GeneralName.uniformResourceIdentifier))) {
281: // found
282: String temp = (String) list.get(1);
283: CertPathValidatorUtilities
284: .addAdditionalStoreFromLocation(temp,
285: pkixParams);
286: }
287: }
288: }
289: }
290:
291: /**
292: * Search the given <code>Set</code> of TrustAnchor's for one that is the
293: * issuer of the given X.509 certificate.
294: *
295: * @param cert The X.509 certificate.
296: * @param trustAnchors A <code>Set</code> of TrustAnchor's
297: *
298: * @return The <code>TrustAnchor</code> object if found or
299: * <code>null</code> if not.
300: *
301: * @exception AnnotatedException if a TrustAnchor was found but the
302: * signature verification on the given certificate has thrown
303: * an exception.
304: */
305: private TrustAnchor findTrustAnchor(X509Certificate cert,
306: Set trustAnchors) throws AnnotatedException {
307: Iterator iter = trustAnchors.iterator();
308: TrustAnchor trust = null;
309: PublicKey trustPublicKey = null;
310: Exception invalidKeyEx = null;
311:
312: X509CertSelector certSelectX509 = new X509CertSelector();
313:
314: try {
315: certSelectX509.setSubject(cert.getIssuerX500Principal()
316: .getEncoded());
317: } catch (IOException ex) {
318: throw new AnnotatedException(
319: "Cannot set subject search criteria for trust anchor.",
320: ex);
321: }
322:
323: while (iter.hasNext() && trust == null) {
324: trust = (TrustAnchor) iter.next();
325: if (trust.getTrustedCert() != null) {
326: if (certSelectX509.match(trust.getTrustedCert())) {
327: trustPublicKey = trust.getTrustedCert()
328: .getPublicKey();
329: } else {
330: trust = null;
331: }
332: } else if (trust.getCAName() != null
333: && trust.getCAPublicKey() != null) {
334: try {
335: X500Principal certIssuer = cert
336: .getIssuerX500Principal();
337: if (certIssuer.getName().equals(trust.getCAName())) {
338: trustPublicKey = trust.getCAPublicKey();
339: } else {
340: trust = null;
341: }
342: } catch (IllegalArgumentException ex) {
343: trust = null;
344: }
345: } else {
346: trust = null;
347: }
348:
349: if (trustPublicKey != null) {
350: try {
351: cert.verify(trustPublicKey);
352: } catch (Exception ex) {
353: invalidKeyEx = ex;
354: trust = null;
355: }
356: }
357: }
358:
359: if (trust == null && invalidKeyEx != null) {
360: throw new AnnotatedException(
361: "Trust anchor found, but certificate validation failed for certificate.",
362: invalidKeyEx);
363: }
364:
365: return trust;
366: }
367:
368: /**
369: * Find the issuer certificates of the given certificate.
370: *
371: * @param cert The certificate for which the issuer certificate should be
372: * found.
373: * @param certStores A list of <code>X509Store</code> object that will be
374: * searched through.
375: *
376: * @return A <code>Collection</code> object containing the issuer
377: * <code>X509Certificate</code>s. Never <code>null</code>.
378: *
379: * @exception AnnotatedException if the signature verification on the given
380: * certificate fails for all found issuer certificates or an
381: * other error occurrs.
382: */
383: private Collection findIssuerCerts(X509Certificate cert,
384: List certStores) throws AnnotatedException {
385: X509CertStoreSelector certSelect = new X509CertStoreSelector();
386: Set certs = new HashSet();
387: try {
388: certSelect.setSubject(cert.getIssuerX500Principal()
389: .getEncoded());
390: } catch (IOException ex) {
391: throw new AnnotatedException(
392: "Subject criteria for certificate selector to find issuer certificate could not be set.",
393: ex);
394: }
395:
396: Iterator iter;
397:
398: try {
399: iter = CertPathValidatorUtilities.findCertificates(
400: (Selector) certSelect, certStores).iterator();
401: } catch (AnnotatedException e) {
402: throw new AnnotatedException(
403: "Issuer certificate cannot be searched.", e);
404: }
405:
406: AnnotatedException lastException = null;
407: boolean issuerCertFound = false;
408:
409: X509Certificate issuer;
410: while (iter.hasNext()) {
411: issuer = (X509Certificate) iter.next();
412: try {
413: cert.verify(issuer.getPublicKey());
414: certs.add(issuer);
415: issuerCertFound = true;
416: } catch (Exception ex) {
417: lastException = new AnnotatedException(
418: "Issued certificate could not be verified with issuer certificate.",
419: ex);
420: }
421: }
422:
423: if (!issuerCertFound && lastException != null) {
424: throw new AnnotatedException(
425: "Issuer certificate found but certificate validation failed for certificate.",
426: lastException);
427: }
428:
429: return certs;
430: }
431:
432: }
|