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