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/OCSPResponse.java
41161 views
1
/*
2
* Copyright (c) 2003, 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.*;
29
import java.security.*;
30
import java.security.cert.CertificateException;
31
import java.security.cert.CertificateParsingException;
32
import java.security.cert.CertPathValidatorException;
33
import java.security.cert.CertPathValidatorException.BasicReason;
34
import java.security.cert.CRLReason;
35
import java.security.cert.TrustAnchor;
36
import java.security.cert.X509Certificate;
37
import java.util.ArrayList;
38
import java.util.Arrays;
39
import java.util.Collections;
40
import java.util.Date;
41
import java.util.HashMap;
42
import java.util.List;
43
import java.util.Map;
44
import java.util.Set;
45
import javax.security.auth.x500.X500Principal;
46
47
import sun.security.util.HexDumpEncoder;
48
import sun.security.action.GetIntegerAction;
49
import sun.security.x509.*;
50
import sun.security.util.*;
51
52
/**
53
* This class is used to process an OCSP response.
54
* The OCSP Response is defined
55
* in RFC 2560 and the ASN.1 encoding is as follows:
56
* <pre>
57
*
58
* OCSPResponse ::= SEQUENCE {
59
* responseStatus OCSPResponseStatus,
60
* responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
61
*
62
* OCSPResponseStatus ::= ENUMERATED {
63
* successful (0), --Response has valid confirmations
64
* malformedRequest (1), --Illegal confirmation request
65
* internalError (2), --Internal error in issuer
66
* tryLater (3), --Try again later
67
* --(4) is not used
68
* sigRequired (5), --Must sign the request
69
* unauthorized (6) --Request unauthorized
70
* }
71
*
72
* ResponseBytes ::= SEQUENCE {
73
* responseType OBJECT IDENTIFIER,
74
* response OCTET STRING }
75
*
76
* BasicOCSPResponse ::= SEQUENCE {
77
* tbsResponseData ResponseData,
78
* signatureAlgorithm AlgorithmIdentifier,
79
* signature BIT STRING,
80
* certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
81
*
82
* The value for signature SHALL be computed on the hash of the DER
83
* encoding ResponseData.
84
*
85
* ResponseData ::= SEQUENCE {
86
* version [0] EXPLICIT Version DEFAULT v1,
87
* responderID ResponderID,
88
* producedAt GeneralizedTime,
89
* responses SEQUENCE OF SingleResponse,
90
* responseExtensions [1] EXPLICIT Extensions OPTIONAL }
91
*
92
* ResponderID ::= CHOICE {
93
* byName [1] Name,
94
* byKey [2] KeyHash }
95
*
96
* KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
97
* (excluding the tag and length fields)
98
*
99
* SingleResponse ::= SEQUENCE {
100
* certID CertID,
101
* certStatus CertStatus,
102
* thisUpdate GeneralizedTime,
103
* nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
104
* singleExtensions [1] EXPLICIT Extensions OPTIONAL }
105
*
106
* CertStatus ::= CHOICE {
107
* good [0] IMPLICIT NULL,
108
* revoked [1] IMPLICIT RevokedInfo,
109
* unknown [2] IMPLICIT UnknownInfo }
110
*
111
* RevokedInfo ::= SEQUENCE {
112
* revocationTime GeneralizedTime,
113
* revocationReason [0] EXPLICIT CRLReason OPTIONAL }
114
*
115
* UnknownInfo ::= NULL -- this can be replaced with an enumeration
116
*
117
* </pre>
118
*
119
* @author Ram Marti
120
*/
121
122
public final class OCSPResponse {
123
124
public enum ResponseStatus {
125
SUCCESSFUL, // Response has valid confirmations
126
MALFORMED_REQUEST, // Illegal request
127
INTERNAL_ERROR, // Internal error in responder
128
TRY_LATER, // Try again later
129
UNUSED, // is not used
130
SIG_REQUIRED, // Must sign the request
131
UNAUTHORIZED // Request unauthorized
132
};
133
private static final ResponseStatus[] rsvalues = ResponseStatus.values();
134
135
private static final Debug debug = Debug.getInstance("certpath");
136
private static final boolean dump = debug != null && Debug.isOn("ocsp");
137
private static final ObjectIdentifier OCSP_BASIC_RESPONSE_OID =
138
ObjectIdentifier.of(KnownOIDs.OCSPBasicResponse);
139
private static final int CERT_STATUS_GOOD = 0;
140
private static final int CERT_STATUS_REVOKED = 1;
141
private static final int CERT_STATUS_UNKNOWN = 2;
142
143
// ResponderID CHOICE tags
144
private static final int NAME_TAG = 1;
145
private static final int KEY_TAG = 2;
146
147
// Default maximum clock skew in milliseconds (15 minutes)
148
// allowed when checking validity of OCSP responses
149
private static final int DEFAULT_MAX_CLOCK_SKEW = 900000;
150
151
/**
152
* Integer value indicating the maximum allowable clock skew,
153
* in milliseconds, to be used for the OCSP check.
154
*/
155
private static final int MAX_CLOCK_SKEW = initializeClockSkew();
156
157
/**
158
* Initialize the maximum allowable clock skew by getting the OCSP
159
* clock skew system property. If the property has not been set, or if its
160
* value is negative, set the skew to the default.
161
*/
162
private static int initializeClockSkew() {
163
@SuppressWarnings("removal")
164
Integer tmp = java.security.AccessController.doPrivileged(
165
new GetIntegerAction("com.sun.security.ocsp.clockSkew"));
166
if (tmp == null || tmp < 0) {
167
return DEFAULT_MAX_CLOCK_SKEW;
168
}
169
// Convert to milliseconds, as the system property will be
170
// specified in seconds
171
return tmp * 1000;
172
}
173
174
// an array of all of the CRLReasons (used in SingleResponse)
175
private static final CRLReason[] values = CRLReason.values();
176
177
private final ResponseStatus responseStatus;
178
private final Map<CertId, SingleResponse> singleResponseMap;
179
private final AlgorithmId sigAlgId;
180
private final byte[] signature;
181
private final byte[] tbsResponseData;
182
private final byte[] responseNonce;
183
private List<X509CertImpl> certs;
184
private X509CertImpl signerCert = null;
185
private final ResponderId respId;
186
private Date producedAtDate = null;
187
private final Map<String, java.security.cert.Extension> responseExtensions;
188
189
/*
190
* Create an OCSP response from its ASN.1 DER encoding.
191
*
192
* @param bytes The DER-encoded bytes for an OCSP response
193
*/
194
public OCSPResponse(byte[] bytes) throws IOException {
195
if (dump) {
196
HexDumpEncoder hexEnc = new HexDumpEncoder();
197
debug.println("OCSPResponse bytes...\n\n" +
198
hexEnc.encode(bytes) + "\n");
199
}
200
DerValue der = new DerValue(bytes);
201
if (der.tag != DerValue.tag_Sequence) {
202
throw new IOException("Bad encoding in OCSP response: " +
203
"expected ASN.1 SEQUENCE tag.");
204
}
205
DerInputStream derIn = der.getData();
206
207
// responseStatus
208
int status = derIn.getEnumerated();
209
if (status >= 0 && status < rsvalues.length) {
210
responseStatus = rsvalues[status];
211
} else {
212
// unspecified responseStatus
213
throw new IOException("Unknown OCSPResponse status: " + status);
214
}
215
if (debug != null) {
216
debug.println("OCSP response status: " + responseStatus);
217
}
218
if (responseStatus != ResponseStatus.SUCCESSFUL) {
219
// no need to continue, responseBytes are not set.
220
singleResponseMap = Collections.emptyMap();
221
certs = new ArrayList<X509CertImpl>();
222
sigAlgId = null;
223
signature = null;
224
tbsResponseData = null;
225
responseNonce = null;
226
responseExtensions = Collections.emptyMap();
227
respId = null;
228
return;
229
}
230
231
// responseBytes
232
der = derIn.getDerValue();
233
if (!der.isContextSpecific((byte)0)) {
234
throw new IOException("Bad encoding in responseBytes element " +
235
"of OCSP response: expected ASN.1 context specific tag 0.");
236
}
237
DerValue tmp = der.data.getDerValue();
238
if (tmp.tag != DerValue.tag_Sequence) {
239
throw new IOException("Bad encoding in responseBytes element " +
240
"of OCSP response: expected ASN.1 SEQUENCE tag.");
241
}
242
243
// responseType
244
derIn = tmp.data;
245
ObjectIdentifier responseType = derIn.getOID();
246
if (responseType.equals((Object)OCSP_BASIC_RESPONSE_OID)) {
247
if (debug != null) {
248
debug.println("OCSP response type: basic");
249
}
250
} else {
251
if (debug != null) {
252
debug.println("OCSP response type: " + responseType);
253
}
254
throw new IOException("Unsupported OCSP response type: " +
255
responseType);
256
}
257
258
// BasicOCSPResponse
259
DerInputStream basicOCSPResponse =
260
new DerInputStream(derIn.getOctetString());
261
262
DerValue[] seqTmp = basicOCSPResponse.getSequence(3);
263
if (seqTmp.length < 3) {
264
throw new IOException("Unexpected BasicOCSPResponse value");
265
}
266
267
DerValue responseData = seqTmp[0];
268
269
// Need the DER encoded ResponseData to verify the signature later
270
tbsResponseData = seqTmp[0].toByteArray();
271
272
// tbsResponseData
273
if (responseData.tag != DerValue.tag_Sequence) {
274
throw new IOException("Bad encoding in tbsResponseData " +
275
"element of OCSP response: expected ASN.1 SEQUENCE tag.");
276
}
277
DerInputStream seqDerIn = responseData.data;
278
DerValue seq = seqDerIn.getDerValue();
279
280
// version
281
if (seq.isContextSpecific((byte)0)) {
282
// seq[0] is version
283
if (seq.isConstructed() && seq.isContextSpecific()) {
284
//System.out.println ("version is available");
285
seq = seq.data.getDerValue();
286
int version = seq.getInteger();
287
if (seq.data.available() != 0) {
288
throw new IOException("Bad encoding in version " +
289
" element of OCSP response: bad format");
290
}
291
seq = seqDerIn.getDerValue();
292
}
293
}
294
295
// responderID
296
respId = new ResponderId(seq.toByteArray());
297
if (debug != null) {
298
debug.println("Responder ID: " + respId);
299
}
300
301
// producedAt
302
seq = seqDerIn.getDerValue();
303
producedAtDate = seq.getGeneralizedTime();
304
if (debug != null) {
305
debug.println("OCSP response produced at: " + producedAtDate);
306
}
307
308
// responses
309
DerValue[] singleResponseDer = seqDerIn.getSequence(1);
310
singleResponseMap = new HashMap<>(singleResponseDer.length);
311
if (debug != null) {
312
debug.println("OCSP number of SingleResponses: "
313
+ singleResponseDer.length);
314
}
315
for (DerValue srDer : singleResponseDer) {
316
SingleResponse singleResponse = new SingleResponse(srDer);
317
singleResponseMap.put(singleResponse.getCertId(), singleResponse);
318
}
319
320
// responseExtensions
321
Map<String, java.security.cert.Extension> tmpExtMap = new HashMap<>();
322
if (seqDerIn.available() > 0) {
323
seq = seqDerIn.getDerValue();
324
if (seq.isContextSpecific((byte)1)) {
325
tmpExtMap = parseExtensions(seq);
326
}
327
}
328
responseExtensions = tmpExtMap;
329
330
// Attach the nonce value if found in the extension map
331
Extension nonceExt = (Extension)tmpExtMap.get(
332
PKIXExtensions.OCSPNonce_Id.toString());
333
responseNonce = (nonceExt != null) ?
334
nonceExt.getExtensionValue() : null;
335
if (debug != null && responseNonce != null) {
336
debug.println("Response nonce: " + Arrays.toString(responseNonce));
337
}
338
339
// signatureAlgorithmId
340
sigAlgId = AlgorithmId.parse(seqTmp[1]);
341
342
// signature
343
signature = seqTmp[2].getBitString();
344
345
// if seq[3] is available , then it is a sequence of certificates
346
if (seqTmp.length > 3) {
347
// certs are available
348
DerValue seqCert = seqTmp[3];
349
if (!seqCert.isContextSpecific((byte)0)) {
350
throw new IOException("Bad encoding in certs element of " +
351
"OCSP response: expected ASN.1 context specific tag 0.");
352
}
353
DerValue[] derCerts = seqCert.getData().getSequence(3);
354
certs = new ArrayList<X509CertImpl>(derCerts.length);
355
try {
356
for (int i = 0; i < derCerts.length; i++) {
357
X509CertImpl cert =
358
new X509CertImpl(derCerts[i].toByteArray());
359
certs.add(cert);
360
361
if (debug != null) {
362
debug.println("OCSP response cert #" + (i + 1) + ": " +
363
cert.getSubjectX500Principal());
364
}
365
}
366
} catch (CertificateException ce) {
367
throw new IOException("Bad encoding in X509 Certificate", ce);
368
}
369
} else {
370
certs = new ArrayList<X509CertImpl>();
371
}
372
}
373
374
void verify(List<CertId> certIds, IssuerInfo issuerInfo,
375
X509Certificate responderCert, Date date, byte[] nonce,
376
String variant)
377
throws CertPathValidatorException
378
{
379
switch (responseStatus) {
380
case SUCCESSFUL:
381
break;
382
case TRY_LATER:
383
case INTERNAL_ERROR:
384
throw new CertPathValidatorException(
385
"OCSP response error: " + responseStatus, null, null, -1,
386
BasicReason.UNDETERMINED_REVOCATION_STATUS);
387
case UNAUTHORIZED:
388
default:
389
throw new CertPathValidatorException("OCSP response error: " +
390
responseStatus);
391
}
392
393
// Check that the response includes a response for all of the
394
// certs that were supplied in the request
395
for (CertId certId : certIds) {
396
SingleResponse sr = getSingleResponse(certId);
397
if (sr == null) {
398
if (debug != null) {
399
debug.println("No response found for CertId: " + certId);
400
}
401
throw new CertPathValidatorException(
402
"OCSP response does not include a response for a " +
403
"certificate supplied in the OCSP request");
404
}
405
if (debug != null) {
406
debug.println("Status of certificate (with serial number " +
407
certId.getSerialNumber() + ") is: " + sr.getCertStatus());
408
}
409
}
410
411
// Locate the signer cert
412
if (signerCert == null) {
413
// Add the Issuing CA cert and/or Trusted Responder cert to the list
414
// of certs from the OCSP response
415
try {
416
if (issuerInfo.getCertificate() != null) {
417
certs.add(X509CertImpl.toImpl(issuerInfo.getCertificate()));
418
}
419
if (responderCert != null) {
420
certs.add(X509CertImpl.toImpl(responderCert));
421
}
422
} catch (CertificateException ce) {
423
throw new CertPathValidatorException(
424
"Invalid issuer or trusted responder certificate", ce);
425
}
426
427
if (respId.getType() == ResponderId.Type.BY_NAME) {
428
X500Principal rName = respId.getResponderName();
429
for (X509CertImpl cert : certs) {
430
if (cert.getSubjectX500Principal().equals(rName)) {
431
signerCert = cert;
432
break;
433
}
434
}
435
} else if (respId.getType() == ResponderId.Type.BY_KEY) {
436
KeyIdentifier ridKeyId = respId.getKeyIdentifier();
437
for (X509CertImpl cert : certs) {
438
// Match responder's key identifier against the cert's SKID
439
// This will match if the SKID is encoded using the 160-bit
440
// SHA-1 hash method as defined in RFC 5280.
441
KeyIdentifier certKeyId = cert.getSubjectKeyId();
442
if (certKeyId != null && ridKeyId.equals(certKeyId)) {
443
signerCert = cert;
444
break;
445
} else {
446
// The certificate does not have a SKID or may have
447
// been using a different algorithm (ex: see RFC 7093).
448
// Check if the responder's key identifier matches
449
// against a newly generated key identifier of the
450
// cert's public key using the 160-bit SHA-1 method.
451
try {
452
certKeyId = new KeyIdentifier(cert.getPublicKey());
453
} catch (IOException e) {
454
// ignore
455
}
456
if (ridKeyId.equals(certKeyId)) {
457
signerCert = cert;
458
break;
459
}
460
}
461
}
462
}
463
}
464
465
// Check whether the signer cert returned by the responder is trusted
466
boolean signedByTrustedResponder = false;
467
if (signerCert != null) {
468
// Check if the response is signed by the issuing CA
469
if (signerCert.getSubjectX500Principal().equals(
470
issuerInfo.getName()) &&
471
signerCert.getPublicKey().equals(
472
issuerInfo.getPublicKey())) {
473
if (debug != null) {
474
debug.println("OCSP response is signed by the target's " +
475
"Issuing CA");
476
}
477
// cert is trusted, now verify the signed response
478
479
// Check if the response is signed by a trusted responder
480
} else if (signerCert.equals(responderCert)) {
481
signedByTrustedResponder = true;
482
if (debug != null) {
483
debug.println("OCSP response is signed by a Trusted " +
484
"Responder");
485
}
486
// cert is trusted, now verify the signed response
487
488
// Check if the response is signed by an authorized responder
489
} else if (signerCert.getIssuerX500Principal().equals(
490
issuerInfo.getName())) {
491
492
// Check for the OCSPSigning key purpose
493
try {
494
List<String> keyPurposes = signerCert.getExtendedKeyUsage();
495
if (keyPurposes == null ||
496
!keyPurposes.contains(KnownOIDs.OCSPSigning.value())) {
497
throw new CertPathValidatorException(
498
"Responder's certificate not valid for signing " +
499
"OCSP responses");
500
}
501
} catch (CertificateParsingException cpe) {
502
// assume cert is not valid for signing
503
throw new CertPathValidatorException(
504
"Responder's certificate not valid for signing " +
505
"OCSP responses", cpe);
506
}
507
508
// Check algorithm constraints specified in security property
509
// "jdk.certpath.disabledAlgorithms".
510
AlgorithmChecker algChecker =
511
new AlgorithmChecker(issuerInfo.getAnchor(), date,
512
variant);
513
algChecker.init(false);
514
algChecker.check(signerCert, Collections.<String>emptySet());
515
516
// check the validity
517
try {
518
if (date == null) {
519
signerCert.checkValidity();
520
} else {
521
signerCert.checkValidity(date);
522
}
523
} catch (CertificateException e) {
524
throw new CertPathValidatorException(
525
"Responder's certificate not within the " +
526
"validity period", e);
527
}
528
529
// check for revocation
530
//
531
// A CA may specify that an OCSP client can trust a
532
// responder for the lifetime of the responder's
533
// certificate. The CA does so by including the
534
// extension id-pkix-ocsp-nocheck.
535
//
536
Extension noCheck =
537
signerCert.getExtension(PKIXExtensions.OCSPNoCheck_Id);
538
if (noCheck != null) {
539
if (debug != null) {
540
debug.println("Responder's certificate includes " +
541
"the extension id-pkix-ocsp-nocheck.");
542
}
543
} else {
544
// we should do the revocation checking of the
545
// authorized responder in a future update.
546
}
547
548
// verify the signature
549
try {
550
signerCert.verify(issuerInfo.getPublicKey());
551
if (debug != null) {
552
debug.println("OCSP response is signed by an " +
553
"Authorized Responder");
554
}
555
// cert is trusted, now verify the signed response
556
557
} catch (GeneralSecurityException e) {
558
signerCert = null;
559
}
560
} else {
561
throw new CertPathValidatorException(
562
"Responder's certificate is not authorized to sign " +
563
"OCSP responses");
564
}
565
}
566
567
// Confirm that the signed response was generated using the public
568
// key from the trusted responder cert
569
if (signerCert != null) {
570
// Check algorithm constraints specified in security property
571
// "jdk.certpath.disabledAlgorithms".
572
AlgorithmChecker.check(signerCert.getPublicKey(), sigAlgId, variant,
573
signedByTrustedResponder
574
? new TrustAnchor(responderCert, null)
575
: issuerInfo.getAnchor());
576
577
if (!verifySignature(signerCert)) {
578
throw new CertPathValidatorException(
579
"Error verifying OCSP Response's signature");
580
}
581
} else {
582
// Need responder's cert in order to verify the signature
583
throw new CertPathValidatorException(
584
"Unable to verify OCSP Response's signature");
585
}
586
587
if (nonce != null) {
588
if (responseNonce != null && !Arrays.equals(nonce, responseNonce)) {
589
throw new CertPathValidatorException("Nonces don't match");
590
}
591
}
592
593
// Check freshness of OCSPResponse
594
long now = (date == null) ? System.currentTimeMillis() : date.getTime();
595
Date nowPlusSkew = new Date(now + MAX_CLOCK_SKEW);
596
Date nowMinusSkew = new Date(now - MAX_CLOCK_SKEW);
597
for (SingleResponse sr : singleResponseMap.values()) {
598
if (debug != null) {
599
String until = "";
600
if (sr.nextUpdate != null) {
601
until = " until " + sr.nextUpdate;
602
}
603
debug.println("OCSP response validity interval is from " +
604
sr.thisUpdate + until);
605
debug.println("Checking validity of OCSP response on " +
606
new Date(now) + " with allowed interval between " +
607
nowMinusSkew + " and " + nowPlusSkew);
608
}
609
610
// Check that the test date is within the validity interval:
611
// [ thisUpdate - MAX_CLOCK_SKEW,
612
// MAX(thisUpdate, nextUpdate) + MAX_CLOCK_SKEW ]
613
if (nowPlusSkew.before(sr.thisUpdate) ||
614
nowMinusSkew.after(
615
sr.nextUpdate != null ? sr.nextUpdate : sr.thisUpdate))
616
{
617
throw new CertPathValidatorException(
618
"Response is unreliable: its validity " +
619
"interval is out-of-date");
620
}
621
}
622
}
623
624
/**
625
* Returns the OCSP ResponseStatus.
626
*
627
* @return the {@code ResponseStatus} for this OCSP response
628
*/
629
public ResponseStatus getResponseStatus() {
630
return responseStatus;
631
}
632
633
/*
634
* Verify the signature of the OCSP response.
635
*/
636
private boolean verifySignature(X509Certificate cert)
637
throws CertPathValidatorException {
638
639
try {
640
Signature respSignature = Signature.getInstance(sigAlgId.getName());
641
respSignature.initVerify(cert.getPublicKey());
642
respSignature.update(tbsResponseData);
643
644
if (respSignature.verify(signature)) {
645
if (debug != null) {
646
debug.println("Verified signature of OCSP Response");
647
}
648
return true;
649
650
} else {
651
if (debug != null) {
652
debug.println(
653
"Error verifying signature of OCSP Response");
654
}
655
return false;
656
}
657
} catch (InvalidKeyException | NoSuchAlgorithmException |
658
SignatureException e)
659
{
660
throw new CertPathValidatorException(e);
661
}
662
}
663
664
/**
665
* Returns the SingleResponse of the specified CertId, or null if
666
* there is no response for that CertId.
667
*
668
* @param certId the {@code CertId} for a {@code SingleResponse} to be
669
* searched for in the OCSP response.
670
*
671
* @return the {@code SingleResponse} for the provided {@code CertId},
672
* or {@code null} if it is not found.
673
*/
674
public SingleResponse getSingleResponse(CertId certId) {
675
return singleResponseMap.get(certId);
676
}
677
678
/**
679
* Return a set of all CertIds in this {@code OCSPResponse}
680
*
681
* @return an unmodifiable set containing every {@code CertId} in this
682
* response.
683
*/
684
public Set<CertId> getCertIds() {
685
return Collections.unmodifiableSet(singleResponseMap.keySet());
686
}
687
688
/*
689
* Returns the certificate for the authority that signed the OCSP response.
690
*/
691
X509Certificate getSignerCertificate() {
692
return signerCert; // set in verify()
693
}
694
695
/**
696
* Get the {@code ResponderId} from this {@code OCSPResponse}
697
*
698
* @return the {@code ResponderId} from this response or {@code null}
699
* if no responder ID is in the body of the response (e.g. a
700
* response with a status other than SUCCESS.
701
*/
702
public ResponderId getResponderId() {
703
return respId;
704
}
705
706
/**
707
* Provide a String representation of an OCSPResponse
708
*
709
* @return a human-readable representation of the OCSPResponse
710
*/
711
@Override
712
public String toString() {
713
StringBuilder sb = new StringBuilder();
714
sb.append("OCSP Response:\n");
715
sb.append("Response Status: ").append(responseStatus).append("\n");
716
sb.append("Responder ID: ").append(respId).append("\n");
717
sb.append("Produced at: ").append(producedAtDate).append("\n");
718
int count = singleResponseMap.size();
719
sb.append(count).append(count == 1 ?
720
" response:\n" : " responses:\n");
721
for (SingleResponse sr : singleResponseMap.values()) {
722
sb.append(sr).append("\n");
723
}
724
if (responseExtensions != null && responseExtensions.size() > 0) {
725
count = responseExtensions.size();
726
sb.append(count).append(count == 1 ?
727
" extension:\n" : " extensions:\n");
728
for (String extId : responseExtensions.keySet()) {
729
sb.append(responseExtensions.get(extId)).append("\n");
730
}
731
}
732
733
return sb.toString();
734
}
735
736
/**
737
* Build a String-Extension map from DER encoded data.
738
* @param derVal A {@code DerValue} object built from a SEQUENCE of
739
* extensions
740
*
741
* @return a {@code Map} using the OID in string form as the keys. If no
742
* extensions are found or an empty SEQUENCE is passed in, then
743
* an empty {@code Map} will be returned.
744
*
745
* @throws IOException if any decoding errors occur.
746
*/
747
private static Map<String, java.security.cert.Extension>
748
parseExtensions(DerValue derVal) throws IOException {
749
DerValue[] extDer = derVal.data.getSequence(3);
750
Map<String, java.security.cert.Extension> extMap =
751
new HashMap<>(extDer.length);
752
753
for (DerValue extDerVal : extDer) {
754
Extension ext = new Extension(extDerVal);
755
if (debug != null) {
756
debug.println("Extension: " + ext);
757
}
758
// We don't support any extensions yet. Therefore, if it
759
// is critical we must throw an exception because we
760
// don't know how to process it.
761
if (ext.isCritical()) {
762
throw new IOException("Unsupported OCSP critical extension: " +
763
ext.getExtensionId());
764
}
765
extMap.put(ext.getId(), ext);
766
}
767
768
return extMap;
769
}
770
771
/*
772
* A class representing a single OCSP response.
773
*/
774
public static final class SingleResponse implements OCSP.RevocationStatus {
775
private final CertId certId;
776
private final CertStatus certStatus;
777
private final Date thisUpdate;
778
private final Date nextUpdate;
779
private final Date revocationTime;
780
private final CRLReason revocationReason;
781
private final Map<String, java.security.cert.Extension> singleExtensions;
782
783
private SingleResponse(DerValue der) throws IOException {
784
if (der.tag != DerValue.tag_Sequence) {
785
throw new IOException("Bad ASN.1 encoding in SingleResponse");
786
}
787
DerInputStream tmp = der.data;
788
789
certId = new CertId(tmp.getDerValue().data);
790
DerValue derVal = tmp.getDerValue();
791
short tag = (byte)(derVal.tag & 0x1f);
792
if (tag == CERT_STATUS_REVOKED) {
793
certStatus = CertStatus.REVOKED;
794
revocationTime = derVal.data.getGeneralizedTime();
795
if (derVal.data.available() != 0) {
796
DerValue dv = derVal.data.getDerValue();
797
tag = (byte)(dv.tag & 0x1f);
798
if (tag == 0) {
799
int reason = dv.data.getEnumerated();
800
// if reason out-of-range just leave as UNSPECIFIED
801
if (reason >= 0 && reason < values.length) {
802
revocationReason = values[reason];
803
} else {
804
revocationReason = CRLReason.UNSPECIFIED;
805
}
806
} else {
807
revocationReason = CRLReason.UNSPECIFIED;
808
}
809
} else {
810
revocationReason = CRLReason.UNSPECIFIED;
811
}
812
// RevokedInfo
813
if (debug != null) {
814
debug.println("Revocation time: " + revocationTime);
815
debug.println("Revocation reason: " + revocationReason);
816
}
817
} else {
818
revocationTime = null;
819
revocationReason = null;
820
if (tag == CERT_STATUS_GOOD) {
821
certStatus = CertStatus.GOOD;
822
} else if (tag == CERT_STATUS_UNKNOWN) {
823
certStatus = CertStatus.UNKNOWN;
824
} else {
825
throw new IOException("Invalid certificate status");
826
}
827
}
828
829
thisUpdate = tmp.getGeneralizedTime();
830
if (debug != null) {
831
debug.println("thisUpdate: " + thisUpdate);
832
}
833
834
// Parse optional fields like nextUpdate and singleExtensions
835
Date tmpNextUpdate = null;
836
Map<String, java.security.cert.Extension> tmpMap = null;
837
838
// Check for the first optional item, it could be nextUpdate
839
// [CONTEXT 0] or singleExtensions [CONTEXT 1]
840
if (tmp.available() > 0) {
841
derVal = tmp.getDerValue();
842
843
// nextUpdate processing
844
if (derVal.isContextSpecific((byte)0)) {
845
tmpNextUpdate = derVal.data.getGeneralizedTime();
846
if (debug != null) {
847
debug.println("nextUpdate: " + tmpNextUpdate);
848
}
849
850
// If more data exists in the singleResponse, it
851
// can only be singleExtensions. Get this DER value
852
// for processing in the next block
853
derVal = tmp.available() > 0 ? tmp.getDerValue() : null;
854
}
855
856
// singleExtensions processing
857
if (derVal != null) {
858
if (derVal.isContextSpecific((byte)1)) {
859
tmpMap = parseExtensions(derVal);
860
861
// There should not be any other items in the
862
// singleResponse at this point.
863
if (tmp.available() > 0) {
864
throw new IOException(tmp.available() +
865
" bytes of additional data in singleResponse");
866
}
867
} else {
868
// Unknown item in the singleResponse
869
throw new IOException("Unsupported singleResponse " +
870
"item, tag = " + String.format("%02X", derVal.tag));
871
}
872
}
873
}
874
875
nextUpdate = tmpNextUpdate;
876
singleExtensions = (tmpMap != null) ? tmpMap :
877
Collections.emptyMap();
878
if (debug != null) {
879
for (java.security.cert.Extension ext :
880
singleExtensions.values()) {
881
debug.println("singleExtension: " + ext);
882
}
883
}
884
}
885
886
/*
887
* Return the certificate's revocation status code
888
*/
889
@Override
890
public CertStatus getCertStatus() {
891
return certStatus;
892
}
893
894
/**
895
* Get the Cert ID that this SingleResponse is for.
896
*
897
* @return the {@code CertId} for this {@code SingleResponse}
898
*/
899
public CertId getCertId() {
900
return certId;
901
}
902
903
/**
904
* Get the {@code thisUpdate} field from this {@code SingleResponse}.
905
*
906
* @return a {@link Date} object containing the thisUpdate date
907
*/
908
public Date getThisUpdate() {
909
return (thisUpdate != null ? (Date) thisUpdate.clone() : null);
910
}
911
912
/**
913
* Get the {@code nextUpdate} field from this {@code SingleResponse}.
914
*
915
* @return a {@link Date} object containing the nexUpdate date or
916
* {@code null} if a nextUpdate field is not present in the response.
917
*/
918
public Date getNextUpdate() {
919
return (nextUpdate != null ? (Date) nextUpdate.clone() : null);
920
}
921
922
/**
923
* Get the {@code revocationTime} field from this
924
* {@code SingleResponse}.
925
*
926
* @return a {@link Date} object containing the revocationTime date or
927
* {@code null} if the {@code SingleResponse} does not have a status
928
* of {@code REVOKED}.
929
*/
930
@Override
931
public Date getRevocationTime() {
932
return (revocationTime != null ? (Date) revocationTime.clone() :
933
null);
934
}
935
936
/**
937
* Get the {@code revocationReason} field for the
938
* {@code SingleResponse}.
939
*
940
* @return a {@link CRLReason} containing the revocation reason, or
941
* {@code null} if a revocation reason was not provided or the
942
* response status is not {@code REVOKED}.
943
*/
944
@Override
945
public CRLReason getRevocationReason() {
946
return revocationReason;
947
}
948
949
/**
950
* Get the {@code singleExtensions} for this {@code SingleResponse}.
951
*
952
* @return a {@link Map} of {@link Extension} objects, keyed by
953
* their OID value in string form.
954
*/
955
@Override
956
public Map<String, java.security.cert.Extension> getSingleExtensions() {
957
return Collections.unmodifiableMap(singleExtensions);
958
}
959
960
/**
961
* Construct a string representation of a single OCSP response.
962
*/
963
@Override public String toString() {
964
StringBuilder sb = new StringBuilder();
965
sb.append("SingleResponse:\n");
966
sb.append(certId);
967
sb.append("\nCertStatus: ").append(certStatus).append("\n");
968
if (certStatus == CertStatus.REVOKED) {
969
sb.append("revocationTime is ");
970
sb.append(revocationTime).append("\n");
971
sb.append("revocationReason is ");
972
sb.append(revocationReason).append("\n");
973
}
974
sb.append("thisUpdate is ").append(thisUpdate).append("\n");
975
if (nextUpdate != null) {
976
sb.append("nextUpdate is ").append(nextUpdate).append("\n");
977
}
978
for (java.security.cert.Extension ext : singleExtensions.values()) {
979
sb.append("singleExtension: ");
980
sb.append(ext.toString()).append("\n");
981
}
982
return sb.toString();
983
}
984
}
985
986
/**
987
* Helper class that allows consumers to pass in issuer information. This
988
* will always consist of the issuer's name and public key, but may also
989
* contain a certificate if the originating data is in that form. The
990
* trust anchor for the certificate chain will be included for certpath
991
* disabled algorithm checking.
992
*/
993
static final class IssuerInfo {
994
private final TrustAnchor anchor;
995
private final X509Certificate certificate;
996
private final X500Principal name;
997
private final PublicKey pubKey;
998
999
IssuerInfo(TrustAnchor anchor) {
1000
this(anchor, (anchor != null) ? anchor.getTrustedCert() : null);
1001
}
1002
1003
IssuerInfo(X509Certificate issuerCert) {
1004
this(null, issuerCert);
1005
}
1006
1007
IssuerInfo(TrustAnchor anchor, X509Certificate issuerCert) {
1008
if (anchor == null && issuerCert == null) {
1009
throw new NullPointerException("TrustAnchor and issuerCert " +
1010
"cannot be null");
1011
}
1012
this.anchor = anchor;
1013
if (issuerCert != null) {
1014
name = issuerCert.getSubjectX500Principal();
1015
pubKey = issuerCert.getPublicKey();
1016
certificate = issuerCert;
1017
} else {
1018
name = anchor.getCA();
1019
pubKey = anchor.getCAPublicKey();
1020
certificate = anchor.getTrustedCert();
1021
}
1022
}
1023
1024
/**
1025
* Get the certificate in this IssuerInfo if present.
1026
*
1027
* @return the {@code X509Certificate} used to create this IssuerInfo
1028
* object, or {@code null} if a certificate was not used in its
1029
* creation.
1030
*/
1031
X509Certificate getCertificate() {
1032
return certificate;
1033
}
1034
1035
/**
1036
* Get the name of this issuer.
1037
*
1038
* @return an {@code X500Principal} corresponding to this issuer's
1039
* name. If derived from an issuer's {@code X509Certificate} this
1040
* would be equivalent to the certificate subject name.
1041
*/
1042
X500Principal getName() {
1043
return name;
1044
}
1045
1046
/**
1047
* Get the public key for this issuer.
1048
*
1049
* @return a {@code PublicKey} for this issuer.
1050
*/
1051
PublicKey getPublicKey() {
1052
return pubKey;
1053
}
1054
1055
/**
1056
* Get the TrustAnchor for the certificate chain.
1057
*
1058
* @return a {@code TrustAnchor}.
1059
*/
1060
TrustAnchor getAnchor() {
1061
return anchor;
1062
}
1063
1064
/**
1065
* Create a string representation of this IssuerInfo.
1066
*
1067
* @return a {@code String} form of this IssuerInfo object.
1068
*/
1069
@Override
1070
public String toString() {
1071
StringBuilder sb = new StringBuilder();
1072
sb.append("Issuer Info:\n");
1073
sb.append("Name: ").append(name.toString()).append("\n");
1074
sb.append("Public Key:\n").append(pubKey.toString()).append("\n");
1075
return sb.toString();
1076
}
1077
}
1078
}
1079
1080