001: package org.bouncycastle.jce.provider;
002:
003: import org.bouncycastle.jce.exception.ExtCertPathBuilderException;
004:
005: import javax.security.auth.x500.X500Principal;
006: import java.io.IOException;
007: import java.security.InvalidAlgorithmParameterException;
008: import java.security.PublicKey;
009: import java.security.cert.CertPath;
010: import java.security.cert.CertPathBuilderException;
011: import java.security.cert.CertPathBuilderResult;
012: import java.security.cert.CertPathBuilderSpi;
013: import java.security.cert.CertPathParameters;
014: import java.security.cert.CertPathValidator;
015: import java.security.cert.CertPathValidatorException;
016: import java.security.cert.CertSelector;
017: import java.security.cert.CertificateException;
018: import java.security.cert.CertificateFactory;
019: import java.security.cert.PKIXBuilderParameters;
020: import java.security.cert.PKIXCertPathBuilderResult;
021: import java.security.cert.PKIXCertPathValidatorResult;
022: import java.security.cert.TrustAnchor;
023: import java.security.cert.X509CertSelector;
024: import java.security.cert.X509Certificate;
025: import java.util.ArrayList;
026: import java.util.Collection;
027: import java.util.HashSet;
028: import java.util.Iterator;
029: import java.util.List;
030: import java.util.Set;
031:
032: /**
033: * Implements the PKIX CertPathBuilding algorithem for BouncyCastle.
034: * <br />
035: * <b>MAYBE: implement more CertPath validation whil build path to omit invalid pathes</b>
036: *
037: * @see CertPathBuilderSpi
038: **/
039: public class PKIXCertPathBuilderSpi extends CertPathBuilderSpi {
040: /**
041: * Build and validate a CertPath using the given parameter.
042: *
043: * @param params PKIXBuilderParameters object containing all
044: * information to build the CertPath
045: **/
046: public CertPathBuilderResult engineBuild(CertPathParameters params)
047: throws CertPathBuilderException,
048: InvalidAlgorithmParameterException {
049: if (!(params instanceof PKIXBuilderParameters)) {
050: throw new InvalidAlgorithmParameterException(
051: "params must be a PKIXBuilderParameters instance");
052: }
053:
054: PKIXBuilderParameters pkixParams = (PKIXBuilderParameters) params;
055:
056: Collection targets;
057: Iterator targetIter;
058: List certPathList = new ArrayList();
059: Set certPathSet = new HashSet();
060: X509Certificate cert;
061: Collection certs;
062: CertPath certPath = null;
063: Exception certPathException = null;
064:
065: // search target certificates
066: CertSelector certSelect = pkixParams.getTargetCertConstraints();
067: if (certSelect == null) {
068: throw new CertPathBuilderException(
069: "targetCertConstraints must be non-null for CertPath building");
070: }
071:
072: try {
073: targets = CertPathValidatorUtilities.findCertificates(
074: certSelect, pkixParams.getCertStores());
075: } catch (AnnotatedException e) {
076: throw new ExtCertPathBuilderException(
077: "Error finding target certificate.", e.getCause());
078: }
079:
080: if (targets.isEmpty()) {
081: throw new CertPathBuilderException(
082: "no certificate found matching targetCertContraints");
083: }
084:
085: CertificateFactory cFact;
086: CertPathValidator validator;
087:
088: try {
089: cFact = CertificateFactory.getInstance("X.509", "BC");
090: validator = CertPathValidator.getInstance("PKIX", "BC");
091: } catch (Exception e) {
092: throw new CertPathBuilderException(
093: "exception creating support classes: " + e);
094: }
095:
096: //
097: // check all potential target certificates
098: targetIter = targets.iterator();
099: while (targetIter.hasNext()) {
100: cert = (X509Certificate) targetIter.next();
101: certPathList.clear();
102: certPathSet.clear();
103: while (cert != null) {
104: // add cert to the certpath
105: certPathList.add(cert);
106: certPathSet.add(cert);
107:
108: // check whether the issuer of <cert> is a TrustAnchor
109: if (findTrustAnchor(cert, pkixParams.getTrustAnchors()) != null) {
110: try {
111: certPath = cFact.generateCertPath(certPathList);
112:
113: PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult) validator
114: .validate(certPath, pkixParams);
115:
116: return new PKIXCertPathBuilderResult(certPath,
117: result.getTrustAnchor(), result
118: .getPolicyTree(), result
119: .getPublicKey());
120: } catch (CertificateException ex) {
121: certPathException = ex;
122: } catch (CertPathValidatorException ex) {
123: certPathException = ex;
124: }
125: // if validation failed go to next certificate
126: cert = null;
127: } else {
128: // try to get the issuer certificate from one
129: // of the CertStores
130: try {
131: X509Certificate issuer = findIssuer(cert,
132: pkixParams.getCertStores());
133: if (issuer.equals(cert)) {
134: cert = null;
135: } else {
136: cert = issuer;
137: // validation failed - circular path detected, go to next certificate
138: if (certPathSet.contains(cert)) {
139: cert = null;
140: }
141: }
142: } catch (CertPathValidatorException ex) {
143: certPathException = ex;
144: cert = null;
145: }
146: }
147: }
148: }
149:
150: if (certPath != null) {
151: throw new CertPathBuilderException(
152: "found certificate chain, but could not be validated",
153: certPathException);
154: }
155:
156: throw new CertPathBuilderException(
157: "unable to find certificate chain");
158: }
159:
160: /**
161: * Search the given Set of TrustAnchor's for one that is the
162: * issuer of the fiven X509 certificate.
163: *
164: * @param cert the X509 certificate
165: * @param trustAnchors a Set of TrustAnchor's
166: *
167: * @return the <code>TrustAnchor</code> object if found or
168: * <code>null</code> if not.
169: *
170: */
171: final TrustAnchor findTrustAnchor(X509Certificate cert,
172: Set trustAnchors) throws CertPathBuilderException {
173: Iterator iter = trustAnchors.iterator();
174: TrustAnchor trust = null;
175: PublicKey trustPublicKey = null;
176: Exception invalidKeyEx = null;
177:
178: X509CertSelector certSelectX509 = new X509CertSelector();
179:
180: try {
181: certSelectX509.setSubject(cert.getIssuerX500Principal()
182: .getEncoded());
183: } catch (IOException ex) {
184: throw new CertPathBuilderException(
185: "can't get trust anchor principal", null);
186: }
187:
188: while (iter.hasNext() && trust == null) {
189: trust = (TrustAnchor) iter.next();
190: if (trust.getTrustedCert() != null) {
191: if (certSelectX509.match(trust.getTrustedCert())) {
192: trustPublicKey = trust.getTrustedCert()
193: .getPublicKey();
194: } else {
195: trust = null;
196: }
197: } else if (trust.getCAName() != null
198: && trust.getCAPublicKey() != null) {
199: try {
200: X500Principal certIssuer = cert
201: .getIssuerX500Principal();
202: X500Principal caName = new X500Principal(trust
203: .getCAName());
204: if (certIssuer.equals(caName)) {
205: trustPublicKey = trust.getCAPublicKey();
206: } else {
207: trust = null;
208: }
209: } catch (IllegalArgumentException ex) {
210: trust = null;
211: }
212: } else {
213: trust = null;
214: }
215:
216: if (trustPublicKey != null) {
217: try {
218: cert.verify(trustPublicKey);
219: } catch (Exception ex) {
220: invalidKeyEx = ex;
221: trust = null;
222: }
223: }
224: }
225:
226: if (trust == null && invalidKeyEx != null) {
227: throw new CertPathBuilderException(
228: "TrustAnchor found put certificate validation failed",
229: invalidKeyEx);
230: }
231:
232: return trust;
233: }
234:
235: /**
236: * Find the issuer certificate of the given certificate.
237: *
238: * @param cert the certificate hows issuer certificate should
239: * be found.
240: * @param certStores a list of <code>CertStore</code> object
241: * that will be searched
242: *
243: * @return then <code>X509Certificate</code> object containing
244: * the issuer certificate or <code>null</code> if not found
245: *
246: * @exception CertPathValidatorException if a TrustAnchor was
247: * found but the signature verificytion on the given certificate
248: * has thrown an exception. This Exception can be obtainted with
249: * <code>getCause()</code> method.
250: **/
251: private X509Certificate findIssuer(X509Certificate cert,
252: List certStores) throws CertPathValidatorException {
253: Exception invalidKeyEx = null;
254: X509CertSelector certSelect = new X509CertSelector();
255: try {
256: certSelect.setSubject(cert.getIssuerX500Principal()
257: .getEncoded());
258: } catch (IOException ex) {
259: throw new CertPathValidatorException("Issuer not found",
260: null, null, -1);
261: }
262:
263: Iterator iter;
264: try {
265: iter = CertPathValidatorUtilities.findCertificates(
266: certSelect, certStores).iterator();
267: } catch (AnnotatedException e) {
268: throw new CertPathValidatorException(e.getCause());
269: }
270:
271: X509Certificate issuer = null;
272: while (iter.hasNext() && issuer == null) {
273: issuer = (X509Certificate) iter.next();
274: try {
275: cert.verify(issuer.getPublicKey());
276: } catch (Exception ex) {
277: invalidKeyEx = ex;
278: issuer = null;
279: }
280: }
281:
282: if (issuer == null && invalidKeyEx == null) {
283: throw new CertPathValidatorException("Issuer not found",
284: null, null, -1);
285: }
286:
287: if (issuer == null && invalidKeyEx != null) {
288: throw new CertPathValidatorException(
289: "issuer found but certificate validation failed",
290: invalidKeyEx, null, -1);
291: }
292:
293: return issuer;
294: }
295: }
|