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