Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/provider/certpath/RevocationChecker.java
38923 views
1
/*
2
* Copyright (c) 2012, 2017, 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.io.IOException;
29
import java.math.BigInteger;
30
import java.net.URI;
31
import java.net.URISyntaxException;
32
import java.security.AccessController;
33
import java.security.InvalidAlgorithmParameterException;
34
import java.security.NoSuchAlgorithmException;
35
import java.security.PrivilegedAction;
36
import java.security.PublicKey;
37
import java.security.Security;
38
import java.security.cert.CertPathValidatorException.BasicReason;
39
import java.security.cert.Extension;
40
import java.security.cert.*;
41
import java.util.*;
42
import javax.security.auth.x500.X500Principal;
43
44
import static sun.security.provider.certpath.OCSP.*;
45
import static sun.security.provider.certpath.PKIX.*;
46
import sun.security.x509.*;
47
import static sun.security.x509.PKIXExtensions.*;
48
import sun.security.util.Debug;
49
50
class RevocationChecker extends PKIXRevocationChecker {
51
52
private static final Debug debug = Debug.getInstance("certpath");
53
54
private TrustAnchor anchor;
55
private ValidatorParams params;
56
private boolean onlyEE;
57
private boolean softFail;
58
private boolean crlDP;
59
private URI responderURI;
60
private X509Certificate responderCert;
61
private List<CertStore> certStores;
62
private Map<X509Certificate, byte[]> ocspResponses;
63
private List<Extension> ocspExtensions;
64
private final boolean legacy;
65
private LinkedList<CertPathValidatorException> softFailExceptions =
66
new LinkedList<>();
67
68
// state variables
69
private OCSPResponse.IssuerInfo issuerInfo;
70
private PublicKey prevPubKey;
71
private boolean crlSignFlag;
72
private int certIndex;
73
74
private enum Mode { PREFER_OCSP, PREFER_CRLS, ONLY_CRLS, ONLY_OCSP };
75
private Mode mode = Mode.PREFER_OCSP;
76
77
private static class RevocationProperties {
78
boolean onlyEE;
79
boolean ocspEnabled;
80
boolean crlDPEnabled;
81
String ocspUrl;
82
String ocspSubject;
83
String ocspIssuer;
84
String ocspSerial;
85
}
86
87
RevocationChecker() {
88
legacy = false;
89
}
90
91
RevocationChecker(TrustAnchor anchor, ValidatorParams params)
92
throws CertPathValidatorException
93
{
94
legacy = true;
95
init(anchor, params);
96
}
97
98
void init(TrustAnchor anchor, ValidatorParams params)
99
throws CertPathValidatorException
100
{
101
RevocationProperties rp = getRevocationProperties();
102
URI uri = getOcspResponder();
103
responderURI = (uri == null) ? toURI(rp.ocspUrl) : uri;
104
X509Certificate cert = getOcspResponderCert();
105
responderCert = (cert == null)
106
? getResponderCert(rp, params.trustAnchors(),
107
params.certStores())
108
: cert;
109
Set<Option> options = getOptions();
110
for (Option option : options) {
111
switch (option) {
112
case ONLY_END_ENTITY:
113
case PREFER_CRLS:
114
case SOFT_FAIL:
115
case NO_FALLBACK:
116
break;
117
default:
118
throw new CertPathValidatorException(
119
"Unrecognized revocation parameter option: " + option);
120
}
121
}
122
softFail = options.contains(Option.SOFT_FAIL);
123
124
// set mode, only end entity flag
125
if (legacy) {
126
mode = (rp.ocspEnabled) ? Mode.PREFER_OCSP : Mode.ONLY_CRLS;
127
onlyEE = rp.onlyEE;
128
} else {
129
if (options.contains(Option.NO_FALLBACK)) {
130
if (options.contains(Option.PREFER_CRLS)) {
131
mode = Mode.ONLY_CRLS;
132
} else {
133
mode = Mode.ONLY_OCSP;
134
}
135
} else if (options.contains(Option.PREFER_CRLS)) {
136
mode = Mode.PREFER_CRLS;
137
}
138
onlyEE = options.contains(Option.ONLY_END_ENTITY);
139
}
140
if (legacy) {
141
crlDP = rp.crlDPEnabled;
142
} else {
143
crlDP = true;
144
}
145
ocspResponses = getOcspResponses();
146
ocspExtensions = getOcspExtensions();
147
148
this.anchor = anchor;
149
this.params = params;
150
this.certStores = new ArrayList<>(params.certStores());
151
try {
152
this.certStores.add(CertStore.getInstance("Collection",
153
new CollectionCertStoreParameters(params.certificates())));
154
} catch (InvalidAlgorithmParameterException |
155
NoSuchAlgorithmException e) {
156
// should never occur but not necessarily fatal, so log it,
157
// ignore and continue
158
if (debug != null) {
159
debug.println("RevocationChecker: " +
160
"error creating Collection CertStore: " + e);
161
}
162
}
163
}
164
165
private static URI toURI(String uriString)
166
throws CertPathValidatorException
167
{
168
try {
169
if (uriString != null) {
170
return new URI(uriString);
171
}
172
return null;
173
} catch (URISyntaxException e) {
174
throw new CertPathValidatorException(
175
"cannot parse ocsp.responderURL property", e);
176
}
177
}
178
179
private static RevocationProperties getRevocationProperties() {
180
return AccessController.doPrivileged(
181
new PrivilegedAction<RevocationProperties>() {
182
public RevocationProperties run() {
183
RevocationProperties rp = new RevocationProperties();
184
String onlyEE = Security.getProperty(
185
"com.sun.security.onlyCheckRevocationOfEECert");
186
rp.onlyEE = onlyEE != null
187
&& onlyEE.equalsIgnoreCase("true");
188
String ocspEnabled = Security.getProperty("ocsp.enable");
189
rp.ocspEnabled = ocspEnabled != null
190
&& ocspEnabled.equalsIgnoreCase("true");
191
rp.ocspUrl = Security.getProperty("ocsp.responderURL");
192
rp.ocspSubject
193
= Security.getProperty("ocsp.responderCertSubjectName");
194
rp.ocspIssuer
195
= Security.getProperty("ocsp.responderCertIssuerName");
196
rp.ocspSerial
197
= Security.getProperty("ocsp.responderCertSerialNumber");
198
rp.crlDPEnabled
199
= Boolean.getBoolean("com.sun.security.enableCRLDP");
200
return rp;
201
}
202
}
203
);
204
}
205
206
private static X509Certificate getResponderCert(RevocationProperties rp,
207
Set<TrustAnchor> anchors,
208
List<CertStore> stores)
209
throws CertPathValidatorException
210
{
211
if (rp.ocspSubject != null) {
212
return getResponderCert(rp.ocspSubject, anchors, stores);
213
} else if (rp.ocspIssuer != null && rp.ocspSerial != null) {
214
return getResponderCert(rp.ocspIssuer, rp.ocspSerial,
215
anchors, stores);
216
} else if (rp.ocspIssuer != null || rp.ocspSerial != null) {
217
throw new CertPathValidatorException(
218
"Must specify both ocsp.responderCertIssuerName and " +
219
"ocsp.responderCertSerialNumber properties");
220
}
221
return null;
222
}
223
224
private static X509Certificate getResponderCert(String subject,
225
Set<TrustAnchor> anchors,
226
List<CertStore> stores)
227
throws CertPathValidatorException
228
{
229
X509CertSelector sel = new X509CertSelector();
230
try {
231
sel.setSubject(new X500Principal(subject));
232
} catch (IllegalArgumentException e) {
233
throw new CertPathValidatorException(
234
"cannot parse ocsp.responderCertSubjectName property", e);
235
}
236
return getResponderCert(sel, anchors, stores);
237
}
238
239
private static X509Certificate getResponderCert(String issuer,
240
String serial,
241
Set<TrustAnchor> anchors,
242
List<CertStore> stores)
243
throws CertPathValidatorException
244
{
245
X509CertSelector sel = new X509CertSelector();
246
try {
247
sel.setIssuer(new X500Principal(issuer));
248
} catch (IllegalArgumentException e) {
249
throw new CertPathValidatorException(
250
"cannot parse ocsp.responderCertIssuerName property", e);
251
}
252
try {
253
sel.setSerialNumber(new BigInteger(stripOutSeparators(serial), 16));
254
} catch (NumberFormatException e) {
255
throw new CertPathValidatorException(
256
"cannot parse ocsp.responderCertSerialNumber property", e);
257
}
258
return getResponderCert(sel, anchors, stores);
259
}
260
261
private static X509Certificate getResponderCert(X509CertSelector sel,
262
Set<TrustAnchor> anchors,
263
List<CertStore> stores)
264
throws CertPathValidatorException
265
{
266
// first check TrustAnchors
267
for (TrustAnchor anchor : anchors) {
268
X509Certificate cert = anchor.getTrustedCert();
269
if (cert == null) {
270
continue;
271
}
272
if (sel.match(cert)) {
273
return cert;
274
}
275
}
276
// now check CertStores
277
for (CertStore store : stores) {
278
try {
279
Collection<? extends Certificate> certs =
280
store.getCertificates(sel);
281
if (!certs.isEmpty()) {
282
return (X509Certificate)certs.iterator().next();
283
}
284
} catch (CertStoreException e) {
285
// ignore and try next CertStore
286
if (debug != null) {
287
debug.println("CertStore exception:" + e);
288
}
289
continue;
290
}
291
}
292
throw new CertPathValidatorException(
293
"Cannot find the responder's certificate " +
294
"(set using the OCSP security properties).");
295
}
296
297
@Override
298
public void init(boolean forward) throws CertPathValidatorException {
299
if (forward) {
300
throw new
301
CertPathValidatorException("forward checking not supported");
302
}
303
if (anchor != null) {
304
issuerInfo = new OCSPResponse.IssuerInfo(anchor);
305
prevPubKey = issuerInfo.getPublicKey();
306
307
}
308
crlSignFlag = true;
309
if (params != null && params.certPath() != null) {
310
certIndex = params.certPath().getCertificates().size() - 1;
311
} else {
312
certIndex = -1;
313
}
314
softFailExceptions.clear();
315
}
316
317
@Override
318
public boolean isForwardCheckingSupported() {
319
return false;
320
}
321
322
@Override
323
public Set<String> getSupportedExtensions() {
324
return null;
325
}
326
327
@Override
328
public List<CertPathValidatorException> getSoftFailExceptions() {
329
return Collections.unmodifiableList(softFailExceptions);
330
}
331
332
@Override
333
public void check(Certificate cert, Collection<String> unresolvedCritExts)
334
throws CertPathValidatorException
335
{
336
check((X509Certificate)cert, unresolvedCritExts,
337
prevPubKey, crlSignFlag);
338
}
339
340
private void check(X509Certificate xcert,
341
Collection<String> unresolvedCritExts,
342
PublicKey pubKey, boolean crlSignFlag)
343
throws CertPathValidatorException
344
{
345
if (debug != null) {
346
debug.println("RevocationChecker.check: checking cert" +
347
"\n SN: " + Debug.toHexString(xcert.getSerialNumber()) +
348
"\n Subject: " + xcert.getSubjectX500Principal() +
349
"\n Issuer: " + xcert.getIssuerX500Principal());
350
}
351
try {
352
if (onlyEE && xcert.getBasicConstraints() != -1) {
353
if (debug != null) {
354
debug.println("Skipping revocation check; cert is not " +
355
"an end entity cert");
356
}
357
return;
358
}
359
switch (mode) {
360
case PREFER_OCSP:
361
case ONLY_OCSP:
362
checkOCSP(xcert, unresolvedCritExts);
363
break;
364
case PREFER_CRLS:
365
case ONLY_CRLS:
366
checkCRLs(xcert, unresolvedCritExts, null,
367
pubKey, crlSignFlag);
368
break;
369
}
370
} catch (CertPathValidatorException e) {
371
if (e.getReason() == BasicReason.REVOKED) {
372
throw e;
373
}
374
boolean eSoftFail = isSoftFailException(e);
375
if (eSoftFail) {
376
if (mode == Mode.ONLY_OCSP || mode == Mode.ONLY_CRLS) {
377
return;
378
}
379
} else {
380
if (mode == Mode.ONLY_OCSP || mode == Mode.ONLY_CRLS) {
381
throw e;
382
}
383
}
384
CertPathValidatorException cause = e;
385
// Otherwise, failover
386
if (debug != null) {
387
debug.println("RevocationChecker.check() " + e.getMessage());
388
debug.println("RevocationChecker.check() preparing to failover");
389
}
390
try {
391
switch (mode) {
392
case PREFER_OCSP:
393
checkCRLs(xcert, unresolvedCritExts, null,
394
pubKey, crlSignFlag);
395
break;
396
case PREFER_CRLS:
397
checkOCSP(xcert, unresolvedCritExts);
398
break;
399
}
400
} catch (CertPathValidatorException x) {
401
if (debug != null) {
402
debug.println("RevocationChecker.check() failover failed");
403
debug.println("RevocationChecker.check() " + x.getMessage());
404
}
405
if (x.getReason() == BasicReason.REVOKED) {
406
throw x;
407
}
408
if (!isSoftFailException(x)) {
409
cause.addSuppressed(x);
410
throw cause;
411
} else {
412
// only pass if both exceptions were soft failures
413
if (!eSoftFail) {
414
throw cause;
415
}
416
}
417
}
418
} finally {
419
updateState(xcert);
420
}
421
}
422
423
private boolean isSoftFailException(CertPathValidatorException e) {
424
if (softFail &&
425
e.getReason() == BasicReason.UNDETERMINED_REVOCATION_STATUS)
426
{
427
// recreate exception with correct index
428
CertPathValidatorException e2 = new CertPathValidatorException(
429
e.getMessage(), e.getCause(), params.certPath(), certIndex,
430
e.getReason());
431
softFailExceptions.addFirst(e2);
432
return true;
433
}
434
return false;
435
}
436
437
private void updateState(X509Certificate cert)
438
throws CertPathValidatorException
439
{
440
issuerInfo = new OCSPResponse.IssuerInfo(anchor, cert);
441
442
// Make new public key if parameters are missing
443
PublicKey pubKey = cert.getPublicKey();
444
if (PKIX.isDSAPublicKeyWithoutParams(pubKey)) {
445
// pubKey needs to inherit DSA parameters from prev key
446
pubKey = BasicChecker.makeInheritedParamsKey(pubKey, prevPubKey);
447
}
448
prevPubKey = pubKey;
449
crlSignFlag = certCanSignCrl(cert);
450
if (certIndex > 0) {
451
certIndex--;
452
}
453
}
454
455
// Maximum clock skew in milliseconds (15 minutes) allowed when checking
456
// validity of CRLs
457
private static final long MAX_CLOCK_SKEW = 900000;
458
private void checkCRLs(X509Certificate cert,
459
Collection<String> unresolvedCritExts,
460
Set<X509Certificate> stackedCerts,
461
PublicKey pubKey, boolean signFlag)
462
throws CertPathValidatorException
463
{
464
checkCRLs(cert, pubKey, null, signFlag, true,
465
stackedCerts, params.trustAnchors());
466
}
467
468
static boolean isCausedByNetworkIssue(String type, CertStoreException cse) {
469
boolean result;
470
Throwable t = cse.getCause();
471
472
switch (type) {
473
case "LDAP":
474
if (t != null) {
475
// These two exception classes are inside java.naming module
476
String cn = t.getClass().getName();
477
result = (cn.equals("javax.naming.ServiceUnavailableException") ||
478
cn.equals("javax.naming.CommunicationException"));
479
} else {
480
result = false;
481
}
482
break;
483
case "SSLServer":
484
result = (t != null && t instanceof IOException);
485
break;
486
case "URI":
487
result = (t != null && t instanceof IOException);
488
break;
489
default:
490
// we don't know about any other remote CertStore types
491
return false;
492
}
493
return result;
494
}
495
496
private void checkCRLs(X509Certificate cert, PublicKey prevKey,
497
X509Certificate prevCert, boolean signFlag,
498
boolean allowSeparateKey,
499
Set<X509Certificate> stackedCerts,
500
Set<TrustAnchor> anchors)
501
throws CertPathValidatorException
502
{
503
if (debug != null) {
504
debug.println("RevocationChecker.checkCRLs()" +
505
" ---checking revocation status ...");
506
}
507
508
// Reject circular dependencies - RFC 5280 is not explicit on how
509
// to handle this, but does suggest that they can be a security
510
// risk and can create unresolvable dependencies
511
if (stackedCerts != null && stackedCerts.contains(cert)) {
512
if (debug != null) {
513
debug.println("RevocationChecker.checkCRLs()" +
514
" circular dependency");
515
}
516
throw new CertPathValidatorException
517
("Could not determine revocation status", null, null, -1,
518
BasicReason.UNDETERMINED_REVOCATION_STATUS);
519
}
520
521
Set<X509CRL> possibleCRLs = new HashSet<>();
522
Set<X509CRL> approvedCRLs = new HashSet<>();
523
X509CRLSelector sel = new X509CRLSelector();
524
sel.setCertificateChecking(cert);
525
CertPathHelper.setDateAndTime(sel, params.date(), MAX_CLOCK_SKEW);
526
527
// First, check user-specified CertStores
528
CertPathValidatorException networkFailureException = null;
529
for (CertStore store : certStores) {
530
try {
531
for (CRL crl : store.getCRLs(sel)) {
532
possibleCRLs.add((X509CRL)crl);
533
}
534
} catch (CertStoreException e) {
535
if (debug != null) {
536
debug.println("RevocationChecker.checkCRLs() " +
537
"CertStoreException: " + e.getMessage());
538
}
539
if (networkFailureException == null &&
540
isCausedByNetworkIssue(store.getType(),e)) {
541
// save this exception, we may need to throw it later
542
networkFailureException = new CertPathValidatorException(
543
"Unable to determine revocation status due to " +
544
"network error", e, null, -1,
545
BasicReason.UNDETERMINED_REVOCATION_STATUS);
546
}
547
}
548
}
549
550
if (debug != null) {
551
debug.println("RevocationChecker.checkCRLs() " +
552
"possible crls.size() = " + possibleCRLs.size());
553
}
554
boolean[] reasonsMask = new boolean[9];
555
if (!possibleCRLs.isEmpty()) {
556
// Now that we have a list of possible CRLs, see which ones can
557
// be approved
558
approvedCRLs.addAll(verifyPossibleCRLs(possibleCRLs, cert, prevKey,
559
signFlag, reasonsMask,
560
anchors));
561
}
562
563
if (debug != null) {
564
debug.println("RevocationChecker.checkCRLs() " +
565
"approved crls.size() = " + approvedCRLs.size());
566
}
567
568
// make sure that we have at least one CRL that _could_ cover
569
// the certificate in question and all reasons are covered
570
if (!approvedCRLs.isEmpty() &&
571
Arrays.equals(reasonsMask, ALL_REASONS))
572
{
573
checkApprovedCRLs(cert, approvedCRLs);
574
} else {
575
// Check Distribution Points
576
// all CRLs returned by the DP Fetcher have also been verified
577
try {
578
if (crlDP) {
579
approvedCRLs.addAll(DistributionPointFetcher.getCRLs(
580
sel, signFlag, prevKey, prevCert,
581
params.sigProvider(), certStores, reasonsMask,
582
anchors, null, params.variant(), anchor));
583
}
584
} catch (CertStoreException e) {
585
if (e instanceof CertStoreTypeException) {
586
CertStoreTypeException cste = (CertStoreTypeException)e;
587
if (isCausedByNetworkIssue(cste.getType(), e)) {
588
throw new CertPathValidatorException(
589
"Unable to determine revocation status due to " +
590
"network error", e, null, -1,
591
BasicReason.UNDETERMINED_REVOCATION_STATUS);
592
}
593
}
594
throw new CertPathValidatorException(e);
595
}
596
if (!approvedCRLs.isEmpty() &&
597
Arrays.equals(reasonsMask, ALL_REASONS))
598
{
599
checkApprovedCRLs(cert, approvedCRLs);
600
} else {
601
if (allowSeparateKey) {
602
try {
603
verifyWithSeparateSigningKey(cert, prevKey, signFlag,
604
stackedCerts);
605
return;
606
} catch (CertPathValidatorException cpve) {
607
if (networkFailureException != null) {
608
// if a network issue previously prevented us from
609
// retrieving a CRL from one of the user-specified
610
// CertStores, throw it now so it can be handled
611
// appropriately
612
throw networkFailureException;
613
}
614
throw cpve;
615
}
616
} else {
617
if (networkFailureException != null) {
618
// if a network issue previously prevented us from
619
// retrieving a CRL from one of the user-specified
620
// CertStores, throw it now so it can be handled
621
// appropriately
622
throw networkFailureException;
623
}
624
throw new CertPathValidatorException(
625
"Could not determine revocation status", null, null, -1,
626
BasicReason.UNDETERMINED_REVOCATION_STATUS);
627
}
628
}
629
}
630
}
631
632
private void checkApprovedCRLs(X509Certificate cert,
633
Set<X509CRL> approvedCRLs)
634
throws CertPathValidatorException
635
{
636
// See if the cert is in the set of approved crls.
637
if (debug != null) {
638
BigInteger sn = cert.getSerialNumber();
639
debug.println("RevocationChecker.checkApprovedCRLs() " +
640
"starting the final sweep...");
641
debug.println("RevocationChecker.checkApprovedCRLs()" +
642
" cert SN: " + sn.toString());
643
}
644
645
CRLReason reasonCode = CRLReason.UNSPECIFIED;
646
X509CRLEntryImpl entry = null;
647
for (X509CRL crl : approvedCRLs) {
648
X509CRLEntry e = crl.getRevokedCertificate(cert);
649
if (e != null) {
650
try {
651
entry = X509CRLEntryImpl.toImpl(e);
652
} catch (CRLException ce) {
653
throw new CertPathValidatorException(ce);
654
}
655
if (debug != null) {
656
debug.println("RevocationChecker.checkApprovedCRLs()"
657
+ " CRL entry: " + entry.toString());
658
}
659
660
/*
661
* Abort CRL validation and throw exception if there are any
662
* unrecognized critical CRL entry extensions (see section
663
* 5.3 of RFC 5280).
664
*/
665
Set<String> unresCritExts = entry.getCriticalExtensionOIDs();
666
if (unresCritExts != null && !unresCritExts.isEmpty()) {
667
/* remove any that we will process */
668
unresCritExts.remove(ReasonCode_Id.toString());
669
unresCritExts.remove(CertificateIssuer_Id.toString());
670
if (!unresCritExts.isEmpty()) {
671
throw new CertPathValidatorException(
672
"Unrecognized critical extension(s) in revoked " +
673
"CRL entry");
674
}
675
}
676
677
reasonCode = entry.getRevocationReason();
678
if (reasonCode == null) {
679
reasonCode = CRLReason.UNSPECIFIED;
680
}
681
Date revocationDate = entry.getRevocationDate();
682
if (revocationDate.before(params.date())) {
683
Throwable t = new CertificateRevokedException(
684
revocationDate, reasonCode,
685
crl.getIssuerX500Principal(), entry.getExtensions());
686
throw new CertPathValidatorException(
687
t.getMessage(), t, null, -1, BasicReason.REVOKED);
688
}
689
}
690
}
691
}
692
693
private void checkOCSP(X509Certificate cert,
694
Collection<String> unresolvedCritExts)
695
throws CertPathValidatorException
696
{
697
X509CertImpl currCert = null;
698
try {
699
currCert = X509CertImpl.toImpl(cert);
700
} catch (CertificateException ce) {
701
throw new CertPathValidatorException(ce);
702
}
703
704
// The algorithm constraints of the OCSP trusted responder certificate
705
// does not need to be checked in this code. The constraints will be
706
// checked when the responder's certificate is validated.
707
708
OCSPResponse response = null;
709
CertId certId = null;
710
try {
711
certId = new CertId(issuerInfo.getName(), issuerInfo.getPublicKey(),
712
currCert.getSerialNumberObject());
713
714
// check if there is a cached OCSP response available
715
byte[] responseBytes = ocspResponses.get(cert);
716
if (responseBytes != null) {
717
if (debug != null) {
718
debug.println("Found cached OCSP response");
719
}
720
response = new OCSPResponse(responseBytes);
721
722
// verify the response
723
byte[] nonce = null;
724
for (Extension ext : ocspExtensions) {
725
if (ext.getId().equals("1.3.6.1.5.5.7.48.1.2")) {
726
nonce = ext.getValue();
727
}
728
}
729
response.verify(Collections.singletonList(certId), issuerInfo,
730
responderCert, params.date(), nonce, params.variant());
731
732
} else {
733
URI responderURI = (this.responderURI != null)
734
? this.responderURI
735
: OCSP.getResponderURI(currCert);
736
if (responderURI == null) {
737
throw new CertPathValidatorException(
738
"Certificate does not specify OCSP responder", null,
739
null, -1);
740
}
741
742
response = OCSP.check(Collections.singletonList(certId),
743
responderURI, issuerInfo, responderCert, null,
744
ocspExtensions, params.variant());
745
}
746
} catch (IOException e) {
747
throw new CertPathValidatorException(
748
"Unable to determine revocation status due to network error",
749
e, null, -1, BasicReason.UNDETERMINED_REVOCATION_STATUS);
750
}
751
752
RevocationStatus rs =
753
(RevocationStatus)response.getSingleResponse(certId);
754
RevocationStatus.CertStatus certStatus = rs.getCertStatus();
755
if (certStatus == RevocationStatus.CertStatus.REVOKED) {
756
Date revocationTime = rs.getRevocationTime();
757
if (revocationTime.before(params.date())) {
758
Throwable t = new CertificateRevokedException(
759
revocationTime, rs.getRevocationReason(),
760
response.getSignerCertificate().getSubjectX500Principal(),
761
rs.getSingleExtensions());
762
throw new CertPathValidatorException(t.getMessage(), t, null,
763
-1, BasicReason.REVOKED);
764
}
765
} else if (certStatus == RevocationStatus.CertStatus.UNKNOWN) {
766
throw new CertPathValidatorException(
767
"Certificate's revocation status is unknown", null,
768
params.certPath(), -1,
769
BasicReason.UNDETERMINED_REVOCATION_STATUS);
770
}
771
}
772
773
/*
774
* Removes any non-hexadecimal characters from a string.
775
*/
776
private static final String HEX_DIGITS = "0123456789ABCDEFabcdef";
777
private static String stripOutSeparators(String value) {
778
char[] chars = value.toCharArray();
779
StringBuilder hexNumber = new StringBuilder();
780
for (int i = 0; i < chars.length; i++) {
781
if (HEX_DIGITS.indexOf(chars[i]) != -1) {
782
hexNumber.append(chars[i]);
783
}
784
}
785
return hexNumber.toString();
786
}
787
788
/**
789
* Checks that a cert can be used to verify a CRL.
790
*
791
* @param cert an X509Certificate to check
792
* @return a boolean specifying if the cert is allowed to vouch for the
793
* validity of a CRL
794
*/
795
static boolean certCanSignCrl(X509Certificate cert) {
796
// if the cert doesn't include the key usage ext, or
797
// the key usage ext asserts cRLSigning, return true,
798
// otherwise return false.
799
boolean[] keyUsage = cert.getKeyUsage();
800
if (keyUsage != null) {
801
return keyUsage[6];
802
}
803
return false;
804
}
805
806
/**
807
* Internal method that verifies a set of possible_crls,
808
* and sees if each is approved, based on the cert.
809
*
810
* @param crls a set of possible CRLs to test for acceptability
811
* @param cert the certificate whose revocation status is being checked
812
* @param signFlag <code>true</code> if prevKey was trusted to sign CRLs
813
* @param prevKey the public key of the issuer of cert
814
* @param reasonsMask the reason code mask
815
* @param trustAnchors a <code>Set</code> of <code>TrustAnchor</code>s>
816
* @return a collection of approved crls (or an empty collection)
817
*/
818
private static final boolean[] ALL_REASONS =
819
{true, true, true, true, true, true, true, true, true};
820
private Collection<X509CRL> verifyPossibleCRLs(Set<X509CRL> crls,
821
X509Certificate cert,
822
PublicKey prevKey,
823
boolean signFlag,
824
boolean[] reasonsMask,
825
Set<TrustAnchor> anchors)
826
throws CertPathValidatorException
827
{
828
try {
829
X509CertImpl certImpl = X509CertImpl.toImpl(cert);
830
if (debug != null) {
831
debug.println("RevocationChecker.verifyPossibleCRLs: " +
832
"Checking CRLDPs for "
833
+ certImpl.getSubjectX500Principal());
834
}
835
CRLDistributionPointsExtension ext =
836
certImpl.getCRLDistributionPointsExtension();
837
List<DistributionPoint> points = null;
838
if (ext == null) {
839
// assume a DP with reasons and CRLIssuer fields omitted
840
// and a DP name of the cert issuer.
841
// TODO add issuerAltName too
842
X500Name certIssuer = (X500Name)certImpl.getIssuerDN();
843
DistributionPoint point = new DistributionPoint(
844
new GeneralNames().add(new GeneralName(certIssuer)),
845
null, null);
846
points = Collections.singletonList(point);
847
} else {
848
points = ext.get(CRLDistributionPointsExtension.POINTS);
849
}
850
Set<X509CRL> results = new HashSet<>();
851
for (DistributionPoint point : points) {
852
for (X509CRL crl : crls) {
853
if (DistributionPointFetcher.verifyCRL(
854
certImpl, point, crl, reasonsMask, signFlag,
855
prevKey, null, params.sigProvider(), anchors,
856
certStores, params.date(), params.variant(), anchor))
857
{
858
results.add(crl);
859
}
860
}
861
if (Arrays.equals(reasonsMask, ALL_REASONS))
862
break;
863
}
864
return results;
865
} catch (CertificateException | CRLException | IOException e) {
866
if (debug != null) {
867
debug.println("Exception while verifying CRL: "+e.getMessage());
868
e.printStackTrace();
869
}
870
return Collections.emptySet();
871
}
872
}
873
874
/**
875
* We have a cert whose revocation status couldn't be verified by
876
* a CRL issued by the cert that issued the CRL. See if we can
877
* find a valid CRL issued by a separate key that can verify the
878
* revocation status of this certificate.
879
* <p>
880
* Note that this does not provide support for indirect CRLs,
881
* only CRLs signed with a different key (but the same issuer
882
* name) as the certificate being checked.
883
*
884
* @param currCert the <code>X509Certificate</code> to be checked
885
* @param prevKey the <code>PublicKey</code> that failed
886
* @param signFlag <code>true</code> if that key was trusted to sign CRLs
887
* @param stackedCerts a <code>Set</code> of <code>X509Certificate</code>s>
888
* whose revocation status depends on the
889
* non-revoked status of this cert. To avoid
890
* circular dependencies, we assume they're
891
* revoked while checking the revocation
892
* status of this cert.
893
* @throws CertPathValidatorException if the cert's revocation status
894
* cannot be verified successfully with another key
895
*/
896
private void verifyWithSeparateSigningKey(X509Certificate cert,
897
PublicKey prevKey,
898
boolean signFlag,
899
Set<X509Certificate> stackedCerts)
900
throws CertPathValidatorException
901
{
902
String msg = "revocation status";
903
if (debug != null) {
904
debug.println(
905
"RevocationChecker.verifyWithSeparateSigningKey()" +
906
" ---checking " + msg + "...");
907
}
908
909
// Reject circular dependencies - RFC 5280 is not explicit on how
910
// to handle this, but does suggest that they can be a security
911
// risk and can create unresolvable dependencies
912
if ((stackedCerts != null) && stackedCerts.contains(cert)) {
913
if (debug != null) {
914
debug.println(
915
"RevocationChecker.verifyWithSeparateSigningKey()" +
916
" circular dependency");
917
}
918
throw new CertPathValidatorException
919
("Could not determine revocation status", null, null, -1,
920
BasicReason.UNDETERMINED_REVOCATION_STATUS);
921
}
922
923
// Try to find another key that might be able to sign
924
// CRLs vouching for this cert.
925
// If prevKey wasn't trusted, maybe we just didn't have the right
926
// path to it. Don't rule that key out.
927
if (!signFlag) {
928
buildToNewKey(cert, null, stackedCerts);
929
} else {
930
buildToNewKey(cert, prevKey, stackedCerts);
931
}
932
}
933
934
/**
935
* Tries to find a CertPath that establishes a key that can be
936
* used to verify the revocation status of a given certificate.
937
* Ignores keys that have previously been tried. Throws a
938
* CertPathValidatorException if no such key could be found.
939
*
940
* @param currCert the <code>X509Certificate</code> to be checked
941
* @param prevKey the <code>PublicKey</code> of the certificate whose key
942
* cannot be used to vouch for the CRL and should be ignored
943
* @param stackedCerts a <code>Set</code> of <code>X509Certificate</code>s>
944
* whose revocation status depends on the
945
* establishment of this path.
946
* @throws CertPathValidatorException on failure
947
*/
948
private static final boolean [] CRL_SIGN_USAGE =
949
{ false, false, false, false, false, false, true };
950
private void buildToNewKey(X509Certificate currCert,
951
PublicKey prevKey,
952
Set<X509Certificate> stackedCerts)
953
throws CertPathValidatorException
954
{
955
956
if (debug != null) {
957
debug.println("RevocationChecker.buildToNewKey()" +
958
" starting work");
959
}
960
Set<PublicKey> badKeys = new HashSet<>();
961
if (prevKey != null) {
962
badKeys.add(prevKey);
963
}
964
X509CertSelector certSel = new RejectKeySelector(badKeys);
965
certSel.setSubject(currCert.getIssuerX500Principal());
966
certSel.setKeyUsage(CRL_SIGN_USAGE);
967
968
Set<TrustAnchor> newAnchors = anchor == null ?
969
params.trustAnchors() :
970
Collections.singleton(anchor);
971
972
PKIXBuilderParameters builderParams;
973
try {
974
builderParams = new PKIXBuilderParameters(newAnchors, certSel);
975
} catch (InvalidAlgorithmParameterException iape) {
976
throw new RuntimeException(iape); // should never occur
977
}
978
builderParams.setInitialPolicies(params.initialPolicies());
979
builderParams.setCertStores(certStores);
980
builderParams.setExplicitPolicyRequired
981
(params.explicitPolicyRequired());
982
builderParams.setPolicyMappingInhibited
983
(params.policyMappingInhibited());
984
builderParams.setAnyPolicyInhibited(params.anyPolicyInhibited());
985
// Policy qualifiers must be rejected, since we don't have
986
// any way to convey them back to the application.
987
// That's the default, so no need to write code.
988
builderParams.setDate(params.date());
989
builderParams.setCertPathCheckers(params.certPathCheckers());
990
builderParams.setSigProvider(params.sigProvider());
991
992
// Skip revocation during this build to detect circular
993
// references. But check revocation afterwards, using the
994
// key (or any other that works).
995
builderParams.setRevocationEnabled(false);
996
997
// check for AuthorityInformationAccess extension
998
if (Builder.USE_AIA == true) {
999
X509CertImpl currCertImpl = null;
1000
try {
1001
currCertImpl = X509CertImpl.toImpl(currCert);
1002
} catch (CertificateException ce) {
1003
// ignore but log it
1004
if (debug != null) {
1005
debug.println("RevocationChecker.buildToNewKey: " +
1006
"error decoding cert: " + ce);
1007
}
1008
}
1009
AuthorityInfoAccessExtension aiaExt = null;
1010
if (currCertImpl != null) {
1011
aiaExt = currCertImpl.getAuthorityInfoAccessExtension();
1012
}
1013
if (aiaExt != null) {
1014
List<AccessDescription> adList = aiaExt.getAccessDescriptions();
1015
if (adList != null) {
1016
for (AccessDescription ad : adList) {
1017
CertStore cs = URICertStore.getInstance(ad);
1018
if (cs != null) {
1019
if (debug != null) {
1020
debug.println("adding AIAext CertStore");
1021
}
1022
builderParams.addCertStore(cs);
1023
}
1024
}
1025
}
1026
}
1027
}
1028
1029
CertPathBuilder builder = null;
1030
try {
1031
builder = CertPathBuilder.getInstance("PKIX");
1032
} catch (NoSuchAlgorithmException nsae) {
1033
throw new CertPathValidatorException(nsae);
1034
}
1035
while (true) {
1036
try {
1037
if (debug != null) {
1038
debug.println("RevocationChecker.buildToNewKey()" +
1039
" about to try build ...");
1040
}
1041
PKIXCertPathBuilderResult cpbr =
1042
(PKIXCertPathBuilderResult)builder.build(builderParams);
1043
1044
if (debug != null) {
1045
debug.println("RevocationChecker.buildToNewKey()" +
1046
" about to check revocation ...");
1047
}
1048
// Now check revocation of all certs in path, assuming that
1049
// the stackedCerts are revoked.
1050
if (stackedCerts == null) {
1051
stackedCerts = new HashSet<X509Certificate>();
1052
}
1053
stackedCerts.add(currCert);
1054
TrustAnchor ta = cpbr.getTrustAnchor();
1055
PublicKey prevKey2 = ta.getCAPublicKey();
1056
if (prevKey2 == null) {
1057
prevKey2 = ta.getTrustedCert().getPublicKey();
1058
}
1059
boolean signFlag = true;
1060
List<? extends Certificate> cpList =
1061
cpbr.getCertPath().getCertificates();
1062
try {
1063
for (int i = cpList.size() - 1; i >= 0; i--) {
1064
X509Certificate cert = (X509Certificate) cpList.get(i);
1065
1066
if (debug != null) {
1067
debug.println("RevocationChecker.buildToNewKey()"
1068
+ " index " + i + " checking "
1069
+ cert);
1070
}
1071
checkCRLs(cert, prevKey2, null, signFlag, true,
1072
stackedCerts, newAnchors);
1073
signFlag = certCanSignCrl(cert);
1074
prevKey2 = cert.getPublicKey();
1075
}
1076
} catch (CertPathValidatorException cpve) {
1077
// ignore it and try to get another key
1078
badKeys.add(cpbr.getPublicKey());
1079
continue;
1080
}
1081
1082
if (debug != null) {
1083
debug.println("RevocationChecker.buildToNewKey()" +
1084
" got key " + cpbr.getPublicKey());
1085
}
1086
// Now check revocation on the current cert using that key and
1087
// the corresponding certificate.
1088
// If it doesn't check out, try to find a different key.
1089
// And if we can't find a key, then return false.
1090
PublicKey newKey = cpbr.getPublicKey();
1091
X509Certificate newCert = cpList.isEmpty() ?
1092
null : (X509Certificate) cpList.get(0);
1093
try {
1094
checkCRLs(currCert, newKey, newCert,
1095
true, false, null, params.trustAnchors());
1096
// If that passed, the cert is OK!
1097
return;
1098
} catch (CertPathValidatorException cpve) {
1099
// If it is revoked, rethrow exception
1100
if (cpve.getReason() == BasicReason.REVOKED) {
1101
throw cpve;
1102
}
1103
// Otherwise, ignore the exception and
1104
// try to get another key.
1105
}
1106
badKeys.add(newKey);
1107
} catch (InvalidAlgorithmParameterException iape) {
1108
throw new CertPathValidatorException(iape);
1109
} catch (CertPathBuilderException cpbe) {
1110
throw new CertPathValidatorException
1111
("Could not determine revocation status", null, null,
1112
-1, BasicReason.UNDETERMINED_REVOCATION_STATUS);
1113
}
1114
}
1115
}
1116
1117
/*
1118
* This inner class extends the X509CertSelector to add an additional
1119
* check to make sure the subject public key isn't on a particular list.
1120
* This class is used by buildToNewKey() to make sure the builder doesn't
1121
* end up with a CertPath to a public key that has already been rejected.
1122
*/
1123
private static class RejectKeySelector extends X509CertSelector {
1124
private final Set<PublicKey> badKeySet;
1125
1126
/**
1127
* Creates a new <code>RejectKeySelector</code>.
1128
*
1129
* @param badPublicKeys a <code>Set</code> of
1130
* <code>PublicKey</code>s that
1131
* should be rejected (or <code>null</code>
1132
* if no such check should be done)
1133
*/
1134
RejectKeySelector(Set<PublicKey> badPublicKeys) {
1135
this.badKeySet = badPublicKeys;
1136
}
1137
1138
/**
1139
* Decides whether a <code>Certificate</code> should be selected.
1140
*
1141
* @param cert the <code>Certificate</code> to be checked
1142
* @return <code>true</code> if the <code>Certificate</code> should be
1143
* selected, <code>false</code> otherwise
1144
*/
1145
@Override
1146
public boolean match(Certificate cert) {
1147
if (!super.match(cert))
1148
return(false);
1149
1150
if (badKeySet.contains(cert.getPublicKey())) {
1151
if (debug != null)
1152
debug.println("RejectKeySelector.match: bad key");
1153
return false;
1154
}
1155
1156
if (debug != null)
1157
debug.println("RejectKeySelector.match: returning true");
1158
return true;
1159
}
1160
1161
/**
1162
* Return a printable representation of the <code>CertSelector</code>.
1163
*
1164
* @return a <code>String</code> describing the contents of the
1165
* <code>CertSelector</code>
1166
*/
1167
@Override
1168
public String toString() {
1169
StringBuilder sb = new StringBuilder();
1170
sb.append("RejectKeySelector: [\n");
1171
sb.append(super.toString());
1172
sb.append(badKeySet);
1173
sb.append("]");
1174
return sb.toString();
1175
}
1176
}
1177
}
1178
1179