1 /* |
|
2 * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. Oracle designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Oracle in the LICENSE file that accompanied this code. |
|
10 * |
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 * version 2 for more details (a copy is included in the LICENSE file that |
|
15 * accompanied this code). |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License version |
|
18 * 2 along with this work; if not, write to the Free Software Foundation, |
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 * |
|
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
22 * or visit www.oracle.com if you need additional information or have any |
|
23 * questions. |
|
24 */ |
|
25 |
|
26 package sun.security.provider.certpath; |
|
27 |
|
28 import java.math.BigInteger; |
|
29 import java.util.Arrays; |
|
30 import java.util.ArrayList; |
|
31 import java.util.Collection; |
|
32 import java.util.Collections; |
|
33 import java.util.Date; |
|
34 import java.util.List; |
|
35 import java.util.HashSet; |
|
36 import java.util.Set; |
|
37 import java.util.Iterator; |
|
38 import java.security.InvalidAlgorithmParameterException; |
|
39 import java.security.NoSuchAlgorithmException; |
|
40 import java.security.PublicKey; |
|
41 import java.security.cert.*; |
|
42 import java.security.cert.CertPathValidatorException.BasicReason; |
|
43 import java.security.interfaces.DSAPublicKey; |
|
44 import sun.security.util.Debug; |
|
45 import sun.security.x509.AccessDescription; |
|
46 import sun.security.x509.AuthorityInfoAccessExtension; |
|
47 import sun.security.x509.CRLDistributionPointsExtension; |
|
48 import sun.security.x509.DistributionPoint; |
|
49 import sun.security.x509.GeneralName; |
|
50 import sun.security.x509.GeneralNames; |
|
51 import sun.security.x509.PKIXExtensions; |
|
52 import sun.security.x509.X500Name; |
|
53 import sun.security.x509.X509CertImpl; |
|
54 import sun.security.x509.X509CRLEntryImpl; |
|
55 |
|
56 /** |
|
57 * CrlRevocationChecker is a <code>PKIXCertPathChecker</code> that checks |
|
58 * revocation status information on a PKIX certificate using CRLs obtained |
|
59 * from one or more <code>CertStores</code>. This is based on section 6.3 |
|
60 * of RFC 3280 (http://www.ietf.org/rfc/rfc3280.txt). |
|
61 * |
|
62 * @since 1.4 |
|
63 * @author Seth Proctor |
|
64 * @author Steve Hanna |
|
65 */ |
|
66 class CrlRevocationChecker extends PKIXCertPathChecker { |
|
67 |
|
68 private static final Debug debug = Debug.getInstance("certpath"); |
|
69 private final TrustAnchor mAnchor; |
|
70 private final List<CertStore> mStores; |
|
71 private final String mSigProvider; |
|
72 private final Date mCurrentTime; |
|
73 private PublicKey mPrevPubKey; |
|
74 private boolean mCRLSignFlag; |
|
75 private HashSet<X509CRL> mPossibleCRLs; |
|
76 private HashSet<X509CRL> mApprovedCRLs; |
|
77 private final PKIXParameters mParams; |
|
78 private static final boolean [] mCrlSignUsage = |
|
79 { false, false, false, false, false, false, true }; |
|
80 private static final boolean[] ALL_REASONS = |
|
81 {true, true, true, true, true, true, true, true, true}; |
|
82 private boolean mOnlyEECert = false; |
|
83 |
|
84 // Maximum clock skew in milliseconds (15 minutes) allowed when checking |
|
85 // validity of CRLs |
|
86 private static final long MAX_CLOCK_SKEW = 900000; |
|
87 |
|
88 /** |
|
89 * Creates a <code>CrlRevocationChecker</code>. |
|
90 * |
|
91 * @param anchor anchor selected to validate the target certificate |
|
92 * @param params <code>PKIXParameters</code> to be used for |
|
93 * finding certificates and CRLs, etc. |
|
94 */ |
|
95 CrlRevocationChecker(TrustAnchor anchor, PKIXParameters params) |
|
96 throws CertPathValidatorException |
|
97 { |
|
98 this(anchor, params, null); |
|
99 } |
|
100 |
|
101 /** |
|
102 * Creates a <code>CrlRevocationChecker</code>, allowing |
|
103 * extra certificates to be supplied beyond those contained |
|
104 * in the <code>PKIXParameters</code>. |
|
105 * |
|
106 * @param anchor anchor selected to validate the target certificate |
|
107 * @param params <code>PKIXParameters</code> to be used for |
|
108 * finding certificates and CRLs, etc. |
|
109 * @param certs a <code>Collection</code> of certificates |
|
110 * that may be useful, beyond those available |
|
111 * through <code>params</code> (<code>null</code> |
|
112 * if none) |
|
113 */ |
|
114 CrlRevocationChecker(TrustAnchor anchor, PKIXParameters params, |
|
115 Collection<X509Certificate> certs) throws CertPathValidatorException |
|
116 { |
|
117 this(anchor, params, certs, false); |
|
118 } |
|
119 |
|
120 CrlRevocationChecker(TrustAnchor anchor, PKIXParameters params, |
|
121 Collection<X509Certificate> certs, boolean onlyEECert) |
|
122 throws CertPathValidatorException { |
|
123 mAnchor = anchor; |
|
124 mParams = params; |
|
125 mStores = new ArrayList<CertStore>(params.getCertStores()); |
|
126 mSigProvider = params.getSigProvider(); |
|
127 if (certs != null) { |
|
128 try { |
|
129 mStores.add(CertStore.getInstance("Collection", |
|
130 new CollectionCertStoreParameters(certs))); |
|
131 } catch (Exception e) { |
|
132 // should never occur but not necessarily fatal, so log it, |
|
133 // ignore and continue |
|
134 if (debug != null) { |
|
135 debug.println("CrlRevocationChecker: " + |
|
136 "error creating Collection CertStore: " + e); |
|
137 } |
|
138 } |
|
139 } |
|
140 Date testDate = params.getDate(); |
|
141 mCurrentTime = (testDate != null ? testDate : new Date()); |
|
142 mOnlyEECert = onlyEECert; |
|
143 init(false); |
|
144 } |
|
145 |
|
146 /** |
|
147 * Initializes the internal state of the checker from parameters |
|
148 * specified in the constructor |
|
149 */ |
|
150 public void init(boolean forward) throws CertPathValidatorException |
|
151 { |
|
152 if (!forward) { |
|
153 if (mAnchor != null) { |
|
154 if (mAnchor.getCAPublicKey() != null) { |
|
155 mPrevPubKey = mAnchor.getCAPublicKey(); |
|
156 } else { |
|
157 mPrevPubKey = mAnchor.getTrustedCert().getPublicKey(); |
|
158 } |
|
159 } else { |
|
160 mPrevPubKey = null; |
|
161 } |
|
162 mCRLSignFlag = true; |
|
163 } else { |
|
164 throw new CertPathValidatorException("forward checking " |
|
165 + "not supported"); |
|
166 } |
|
167 } |
|
168 |
|
169 public boolean isForwardCheckingSupported() { |
|
170 return false; |
|
171 } |
|
172 |
|
173 public Set<String> getSupportedExtensions() { |
|
174 return null; |
|
175 } |
|
176 |
|
177 /** |
|
178 * Performs the revocation status check on the certificate using |
|
179 * its internal state. |
|
180 * |
|
181 * @param cert the Certificate |
|
182 * @param unresolvedCritExts a Collection of the unresolved critical |
|
183 * extensions |
|
184 * @exception CertPathValidatorException Exception thrown if |
|
185 * certificate does not verify |
|
186 */ |
|
187 public void check(Certificate cert, Collection<String> unresolvedCritExts) |
|
188 throws CertPathValidatorException |
|
189 { |
|
190 X509Certificate currCert = (X509Certificate) cert; |
|
191 verifyRevocationStatus(currCert, mPrevPubKey, mCRLSignFlag, true); |
|
192 |
|
193 // Make new public key if parameters are missing |
|
194 PublicKey cKey = currCert.getPublicKey(); |
|
195 if (cKey instanceof DSAPublicKey && |
|
196 ((DSAPublicKey)cKey).getParams() == null) { |
|
197 // cKey needs to inherit DSA parameters from prev key |
|
198 cKey = BasicChecker.makeInheritedParamsKey(cKey, mPrevPubKey); |
|
199 } |
|
200 mPrevPubKey = cKey; |
|
201 mCRLSignFlag = certCanSignCrl(currCert); |
|
202 } |
|
203 |
|
204 /** |
|
205 * Performs the revocation status check on the certificate using |
|
206 * the provided state variables, as well as the constant internal |
|
207 * data. |
|
208 * |
|
209 * @param currCert the Certificate |
|
210 * @param prevKey the previous PublicKey in the chain |
|
211 * @param signFlag a boolean as returned from the last call, or true |
|
212 * if this is the first cert in the chain |
|
213 * @return a boolean specifying if the cert is allowed to vouch for the |
|
214 * validity of a CRL for the next iteration |
|
215 * @exception CertPathValidatorException Exception thrown if |
|
216 * certificate does not verify. |
|
217 */ |
|
218 public boolean check(X509Certificate currCert, PublicKey prevKey, |
|
219 boolean signFlag) throws CertPathValidatorException |
|
220 { |
|
221 verifyRevocationStatus(currCert, prevKey, signFlag, true); |
|
222 return certCanSignCrl(currCert); |
|
223 } |
|
224 |
|
225 /** |
|
226 * Checks that a cert can be used to verify a CRL. |
|
227 * |
|
228 * @param currCert an X509Certificate to check |
|
229 * @return a boolean specifying if the cert is allowed to vouch for the |
|
230 * validity of a CRL |
|
231 */ |
|
232 static boolean certCanSignCrl(X509Certificate currCert) { |
|
233 // if the cert doesn't include the key usage ext, or |
|
234 // the key usage ext asserts cRLSigning, return true, |
|
235 // otherwise return false. |
|
236 boolean[] kbools = currCert.getKeyUsage(); |
|
237 if (kbools != null) { |
|
238 return kbools[6]; |
|
239 } |
|
240 return false; |
|
241 } |
|
242 |
|
243 /** |
|
244 * Internal method to start the verification of a cert |
|
245 */ |
|
246 private void verifyRevocationStatus(X509Certificate currCert, |
|
247 PublicKey prevKey, boolean signFlag, boolean allowSeparateKey) |
|
248 throws CertPathValidatorException |
|
249 { |
|
250 verifyRevocationStatus(currCert, prevKey, signFlag, |
|
251 allowSeparateKey, null, mParams.getTrustAnchors()); |
|
252 } |
|
253 |
|
254 /** |
|
255 * Internal method to start the verification of a cert |
|
256 * @param stackedCerts a <code>Set</code> of <code>X509Certificate</code>s> |
|
257 * whose revocation status depends on the |
|
258 * non-revoked status of this cert. To avoid |
|
259 * circular dependencies, we assume they're |
|
260 * revoked while checking the revocation |
|
261 * status of this cert. |
|
262 * @param trustAnchors a <code>Set</code> of <code>TrustAnchor</code>s |
|
263 */ |
|
264 private void verifyRevocationStatus(X509Certificate currCert, |
|
265 PublicKey prevKey, boolean signFlag, boolean allowSeparateKey, |
|
266 Set<X509Certificate> stackedCerts, |
|
267 Set<TrustAnchor> trustAnchors) throws CertPathValidatorException { |
|
268 |
|
269 String msg = "revocation status"; |
|
270 if (debug != null) { |
|
271 debug.println("CrlRevocationChecker.verifyRevocationStatus()" + |
|
272 " ---checking " + msg + "..."); |
|
273 } |
|
274 |
|
275 if (mOnlyEECert && currCert.getBasicConstraints() != -1) { |
|
276 if (debug != null) { |
|
277 debug.println("Skipping revocation check, not end entity cert"); |
|
278 } |
|
279 return; |
|
280 } |
|
281 |
|
282 // reject circular dependencies - RFC 3280 is not explicit on how |
|
283 // to handle this, so we feel it is safest to reject them until |
|
284 // the issue is resolved in the PKIX WG. |
|
285 if ((stackedCerts != null) && stackedCerts.contains(currCert)) { |
|
286 if (debug != null) { |
|
287 debug.println("CrlRevocationChecker.verifyRevocationStatus()" + |
|
288 " circular dependency"); |
|
289 } |
|
290 throw new CertPathValidatorException |
|
291 ("Could not determine revocation status", null, null, -1, |
|
292 BasicReason.UNDETERMINED_REVOCATION_STATUS); |
|
293 } |
|
294 |
|
295 // init the state for this run |
|
296 mPossibleCRLs = new HashSet<X509CRL>(); |
|
297 mApprovedCRLs = new HashSet<X509CRL>(); |
|
298 boolean[] reasonsMask = new boolean[9]; |
|
299 |
|
300 try { |
|
301 X509CRLSelector sel = new X509CRLSelector(); |
|
302 sel.setCertificateChecking(currCert); |
|
303 CertPathHelper.setDateAndTime(sel, mCurrentTime, MAX_CLOCK_SKEW); |
|
304 |
|
305 for (CertStore mStore : mStores) { |
|
306 for (java.security.cert.CRL crl : mStore.getCRLs(sel)) { |
|
307 mPossibleCRLs.add((X509CRL)crl); |
|
308 } |
|
309 } |
|
310 DistributionPointFetcher store = |
|
311 DistributionPointFetcher.getInstance(); |
|
312 // all CRLs returned by the DP Fetcher have also been verified |
|
313 mApprovedCRLs.addAll(store.getCRLs(sel, signFlag, prevKey, |
|
314 mSigProvider, mStores, reasonsMask, trustAnchors, |
|
315 mParams.getDate())); |
|
316 } catch (Exception e) { |
|
317 if (debug != null) { |
|
318 debug.println("CrlRevocationChecker.verifyRevocationStatus() " |
|
319 + "unexpected exception: " + e.getMessage()); |
|
320 } |
|
321 throw new CertPathValidatorException(e); |
|
322 } |
|
323 |
|
324 if (debug != null) { |
|
325 debug.println("CrlRevocationChecker.verifyRevocationStatus() " + |
|
326 "crls.size() = " + mPossibleCRLs.size()); |
|
327 } |
|
328 if (!mPossibleCRLs.isEmpty()) { |
|
329 // Now that we have a list of possible CRLs, see which ones can |
|
330 // be approved |
|
331 mApprovedCRLs.addAll(verifyPossibleCRLs(mPossibleCRLs, currCert, |
|
332 signFlag, prevKey, reasonsMask, trustAnchors)); |
|
333 } |
|
334 if (debug != null) { |
|
335 debug.println("CrlRevocationChecker.verifyRevocationStatus() " + |
|
336 "approved crls.size() = " + mApprovedCRLs.size()); |
|
337 } |
|
338 |
|
339 // make sure that we have at least one CRL that _could_ cover |
|
340 // the certificate in question and all reasons are covered |
|
341 if (mApprovedCRLs.isEmpty() || |
|
342 !Arrays.equals(reasonsMask, ALL_REASONS)) { |
|
343 if (allowSeparateKey) { |
|
344 verifyWithSeparateSigningKey(currCert, prevKey, signFlag, |
|
345 stackedCerts); |
|
346 return; |
|
347 } else { |
|
348 throw new CertPathValidatorException |
|
349 ("Could not determine revocation status", null, null, -1, |
|
350 BasicReason.UNDETERMINED_REVOCATION_STATUS); |
|
351 } |
|
352 } |
|
353 |
|
354 // See if the cert is in the set of approved crls. |
|
355 if (debug != null) { |
|
356 BigInteger sn = currCert.getSerialNumber(); |
|
357 debug.println("CrlRevocationChecker.verifyRevocationStatus() " + |
|
358 "starting the final sweep..."); |
|
359 debug.println("CrlRevocationChecker.verifyRevocationStatus" + |
|
360 " cert SN: " + sn.toString()); |
|
361 } |
|
362 |
|
363 CRLReason reasonCode = CRLReason.UNSPECIFIED; |
|
364 X509CRLEntryImpl entry = null; |
|
365 for (X509CRL crl : mApprovedCRLs) { |
|
366 X509CRLEntry e = crl.getRevokedCertificate(currCert); |
|
367 if (e != null) { |
|
368 try { |
|
369 entry = X509CRLEntryImpl.toImpl(e); |
|
370 } catch (CRLException ce) { |
|
371 throw new CertPathValidatorException(ce); |
|
372 } |
|
373 if (debug != null) { |
|
374 debug.println("CrlRevocationChecker.verifyRevocationStatus" |
|
375 + " CRL entry: " + entry.toString()); |
|
376 } |
|
377 |
|
378 /* |
|
379 * Abort CRL validation and throw exception if there are any |
|
380 * unrecognized critical CRL entry extensions (see section |
|
381 * 5.3 of RFC 3280). |
|
382 */ |
|
383 Set<String> unresCritExts = entry.getCriticalExtensionOIDs(); |
|
384 if (unresCritExts != null && !unresCritExts.isEmpty()) { |
|
385 /* remove any that we will process */ |
|
386 unresCritExts.remove |
|
387 (PKIXExtensions.ReasonCode_Id.toString()); |
|
388 unresCritExts.remove |
|
389 (PKIXExtensions.CertificateIssuer_Id.toString()); |
|
390 if (!unresCritExts.isEmpty()) { |
|
391 if (debug != null) { |
|
392 debug.println("Unrecognized " |
|
393 + "critical extension(s) in revoked CRL entry: " |
|
394 + unresCritExts); |
|
395 } |
|
396 throw new CertPathValidatorException |
|
397 ("Could not determine revocation status", null, null, |
|
398 -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); |
|
399 } |
|
400 } |
|
401 |
|
402 reasonCode = entry.getRevocationReason(); |
|
403 if (reasonCode == null) { |
|
404 reasonCode = CRLReason.UNSPECIFIED; |
|
405 } |
|
406 Throwable t = new CertificateRevokedException |
|
407 (entry.getRevocationDate(), reasonCode, |
|
408 crl.getIssuerX500Principal(), entry.getExtensions()); |
|
409 throw new CertPathValidatorException(t.getMessage(), t, |
|
410 null, -1, BasicReason.REVOKED); |
|
411 } |
|
412 } |
|
413 } |
|
414 |
|
415 /** |
|
416 * We have a cert whose revocation status couldn't be verified by |
|
417 * a CRL issued by the cert that issued the CRL. See if we can |
|
418 * find a valid CRL issued by a separate key that can verify the |
|
419 * revocation status of this certificate. |
|
420 * <p> |
|
421 * Note that this does not provide support for indirect CRLs, |
|
422 * only CRLs signed with a different key (but the same issuer |
|
423 * name) as the certificate being checked. |
|
424 * |
|
425 * @param currCert the <code>X509Certificate</code> to be checked |
|
426 * @param prevKey the <code>PublicKey</code> that failed |
|
427 * @param signFlag <code>true</code> if that key was trusted to sign CRLs |
|
428 * @param stackedCerts a <code>Set</code> of <code>X509Certificate</code>s> |
|
429 * whose revocation status depends on the |
|
430 * non-revoked status of this cert. To avoid |
|
431 * circular dependencies, we assume they're |
|
432 * revoked while checking the revocation |
|
433 * status of this cert. |
|
434 * @throws CertPathValidatorException if the cert's revocation status |
|
435 * cannot be verified successfully with another key |
|
436 */ |
|
437 private void verifyWithSeparateSigningKey(X509Certificate currCert, |
|
438 PublicKey prevKey, boolean signFlag, Set<X509Certificate> stackedCerts) |
|
439 throws CertPathValidatorException { |
|
440 String msg = "revocation status"; |
|
441 if (debug != null) { |
|
442 debug.println( |
|
443 "CrlRevocationChecker.verifyWithSeparateSigningKey()" + |
|
444 " ---checking " + msg + "..."); |
|
445 } |
|
446 |
|
447 // reject circular dependencies - RFC 3280 is not explicit on how |
|
448 // to handle this, so we feel it is safest to reject them until |
|
449 // the issue is resolved in the PKIX WG. |
|
450 if ((stackedCerts != null) && stackedCerts.contains(currCert)) { |
|
451 if (debug != null) { |
|
452 debug.println( |
|
453 "CrlRevocationChecker.verifyWithSeparateSigningKey()" + |
|
454 " circular dependency"); |
|
455 } |
|
456 throw new CertPathValidatorException |
|
457 ("Could not determine revocation status", null, null, |
|
458 -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); |
|
459 } |
|
460 |
|
461 // If prevKey wasn't trusted, maybe we just didn't have the right |
|
462 // path to it. Don't rule that key out. |
|
463 if (!signFlag) { |
|
464 prevKey = null; |
|
465 } |
|
466 |
|
467 // Try to find another key that might be able to sign |
|
468 // CRLs vouching for this cert. |
|
469 buildToNewKey(currCert, prevKey, stackedCerts); |
|
470 } |
|
471 |
|
472 /** |
|
473 * Tries to find a CertPath that establishes a key that can be |
|
474 * used to verify the revocation status of a given certificate. |
|
475 * Ignores keys that have previously been tried. Throws a |
|
476 * CertPathValidatorException if no such key could be found. |
|
477 * |
|
478 * @param currCert the <code>X509Certificate</code> to be checked |
|
479 * @param prevKey the <code>PublicKey</code> of the certificate whose key |
|
480 * cannot be used to vouch for the CRL and should be ignored |
|
481 * @param stackedCerts a <code>Set</code> of <code>X509Certificate</code>s> |
|
482 * whose revocation status depends on the |
|
483 * establishment of this path. |
|
484 * @throws CertPathValidatorException on failure |
|
485 */ |
|
486 private void buildToNewKey(X509Certificate currCert, |
|
487 PublicKey prevKey, Set<X509Certificate> stackedCerts) |
|
488 throws CertPathValidatorException { |
|
489 |
|
490 if (debug != null) { |
|
491 debug.println("CrlRevocationChecker.buildToNewKey()" + |
|
492 " starting work"); |
|
493 } |
|
494 Set<PublicKey> badKeys = new HashSet<PublicKey>(); |
|
495 if (prevKey != null) { |
|
496 badKeys.add(prevKey); |
|
497 } |
|
498 X509CertSelector certSel = new RejectKeySelector(badKeys); |
|
499 certSel.setSubject(currCert.getIssuerX500Principal()); |
|
500 certSel.setKeyUsage(mCrlSignUsage); |
|
501 |
|
502 Set<TrustAnchor> newAnchors = |
|
503 (mAnchor == null ? mParams.getTrustAnchors() : |
|
504 Collections.singleton(mAnchor)); |
|
505 |
|
506 PKIXBuilderParameters builderParams; |
|
507 if (mParams instanceof PKIXBuilderParameters) { |
|
508 builderParams = (PKIXBuilderParameters) mParams.clone(); |
|
509 builderParams.setTargetCertConstraints(certSel); |
|
510 // Policy qualifiers must be rejected, since we don't have |
|
511 // any way to convey them back to the application. |
|
512 builderParams.setPolicyQualifiersRejected(true); |
|
513 try { |
|
514 builderParams.setTrustAnchors(newAnchors); |
|
515 } catch (InvalidAlgorithmParameterException iape) { |
|
516 throw new RuntimeException(iape); // should never occur |
|
517 } |
|
518 } else { |
|
519 // It's unfortunate that there's no easy way to make a |
|
520 // PKIXBuilderParameters object from a PKIXParameters |
|
521 // object. This might miss some things if parameters |
|
522 // are added in the future or the validatorParams object |
|
523 // is a custom class derived from PKIXValidatorParameters. |
|
524 try { |
|
525 builderParams = new PKIXBuilderParameters(newAnchors, certSel); |
|
526 } catch (InvalidAlgorithmParameterException iape) { |
|
527 throw new RuntimeException(iape); // should never occur |
|
528 } |
|
529 builderParams.setInitialPolicies(mParams.getInitialPolicies()); |
|
530 builderParams.setCertStores(mStores); |
|
531 builderParams.setExplicitPolicyRequired |
|
532 (mParams.isExplicitPolicyRequired()); |
|
533 builderParams.setPolicyMappingInhibited |
|
534 (mParams.isPolicyMappingInhibited()); |
|
535 builderParams.setAnyPolicyInhibited(mParams.isAnyPolicyInhibited()); |
|
536 // Policy qualifiers must be rejected, since we don't have |
|
537 // any way to convey them back to the application. |
|
538 // That's the default, so no need to write code. |
|
539 builderParams.setDate(mParams.getDate()); |
|
540 builderParams.setCertPathCheckers(mParams.getCertPathCheckers()); |
|
541 builderParams.setSigProvider(mParams.getSigProvider()); |
|
542 } |
|
543 |
|
544 // Skip revocation during this build to detect circular |
|
545 // references. But check revocation afterwards, using the |
|
546 // key (or any other that works). |
|
547 builderParams.setRevocationEnabled(false); |
|
548 |
|
549 // check for AuthorityInformationAccess extension |
|
550 if (Builder.USE_AIA == true) { |
|
551 X509CertImpl currCertImpl = null; |
|
552 try { |
|
553 currCertImpl = X509CertImpl.toImpl(currCert); |
|
554 } catch (CertificateException ce) { |
|
555 // ignore but log it |
|
556 if (debug != null) { |
|
557 debug.println("CrlRevocationChecker.buildToNewKey: " + |
|
558 "error decoding cert: " + ce); |
|
559 } |
|
560 } |
|
561 AuthorityInfoAccessExtension aiaExt = null; |
|
562 if (currCertImpl != null) { |
|
563 aiaExt = currCertImpl.getAuthorityInfoAccessExtension(); |
|
564 } |
|
565 if (aiaExt != null) { |
|
566 List<AccessDescription> adList = aiaExt.getAccessDescriptions(); |
|
567 if (adList != null) { |
|
568 for (AccessDescription ad : adList) { |
|
569 CertStore cs = URICertStore.getInstance(ad); |
|
570 if (cs != null) { |
|
571 if (debug != null) { |
|
572 debug.println("adding AIAext CertStore"); |
|
573 } |
|
574 builderParams.addCertStore(cs); |
|
575 } |
|
576 } |
|
577 } |
|
578 } |
|
579 } |
|
580 |
|
581 CertPathBuilder builder = null; |
|
582 try { |
|
583 builder = CertPathBuilder.getInstance("PKIX"); |
|
584 } catch (NoSuchAlgorithmException nsae) { |
|
585 throw new CertPathValidatorException(nsae); |
|
586 } |
|
587 while (true) { |
|
588 try { |
|
589 if (debug != null) { |
|
590 debug.println("CrlRevocationChecker.buildToNewKey()" + |
|
591 " about to try build ..."); |
|
592 } |
|
593 PKIXCertPathBuilderResult cpbr = |
|
594 (PKIXCertPathBuilderResult) builder.build(builderParams); |
|
595 |
|
596 if (debug != null) { |
|
597 debug.println("CrlRevocationChecker.buildToNewKey()" + |
|
598 " about to check revocation ..."); |
|
599 } |
|
600 // Now check revocation of all certs in path, assuming that |
|
601 // the stackedCerts are revoked. |
|
602 if (stackedCerts == null) { |
|
603 stackedCerts = new HashSet<X509Certificate>(); |
|
604 } |
|
605 stackedCerts.add(currCert); |
|
606 TrustAnchor ta = cpbr.getTrustAnchor(); |
|
607 PublicKey prevKey2 = ta.getCAPublicKey(); |
|
608 if (prevKey2 == null) { |
|
609 prevKey2 = ta.getTrustedCert().getPublicKey(); |
|
610 } |
|
611 boolean signFlag = true; |
|
612 List<? extends Certificate> cpList = |
|
613 cpbr.getCertPath().getCertificates(); |
|
614 try { |
|
615 for (int i = cpList.size()-1; i >= 0; i-- ) { |
|
616 X509Certificate cert = (X509Certificate) cpList.get(i); |
|
617 |
|
618 if (debug != null) { |
|
619 debug.println("CrlRevocationChecker.buildToNewKey()" |
|
620 + " index " + i + " checking " + cert); |
|
621 } |
|
622 verifyRevocationStatus(cert, prevKey2, signFlag, true, |
|
623 stackedCerts, newAnchors); |
|
624 signFlag = certCanSignCrl(cert); |
|
625 prevKey2 = cert.getPublicKey(); |
|
626 } |
|
627 } catch (CertPathValidatorException cpve) { |
|
628 // ignore it and try to get another key |
|
629 badKeys.add(cpbr.getPublicKey()); |
|
630 continue; |
|
631 } |
|
632 |
|
633 if (debug != null) { |
|
634 debug.println("CrlRevocationChecker.buildToNewKey()" + |
|
635 " got key " + cpbr.getPublicKey()); |
|
636 } |
|
637 // Now check revocation on the current cert using that key. |
|
638 // If it doesn't check out, try to find a different key. |
|
639 // And if we can't find a key, then return false. |
|
640 PublicKey newKey = cpbr.getPublicKey(); |
|
641 try { |
|
642 verifyRevocationStatus(currCert, newKey, true, false); |
|
643 // If that passed, the cert is OK! |
|
644 return; |
|
645 } catch (CertPathValidatorException cpve) { |
|
646 // If it is revoked, rethrow exception |
|
647 if (cpve.getReason() == BasicReason.REVOKED) { |
|
648 throw cpve; |
|
649 } |
|
650 // Otherwise, ignore the exception and |
|
651 // try to get another key. |
|
652 } |
|
653 badKeys.add(newKey); |
|
654 } catch (InvalidAlgorithmParameterException iape) { |
|
655 throw new CertPathValidatorException(iape); |
|
656 } catch (CertPathBuilderException cpbe) { |
|
657 throw new CertPathValidatorException |
|
658 ("Could not determine revocation status", null, null, |
|
659 -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); |
|
660 } |
|
661 } |
|
662 } |
|
663 |
|
664 /* |
|
665 * This inner class extends the X509CertSelector to add an additional |
|
666 * check to make sure the subject public key isn't on a particular list. |
|
667 * This class is used by buildToNewKey() to make sure the builder doesn't |
|
668 * end up with a CertPath to a public key that has already been rejected. |
|
669 */ |
|
670 private static class RejectKeySelector extends X509CertSelector { |
|
671 private final Set<PublicKey> badKeySet; |
|
672 |
|
673 /** |
|
674 * Creates a new <code>RejectKeySelector</code>. |
|
675 * |
|
676 * @param badPublicKeys a <code>Set</code> of |
|
677 * <code>PublicKey</code>s that |
|
678 * should be rejected (or <code>null</code> |
|
679 * if no such check should be done) |
|
680 */ |
|
681 RejectKeySelector(Set<PublicKey> badPublicKeys) { |
|
682 this.badKeySet = badPublicKeys; |
|
683 } |
|
684 |
|
685 /** |
|
686 * Decides whether a <code>Certificate</code> should be selected. |
|
687 * |
|
688 * @param cert the <code>Certificate</code> to be checked |
|
689 * @return <code>true</code> if the <code>Certificate</code> should be |
|
690 * selected, <code>false</code> otherwise |
|
691 */ |
|
692 public boolean match(Certificate cert) { |
|
693 if (!super.match(cert)) |
|
694 return(false); |
|
695 |
|
696 if (badKeySet.contains(cert.getPublicKey())) { |
|
697 if (debug != null) |
|
698 debug.println("RejectCertSelector.match: bad key"); |
|
699 return false; |
|
700 } |
|
701 |
|
702 if (debug != null) |
|
703 debug.println("RejectCertSelector.match: returning true"); |
|
704 return true; |
|
705 } |
|
706 |
|
707 /** |
|
708 * Return a printable representation of the <code>CertSelector</code>. |
|
709 * |
|
710 * @return a <code>String</code> describing the contents of the |
|
711 * <code>CertSelector</code> |
|
712 */ |
|
713 public String toString() { |
|
714 StringBuilder sb = new StringBuilder(); |
|
715 sb.append("RejectCertSelector: [\n"); |
|
716 sb.append(super.toString()); |
|
717 sb.append(badKeySet); |
|
718 sb.append("]"); |
|
719 return sb.toString(); |
|
720 } |
|
721 } |
|
722 |
|
723 /** |
|
724 * Internal method that verifies a set of possible_crls, |
|
725 * and sees if each is approved, based on the cert. |
|
726 * |
|
727 * @param crls a set of possible CRLs to test for acceptability |
|
728 * @param cert the certificate whose revocation status is being checked |
|
729 * @param signFlag <code>true</code> if prevKey was trusted to sign CRLs |
|
730 * @param prevKey the public key of the issuer of cert |
|
731 * @param reasonsMask the reason code mask |
|
732 * @param trustAnchors a <code>Set</code> of <code>TrustAnchor</code>s> |
|
733 * @return a collection of approved crls (or an empty collection) |
|
734 */ |
|
735 private Collection<X509CRL> verifyPossibleCRLs(Set<X509CRL> crls, |
|
736 X509Certificate cert, boolean signFlag, PublicKey prevKey, |
|
737 boolean[] reasonsMask, |
|
738 Set<TrustAnchor> trustAnchors) throws CertPathValidatorException { |
|
739 |
|
740 try { |
|
741 X509CertImpl certImpl = X509CertImpl.toImpl(cert); |
|
742 if (debug != null) { |
|
743 debug.println("CRLRevocationChecker.verifyPossibleCRLs: " + |
|
744 "Checking CRLDPs for " |
|
745 + certImpl.getSubjectX500Principal()); |
|
746 } |
|
747 CRLDistributionPointsExtension ext = |
|
748 certImpl.getCRLDistributionPointsExtension(); |
|
749 List<DistributionPoint> points = null; |
|
750 if (ext == null) { |
|
751 // assume a DP with reasons and CRLIssuer fields omitted |
|
752 // and a DP name of the cert issuer. |
|
753 // TODO add issuerAltName too |
|
754 X500Name certIssuer = (X500Name)certImpl.getIssuerDN(); |
|
755 DistributionPoint point = new DistributionPoint |
|
756 (new GeneralNames().add(new GeneralName(certIssuer)), |
|
757 null, null); |
|
758 points = Collections.singletonList(point); |
|
759 } else { |
|
760 points = ext.get(CRLDistributionPointsExtension.POINTS); |
|
761 } |
|
762 Set<X509CRL> results = new HashSet<X509CRL>(); |
|
763 DistributionPointFetcher dpf = |
|
764 DistributionPointFetcher.getInstance(); |
|
765 for (Iterator<DistributionPoint> t = points.iterator(); |
|
766 t.hasNext() && !Arrays.equals(reasonsMask, ALL_REASONS); ) { |
|
767 DistributionPoint point = t.next(); |
|
768 for (X509CRL crl : crls) { |
|
769 if (dpf.verifyCRL(certImpl, point, crl, reasonsMask, |
|
770 signFlag, prevKey, mSigProvider, |
|
771 trustAnchors, mStores, mParams.getDate())) { |
|
772 results.add(crl); |
|
773 } |
|
774 } |
|
775 } |
|
776 return results; |
|
777 } catch (Exception e) { |
|
778 if (debug != null) { |
|
779 debug.println("Exception while verifying CRL: "+e.getMessage()); |
|
780 e.printStackTrace(); |
|
781 } |
|
782 return Collections.emptySet(); |
|
783 } |
|
784 } |
|
785 } |
|