blob: 51c05f2a6c54be667fdb44b3102ab3a18d98ad18 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2002-2007 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.security.provider.certpath;
27
28import java.io.*;
29import java.net.URI;
30import java.util.*;
31import java.security.*;
32import java.security.cert.*;
33import javax.security.auth.x500.X500Principal;
34
35import sun.security.action.GetPropertyAction;
36import sun.security.util.Debug;
37import sun.security.x509.*;
38
39/**
40 * Class to obtain CRLs via the CRLDistributionPoints extension.
41 * Note that the functionality of this class must be explicitly enabled
42 * via a system property, see the USE_CRLDP variable below.
43 *
44 * This class uses the URICertStore class to fetch CRLs. The URICertStore
45 * class also implements CRL caching: see the class description for more
46 * information.
47 *
48 * @author Andreas Sterbenz
49 * @author Sean Mullan
50 * @since 1.4.2
51 */
52class DistributionPointFetcher {
53
54 private static final Debug debug = Debug.getInstance("certpath");
55
56 private static final boolean[] ALL_REASONS =
57 {true, true, true, true, true, true, true, true, true};
58
59 /**
60 * Flag indicating whether support for the CRL distribution point
61 * extension shall be enabled. Currently disabled by default for
62 * compatibility and legal reasons.
63 */
64 private final static boolean USE_CRLDP =
65 getBooleanProperty("com.sun.security.enableCRLDP", false);
66
67 /**
68 * Return the value of the boolean System property propName.
69 */
70 public static boolean getBooleanProperty(String propName,
71 boolean defaultValue) {
72 // if set, require value of either true or false
73 String b = AccessController.doPrivileged(
74 new GetPropertyAction(propName));
75 if (b == null) {
76 return defaultValue;
77 } else if (b.equalsIgnoreCase("false")) {
78 return false;
79 } else if (b.equalsIgnoreCase("true")) {
80 return true;
81 } else {
82 throw new RuntimeException("Value of " + propName
83 + " must either be 'true' or 'false'");
84 }
85 }
86
87 // singleton instance
88 private static final DistributionPointFetcher INSTANCE =
89 new DistributionPointFetcher();
90
91 /**
92 * Private instantiation only.
93 */
94 private DistributionPointFetcher() {}
95
96 /**
97 * Return a DistributionPointFetcher instance.
98 */
99 static DistributionPointFetcher getInstance() {
100 return INSTANCE;
101 }
102
103 /**
104 * Return the X509CRLs matching this selector. The selector must be
105 * an X509CRLSelector with certificateChecking set.
106 *
107 * If CRLDP support is disabled, this method always returns an
108 * empty set.
109 */
110 Collection<X509CRL> getCRLs(X509CRLSelector selector, boolean signFlag,
111 PublicKey prevKey, String provider, List<CertStore> certStores,
112 boolean[] reasonsMask, TrustAnchor anchor) throws CertStoreException
113 {
114 if (USE_CRLDP == false) {
115 return Collections.emptySet();
116 }
117 X509Certificate cert = selector.getCertificateChecking();
118 if (cert == null) {
119 return Collections.emptySet();
120 }
121 try {
122 X509CertImpl certImpl = X509CertImpl.toImpl(cert);
123 if (debug != null) {
124 debug.println("DistributionPointFetcher.getCRLs: Checking "
125 + "CRLDPs for " + certImpl.getSubjectX500Principal());
126 }
127 CRLDistributionPointsExtension ext =
128 certImpl.getCRLDistributionPointsExtension();
129 if (ext == null) {
130 if (debug != null) {
131 debug.println("No CRLDP ext");
132 }
133 return Collections.emptySet();
134 }
135 List<DistributionPoint> points = (List<DistributionPoint>)ext.get(
136 CRLDistributionPointsExtension.POINTS);
137 Set<X509CRL> results = new HashSet<X509CRL>();
138 for (Iterator<DistributionPoint> t = points.iterator();
139 t.hasNext() && !Arrays.equals(reasonsMask, ALL_REASONS); ) {
140 DistributionPoint point = t.next();
141 Collection<X509CRL> crls = getCRLs(selector, certImpl,
142 point, reasonsMask, signFlag, prevKey, provider,
143 certStores, anchor);
144 results.addAll(crls);
145 }
146 if (debug != null) {
147 debug.println("Returning " + results.size() + " CRLs");
148 }
149 return results;
150 } catch (CertificateException e) {
151 return Collections.emptySet();
152 } catch (IOException e) {
153 return Collections.emptySet();
154 }
155 }
156
157 /**
158 * Download CRLs from the given distribution point, verify and return them.
159 * See the top of the class for current limitations.
160 */
161 private Collection<X509CRL> getCRLs(X509CRLSelector selector,
162 X509CertImpl certImpl, DistributionPoint point, boolean[] reasonsMask,
163 boolean signFlag, PublicKey prevKey, String provider,
164 List<CertStore> certStores, TrustAnchor anchor)
165 {
166 // check for full name
167 GeneralNames fullName = point.getFullName();
168 if (fullName == null) {
169 // check for relative name
170 RDN relativeName = point.getRelativeName();
171 if (relativeName == null) {
172 return Collections.emptySet();
173 }
174 try {
175 GeneralNames crlIssuers = point.getCRLIssuer();
176 if (crlIssuers == null) {
177 fullName = getFullNames
178 ((X500Name) certImpl.getIssuerDN(), relativeName);
179 } else {
180 // should only be one CRL Issuer
181 if (crlIssuers.size() != 1) {
182 return Collections.emptySet();
183 } else {
184 fullName = getFullNames
185 ((X500Name) crlIssuers.get(0).getName(), relativeName);
186 }
187 }
188 } catch (IOException ioe) {
189 return Collections.emptySet();
190 }
191 }
192 Collection<X509CRL> possibleCRLs = new ArrayList<X509CRL>();
193 Collection<X509CRL> crls = new ArrayList<X509CRL>(2);
194 for (Iterator<GeneralName> t = fullName.iterator(); t.hasNext(); ) {
195 GeneralName name = t.next();
196 if (name.getType() == GeneralNameInterface.NAME_DIRECTORY) {
197 X500Name x500Name = (X500Name) name.getName();
198 possibleCRLs.addAll(
199 getCRLs(x500Name, certImpl.getIssuerX500Principal(),
200 certStores));
201 } else if (name.getType() == GeneralNameInterface.NAME_URI) {
202 URIName uriName = (URIName)name.getName();
203 X509CRL crl = getCRL(uriName);
204 if (crl != null) {
205 possibleCRLs.add(crl);
206 }
207 }
208 }
209
210 for (X509CRL crl : possibleCRLs) {
211 try {
212 // make sure issuer is not set
213 // we check the issuer in verifyCRLs method
214 selector.setIssuerNames(null);
215 if (selector.match(crl) && verifyCRL(certImpl, point, crl,
216 reasonsMask, signFlag, prevKey, provider, anchor,
217 certStores)) {
218 crls.add(crl);
219 }
220 } catch (Exception e) {
221 // don't add the CRL
222 if (debug != null) {
223 debug.println("Exception verifying CRL: " + e.getMessage());
224 e.printStackTrace();
225 }
226 }
227 }
228 return crls;
229 }
230
231 /**
232 * Download CRL from given URI.
233 */
234 private X509CRL getCRL(URIName name) {
235 URI uri = name.getURI();
236 if (debug != null) {
237 debug.println("Trying to fetch CRL from DP " + uri);
238 }
239 try {
240 CertStore ucs = URICertStore.getInstance
241 (new URICertStore.URICertStoreParameters(uri));
242 Collection<? extends CRL> crls = ucs.getCRLs(null);
243 if (crls.isEmpty()) {
244 return null;
245 } else {
246 return (X509CRL) crls.iterator().next();
247 }
248 } catch (Exception e) {
249 if (debug != null) {
250 debug.println("Exception getting CRL from CertStore: " + e);
251 e.printStackTrace();
252 }
253 }
254 return null;
255 }
256
257 /**
258 * Fetch CRLs from certStores.
259 */
260 private Collection<X509CRL> getCRLs(X500Name name,
261 X500Principal certIssuer, List<CertStore> certStores)
262 {
263 if (debug != null) {
264 debug.println("Trying to fetch CRL from DP " + name);
265 }
266 X509CRLSelector xcs = new X509CRLSelector();
267 xcs.addIssuer(name.asX500Principal());
268 xcs.addIssuer(certIssuer);
269 Collection<X509CRL> crls = new ArrayList<X509CRL>();
270 for (CertStore store : certStores) {
271 try {
272 for (CRL crl : store.getCRLs(xcs)) {
273 crls.add((X509CRL)crl);
274 }
275 } catch (CertStoreException cse) {
276 // don't add the CRL
277 if (debug != null) {
278 debug.println("Non-fatal exception while retrieving " +
279 "CRLs: " + cse);
280 cse.printStackTrace();
281 }
282 }
283 }
284 return crls;
285 }
286
287 /**
288 * Verifies a CRL for the given certificate's Distribution Point to
289 * ensure it is appropriate for checking the revocation status.
290 *
291 * @param certImpl the certificate whose revocation status is being checked
292 * @param point one of the distribution points of the certificate
293 * @param crl the CRL
294 * @param reasonsMask the interim reasons mask
295 * @param signFlag true if prevKey can be used to verify the CRL
296 * @param prevKey the public key that verifies the certificate's signature
297 * @param provider the Signature provider to use
298 * @return true if ok, false if not
299 */
300 boolean verifyCRL(X509CertImpl certImpl, DistributionPoint point,
301 X509CRL crl, boolean[] reasonsMask, boolean signFlag,
302 PublicKey prevKey, String provider, TrustAnchor anchor,
303 List<CertStore> certStores) throws CRLException, IOException {
304 boolean indirectCRL = false;
305 X509CRLImpl crlImpl = X509CRLImpl.toImpl(crl);
306 IssuingDistributionPointExtension idpExt =
307 crlImpl.getIssuingDistributionPointExtension();
308 X500Name certIssuer = (X500Name) certImpl.getIssuerDN();
309 X500Name crlIssuer = (X500Name) crlImpl.getIssuerDN();
310
311 // if crlIssuer is set, verify that it matches the issuer of the
312 // CRL and the CRL contains an IDP extension with the indirectCRL
313 // boolean asserted. Otherwise, verify that the CRL issuer matches the
314 // certificate issuer.
315 GeneralNames pointCrlIssuers = point.getCRLIssuer();
316 X500Name pointCrlIssuer = null;
317 if (pointCrlIssuers != null) {
318 if (idpExt == null ||
319 ((Boolean) idpExt.get
320 (IssuingDistributionPointExtension.INDIRECT_CRL)).equals
321 (Boolean.FALSE)) {
322 return false;
323 }
324 boolean match = false;
325 for (Iterator<GeneralName> t = pointCrlIssuers.iterator();
326 !match && t.hasNext(); ) {
327 GeneralNameInterface name = t.next().getName();
328 if (crlIssuer.equals(name) == true) {
329 pointCrlIssuer = (X500Name) name;
330 match = true;
331 }
332 }
333 if (match == false) {
334 return false;
335 }
336 indirectCRL = true;
337 } else if (crlIssuer.equals(certIssuer) == false) {
338 if (debug != null) {
339 debug.println("crl issuer does not equal cert issuer");
340 }
341 return false;
342 }
343
344 if (!indirectCRL && !signFlag) {
345 // cert's key cannot be used to verify the CRL
346 return false;
347 }
348
349 if (idpExt != null) {
350 DistributionPointName idpPoint = (DistributionPointName)
351 idpExt.get(IssuingDistributionPointExtension.POINT);
352 if (idpPoint != null) {
353 GeneralNames idpNames = idpPoint.getFullName();
354 if (idpNames == null) {
355 RDN relativeName = idpPoint.getRelativeName();
356 if (relativeName == null) {
357 if (debug != null) {
358 debug.println("IDP must be relative or full DN");
359 }
360 return false;
361 }
362 if (debug != null) {
363 debug.println("IDP relativeName:" + relativeName);
364 }
365 idpNames = getFullNames(crlIssuer, relativeName);
366 }
367 // if the DP name is present in the IDP CRL extension and the
368 // DP field is present in the DP, then verify that one of the
369 // names in the IDP matches one of the names in the DP
370 if (point.getFullName() != null ||
371 point.getRelativeName() != null) {
372 GeneralNames pointNames = point.getFullName();
373 if (pointNames == null) {
374 RDN relativeName = point.getRelativeName();
375 if (relativeName == null) {
376 if (debug != null) {
377 debug.println("DP must be relative or full DN");
378 }
379 return false;
380 }
381 if (debug != null) {
382 debug.println("DP relativeName:" + relativeName);
383 }
384 if (indirectCRL) {
385 if (pointCrlIssuers.size() != 1) {
386 // RFC 3280: there must be only 1 CRL issuer
387 // name when relativeName is present
388 if (debug != null) {
389 debug.println("must only be one CRL " +
390 "issuer when relative name present");
391 }
392 return false;
393 }
394 pointNames = getFullNames
395 (pointCrlIssuer, relativeName);
396 } else {
397 pointNames = getFullNames(certIssuer, relativeName);
398 }
399 }
400 boolean match = false;
401 for (Iterator<GeneralName> i = idpNames.iterator();
402 !match && i.hasNext(); ) {
403 GeneralNameInterface idpName = i.next().getName();
404 if (debug != null) {
405 debug.println("idpName: " + idpName);
406 }
407 for (Iterator<GeneralName> p = pointNames.iterator();
408 !match && p.hasNext(); ) {
409 GeneralNameInterface pointName = p.next().getName();
410 if (debug != null) {
411 debug.println("pointName: " + pointName);
412 }
413 match = idpName.equals(pointName);
414 }
415 }
416 if (!match) {
417 if (debug != null) {
418 debug.println("IDP name does not match DP name");
419 }
420 return false;
421 }
422 // if the DP name is present in the IDP CRL extension and the
423 // DP field is absent from the DP, then verify that one of the
424 // names in the IDP matches one of the names in the crlIssuer
425 // field of the DP
426 } else {
427 // verify that one of the names in the IDP matches one of
428 // the names in the cRLIssuer of the cert's DP
429 boolean match = false;
430 for (Iterator<GeneralName> t = pointCrlIssuers.iterator();
431 !match && t.hasNext(); ) {
432 GeneralNameInterface crlIssuerName = t.next().getName();
433 for (Iterator<GeneralName> i = idpNames.iterator();
434 !match && i.hasNext(); ) {
435 GeneralNameInterface idpName = i.next().getName();
436 match = crlIssuerName.equals(idpName);
437 }
438 }
439 if (!match) {
440 return false;
441 }
442 }
443 }
444
445 // if the onlyContainsUserCerts boolean is asserted, verify that the
446 // cert is not a CA cert
447 Boolean b = (Boolean)
448 idpExt.get(IssuingDistributionPointExtension.ONLY_USER_CERTS);
449 if (b.equals(Boolean.TRUE) && certImpl.getBasicConstraints() != -1) {
450 if (debug != null) {
451 debug.println("cert must be a EE cert");
452 }
453 return false;
454 }
455
456 // if the onlyContainsCACerts boolean is asserted, verify that the
457 // cert is a CA cert
458 b = (Boolean)
459 idpExt.get(IssuingDistributionPointExtension.ONLY_CA_CERTS);
460 if (b.equals(Boolean.TRUE) && certImpl.getBasicConstraints() == -1) {
461 if (debug != null) {
462 debug.println("cert must be a CA cert");
463 }
464 return false;
465 }
466
467 // verify that the onlyContainsAttributeCerts boolean is not
468 // asserted
469 b = (Boolean) idpExt.get
470 (IssuingDistributionPointExtension.ONLY_ATTRIBUTE_CERTS);
471 if (b.equals(Boolean.TRUE)) {
472 if (debug != null) {
473 debug.println("cert must not be an AA cert");
474 }
475 return false;
476 }
477 }
478
479 // compute interim reasons mask
480 boolean[] interimReasonsMask = new boolean[9];
481 ReasonFlags reasons = null;
482 if (idpExt != null) {
483 reasons = (ReasonFlags)
484 idpExt.get(IssuingDistributionPointExtension.REASONS);
485 }
486
487 boolean[] pointReasonFlags = point.getReasonFlags();
488 if (reasons != null) {
489 if (pointReasonFlags != null) {
490 // set interim reasons mask to the intersection of
491 // reasons in the DP and onlySomeReasons in the IDP
492 boolean[] idpReasonFlags = reasons.getFlags();
493 for (int i = 0; i < idpReasonFlags.length; i++) {
494 if (idpReasonFlags[i] && pointReasonFlags[i]) {
495 interimReasonsMask[i] = true;
496 }
497 }
498 } else {
499 // set interim reasons mask to the value of
500 // onlySomeReasons in the IDP (and clone it since we may
501 // modify it)
502 interimReasonsMask = reasons.getFlags().clone();
503 }
504 } else if (idpExt == null || reasons == null) {
505 if (pointReasonFlags != null) {
506 // set interim reasons mask to the value of DP reasons
507 interimReasonsMask = pointReasonFlags.clone();
508 } else {
509 // set interim reasons mask to the special value all-reasons
510 interimReasonsMask = new boolean[9];
511 Arrays.fill(interimReasonsMask, true);
512 }
513 }
514
515 // verify that interim reasons mask includes one or more reasons
516 // not included in the reasons mask
517 boolean oneOrMore = false;
518 for (int i=0; i < interimReasonsMask.length && !oneOrMore; i++) {
519 if (!reasonsMask[i] && interimReasonsMask[i]) {
520 oneOrMore = true;
521 }
522 }
523 if (!oneOrMore) {
524 return false;
525 }
526
527 // Obtain and validate the certification path for the complete
528 // CRL issuer (if indirect CRL). If a key usage extension is present
529 // in the CRL issuer's certificate, verify that the cRLSign bit is set.
530 if (indirectCRL) {
531 X509CertSelector certSel = new X509CertSelector();
532 certSel.setSubject(crlIssuer.asX500Principal());
533 boolean[] crlSign = {false,false,false,false,false,false,true};
534 certSel.setKeyUsage(crlSign);
535 PKIXBuilderParameters params = null;
536 try {
537 params = new PKIXBuilderParameters
538 (Collections.singleton(anchor), certSel);
539 } catch (InvalidAlgorithmParameterException iape) {
540 throw new CRLException(iape);
541 }
542 params.setCertStores(certStores);
543 params.setSigProvider(provider);
544 try {
545 CertPathBuilder builder = CertPathBuilder.getInstance("PKIX");
546 PKIXCertPathBuilderResult result =
547 (PKIXCertPathBuilderResult) builder.build(params);
548 prevKey = result.getPublicKey();
549 } catch (Exception e) {
550 throw new CRLException(e);
551 }
552 }
553
554 // validate the signature on the CRL
555 try {
556 crl.verify(prevKey, provider);
557 } catch (Exception e) {
558 if (debug != null) {
559 debug.println("CRL signature failed to verify");
560 }
561 return false;
562 }
563
564 // reject CRL if any unresolved critical extensions remain in the CRL.
565 Set<String> unresCritExts = crl.getCriticalExtensionOIDs();
566 // remove any that we have processed
567 if (unresCritExts != null) {
568 unresCritExts.remove
569 (PKIXExtensions.IssuingDistributionPoint_Id.toString());
570 if (!unresCritExts.isEmpty()) {
571 if (debug != null) {
572 debug.println("Unrecognized critical extension(s) in CRL: "
573 + unresCritExts);
574 Iterator<String> i = unresCritExts.iterator();
575 while (i.hasNext())
576 debug.println(i.next());
577 }
578 return false;
579 }
580 }
581
582 // update reasonsMask
583 for (int i=0; i < interimReasonsMask.length; i++) {
584 if (!reasonsMask[i] && interimReasonsMask[i]) {
585 reasonsMask[i] = true;
586 }
587 }
588 return true;
589 }
590
591 /**
592 * Append relative name to the issuer name and return a new
593 * GeneralNames object.
594 */
595 private GeneralNames getFullNames(X500Name issuer, RDN rdn)
596 throws IOException {
597 List<RDN> rdns = new ArrayList<RDN>(issuer.rdns());
598 rdns.add(rdn);
599 X500Name fullName = new X500Name(rdns.toArray(new RDN[0]));
600 GeneralNames fullNames = new GeneralNames();
601 fullNames.add(new GeneralName(fullName));
602 return fullNames;
603 }
604}