Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/jdk17u
Path: blob/master/src/java.base/share/classes/sun/security/provider/certpath/OCSPResponse.java
67848 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
SignatureUtil.initVerifyWithParam(respSignature,
642
cert.getPublicKey(),
643
SignatureUtil.getParamSpec(sigAlgId.getName(),
644
sigAlgId.getEncodedParams()));
645
respSignature.update(tbsResponseData);
646
647
if (respSignature.verify(signature)) {
648
if (debug != null) {
649
debug.println("Verified signature of OCSP Response");
650
}
651
return true;
652
653
} else {
654
if (debug != null) {
655
debug.println(
656
"Error verifying signature of OCSP Response");
657
}
658
return false;
659
}
660
} catch (InvalidAlgorithmParameterException | InvalidKeyException
661
| NoSuchAlgorithmException | SignatureException e)
662
{
663
throw new CertPathValidatorException(e);
664
}
665
}
666
667
/**
668
* Returns the SingleResponse of the specified CertId, or null if
669
* there is no response for that CertId.
670
*
671
* @param certId the {@code CertId} for a {@code SingleResponse} to be
672
* searched for in the OCSP response.
673
*
674
* @return the {@code SingleResponse} for the provided {@code CertId},
675
* or {@code null} if it is not found.
676
*/
677
public SingleResponse getSingleResponse(CertId certId) {
678
return singleResponseMap.get(certId);
679
}
680
681
/**
682
* Return a set of all CertIds in this {@code OCSPResponse}
683
*
684
* @return an unmodifiable set containing every {@code CertId} in this
685
* response.
686
*/
687
public Set<CertId> getCertIds() {
688
return Collections.unmodifiableSet(singleResponseMap.keySet());
689
}
690
691
/*
692
* Returns the certificate for the authority that signed the OCSP response.
693
*/
694
X509Certificate getSignerCertificate() {
695
return signerCert; // set in verify()
696
}
697
698
/**
699
* Get the {@code ResponderId} from this {@code OCSPResponse}
700
*
701
* @return the {@code ResponderId} from this response or {@code null}
702
* if no responder ID is in the body of the response (e.g. a
703
* response with a status other than SUCCESS.
704
*/
705
public ResponderId getResponderId() {
706
return respId;
707
}
708
709
/**
710
* Provide a String representation of an OCSPResponse
711
*
712
* @return a human-readable representation of the OCSPResponse
713
*/
714
@Override
715
public String toString() {
716
StringBuilder sb = new StringBuilder();
717
sb.append("OCSP Response:\n");
718
sb.append("Response Status: ").append(responseStatus).append("\n");
719
sb.append("Responder ID: ").append(respId).append("\n");
720
sb.append("Produced at: ").append(producedAtDate).append("\n");
721
int count = singleResponseMap.size();
722
sb.append(count).append(count == 1 ?
723
" response:\n" : " responses:\n");
724
for (SingleResponse sr : singleResponseMap.values()) {
725
sb.append(sr).append("\n");
726
}
727
if (responseExtensions != null && responseExtensions.size() > 0) {
728
count = responseExtensions.size();
729
sb.append(count).append(count == 1 ?
730
" extension:\n" : " extensions:\n");
731
for (String extId : responseExtensions.keySet()) {
732
sb.append(responseExtensions.get(extId)).append("\n");
733
}
734
}
735
736
return sb.toString();
737
}
738
739
/**
740
* Build a String-Extension map from DER encoded data.
741
* @param derVal A {@code DerValue} object built from a SEQUENCE of
742
* extensions
743
*
744
* @return a {@code Map} using the OID in string form as the keys. If no
745
* extensions are found or an empty SEQUENCE is passed in, then
746
* an empty {@code Map} will be returned.
747
*
748
* @throws IOException if any decoding errors occur.
749
*/
750
private static Map<String, java.security.cert.Extension>
751
parseExtensions(DerValue derVal) throws IOException {
752
DerValue[] extDer = derVal.data.getSequence(3);
753
Map<String, java.security.cert.Extension> extMap =
754
new HashMap<>(extDer.length);
755
756
for (DerValue extDerVal : extDer) {
757
Extension ext = new Extension(extDerVal);
758
if (debug != null) {
759
debug.println("Extension: " + ext);
760
}
761
// We don't support any extensions yet. Therefore, if it
762
// is critical we must throw an exception because we
763
// don't know how to process it.
764
if (ext.isCritical()) {
765
throw new IOException("Unsupported OCSP critical extension: " +
766
ext.getExtensionId());
767
}
768
extMap.put(ext.getId(), ext);
769
}
770
771
return extMap;
772
}
773
774
/*
775
* A class representing a single OCSP response.
776
*/
777
public static final class SingleResponse implements OCSP.RevocationStatus {
778
private final CertId certId;
779
private final CertStatus certStatus;
780
private final Date thisUpdate;
781
private final Date nextUpdate;
782
private final Date revocationTime;
783
private final CRLReason revocationReason;
784
private final Map<String, java.security.cert.Extension> singleExtensions;
785
786
private SingleResponse(DerValue der) throws IOException {
787
if (der.tag != DerValue.tag_Sequence) {
788
throw new IOException("Bad ASN.1 encoding in SingleResponse");
789
}
790
DerInputStream tmp = der.data;
791
792
certId = new CertId(tmp.getDerValue().data);
793
DerValue derVal = tmp.getDerValue();
794
short tag = (byte)(derVal.tag & 0x1f);
795
if (tag == CERT_STATUS_REVOKED) {
796
certStatus = CertStatus.REVOKED;
797
revocationTime = derVal.data.getGeneralizedTime();
798
if (derVal.data.available() != 0) {
799
DerValue dv = derVal.data.getDerValue();
800
tag = (byte)(dv.tag & 0x1f);
801
if (tag == 0) {
802
int reason = dv.data.getEnumerated();
803
// if reason out-of-range just leave as UNSPECIFIED
804
if (reason >= 0 && reason < values.length) {
805
revocationReason = values[reason];
806
} else {
807
revocationReason = CRLReason.UNSPECIFIED;
808
}
809
} else {
810
revocationReason = CRLReason.UNSPECIFIED;
811
}
812
} else {
813
revocationReason = CRLReason.UNSPECIFIED;
814
}
815
// RevokedInfo
816
if (debug != null) {
817
debug.println("Revocation time: " + revocationTime);
818
debug.println("Revocation reason: " + revocationReason);
819
}
820
} else {
821
revocationTime = null;
822
revocationReason = null;
823
if (tag == CERT_STATUS_GOOD) {
824
certStatus = CertStatus.GOOD;
825
} else if (tag == CERT_STATUS_UNKNOWN) {
826
certStatus = CertStatus.UNKNOWN;
827
} else {
828
throw new IOException("Invalid certificate status");
829
}
830
}
831
832
thisUpdate = tmp.getGeneralizedTime();
833
if (debug != null) {
834
debug.println("thisUpdate: " + thisUpdate);
835
}
836
837
// Parse optional fields like nextUpdate and singleExtensions
838
Date tmpNextUpdate = null;
839
Map<String, java.security.cert.Extension> tmpMap = null;
840
841
// Check for the first optional item, it could be nextUpdate
842
// [CONTEXT 0] or singleExtensions [CONTEXT 1]
843
if (tmp.available() > 0) {
844
derVal = tmp.getDerValue();
845
846
// nextUpdate processing
847
if (derVal.isContextSpecific((byte)0)) {
848
tmpNextUpdate = derVal.data.getGeneralizedTime();
849
if (debug != null) {
850
debug.println("nextUpdate: " + tmpNextUpdate);
851
}
852
853
// If more data exists in the singleResponse, it
854
// can only be singleExtensions. Get this DER value
855
// for processing in the next block
856
derVal = tmp.available() > 0 ? tmp.getDerValue() : null;
857
}
858
859
// singleExtensions processing
860
if (derVal != null) {
861
if (derVal.isContextSpecific((byte)1)) {
862
tmpMap = parseExtensions(derVal);
863
864
// There should not be any other items in the
865
// singleResponse at this point.
866
if (tmp.available() > 0) {
867
throw new IOException(tmp.available() +
868
" bytes of additional data in singleResponse");
869
}
870
} else {
871
// Unknown item in the singleResponse
872
throw new IOException("Unsupported singleResponse " +
873
"item, tag = " + String.format("%02X", derVal.tag));
874
}
875
}
876
}
877
878
nextUpdate = tmpNextUpdate;
879
singleExtensions = (tmpMap != null) ? tmpMap :
880
Collections.emptyMap();
881
if (debug != null) {
882
for (java.security.cert.Extension ext :
883
singleExtensions.values()) {
884
debug.println("singleExtension: " + ext);
885
}
886
}
887
}
888
889
/*
890
* Return the certificate's revocation status code
891
*/
892
@Override
893
public CertStatus getCertStatus() {
894
return certStatus;
895
}
896
897
/**
898
* Get the Cert ID that this SingleResponse is for.
899
*
900
* @return the {@code CertId} for this {@code SingleResponse}
901
*/
902
public CertId getCertId() {
903
return certId;
904
}
905
906
/**
907
* Get the {@code thisUpdate} field from this {@code SingleResponse}.
908
*
909
* @return a {@link Date} object containing the thisUpdate date
910
*/
911
public Date getThisUpdate() {
912
return (thisUpdate != null ? (Date) thisUpdate.clone() : null);
913
}
914
915
/**
916
* Get the {@code nextUpdate} field from this {@code SingleResponse}.
917
*
918
* @return a {@link Date} object containing the nexUpdate date or
919
* {@code null} if a nextUpdate field is not present in the response.
920
*/
921
public Date getNextUpdate() {
922
return (nextUpdate != null ? (Date) nextUpdate.clone() : null);
923
}
924
925
/**
926
* Get the {@code revocationTime} field from this
927
* {@code SingleResponse}.
928
*
929
* @return a {@link Date} object containing the revocationTime date or
930
* {@code null} if the {@code SingleResponse} does not have a status
931
* of {@code REVOKED}.
932
*/
933
@Override
934
public Date getRevocationTime() {
935
return (revocationTime != null ? (Date) revocationTime.clone() :
936
null);
937
}
938
939
/**
940
* Get the {@code revocationReason} field for the
941
* {@code SingleResponse}.
942
*
943
* @return a {@link CRLReason} containing the revocation reason, or
944
* {@code null} if a revocation reason was not provided or the
945
* response status is not {@code REVOKED}.
946
*/
947
@Override
948
public CRLReason getRevocationReason() {
949
return revocationReason;
950
}
951
952
/**
953
* Get the {@code singleExtensions} for this {@code SingleResponse}.
954
*
955
* @return a {@link Map} of {@link Extension} objects, keyed by
956
* their OID value in string form.
957
*/
958
@Override
959
public Map<String, java.security.cert.Extension> getSingleExtensions() {
960
return Collections.unmodifiableMap(singleExtensions);
961
}
962
963
/**
964
* Construct a string representation of a single OCSP response.
965
*/
966
@Override public String toString() {
967
StringBuilder sb = new StringBuilder();
968
sb.append("SingleResponse:\n");
969
sb.append(certId);
970
sb.append("\nCertStatus: ").append(certStatus).append("\n");
971
if (certStatus == CertStatus.REVOKED) {
972
sb.append("revocationTime is ");
973
sb.append(revocationTime).append("\n");
974
sb.append("revocationReason is ");
975
sb.append(revocationReason).append("\n");
976
}
977
sb.append("thisUpdate is ").append(thisUpdate).append("\n");
978
if (nextUpdate != null) {
979
sb.append("nextUpdate is ").append(nextUpdate).append("\n");
980
}
981
for (java.security.cert.Extension ext : singleExtensions.values()) {
982
sb.append("singleExtension: ");
983
sb.append(ext.toString()).append("\n");
984
}
985
return sb.toString();
986
}
987
}
988
989
/**
990
* Helper class that allows consumers to pass in issuer information. This
991
* will always consist of the issuer's name and public key, but may also
992
* contain a certificate if the originating data is in that form. The
993
* trust anchor for the certificate chain will be included for certpath
994
* disabled algorithm checking.
995
*/
996
static final class IssuerInfo {
997
private final TrustAnchor anchor;
998
private final X509Certificate certificate;
999
private final X500Principal name;
1000
private final PublicKey pubKey;
1001
1002
IssuerInfo(TrustAnchor anchor) {
1003
this(anchor, (anchor != null) ? anchor.getTrustedCert() : null);
1004
}
1005
1006
IssuerInfo(X509Certificate issuerCert) {
1007
this(null, issuerCert);
1008
}
1009
1010
IssuerInfo(TrustAnchor anchor, X509Certificate issuerCert) {
1011
if (anchor == null && issuerCert == null) {
1012
throw new NullPointerException("TrustAnchor and issuerCert " +
1013
"cannot be null");
1014
}
1015
this.anchor = anchor;
1016
if (issuerCert != null) {
1017
name = issuerCert.getSubjectX500Principal();
1018
pubKey = issuerCert.getPublicKey();
1019
certificate = issuerCert;
1020
} else {
1021
name = anchor.getCA();
1022
pubKey = anchor.getCAPublicKey();
1023
certificate = anchor.getTrustedCert();
1024
}
1025
}
1026
1027
/**
1028
* Get the certificate in this IssuerInfo if present.
1029
*
1030
* @return the {@code X509Certificate} used to create this IssuerInfo
1031
* object, or {@code null} if a certificate was not used in its
1032
* creation.
1033
*/
1034
X509Certificate getCertificate() {
1035
return certificate;
1036
}
1037
1038
/**
1039
* Get the name of this issuer.
1040
*
1041
* @return an {@code X500Principal} corresponding to this issuer's
1042
* name. If derived from an issuer's {@code X509Certificate} this
1043
* would be equivalent to the certificate subject name.
1044
*/
1045
X500Principal getName() {
1046
return name;
1047
}
1048
1049
/**
1050
* Get the public key for this issuer.
1051
*
1052
* @return a {@code PublicKey} for this issuer.
1053
*/
1054
PublicKey getPublicKey() {
1055
return pubKey;
1056
}
1057
1058
/**
1059
* Get the TrustAnchor for the certificate chain.
1060
*
1061
* @return a {@code TrustAnchor}.
1062
*/
1063
TrustAnchor getAnchor() {
1064
return anchor;
1065
}
1066
1067
/**
1068
* Create a string representation of this IssuerInfo.
1069
*
1070
* @return a {@code String} form of this IssuerInfo object.
1071
*/
1072
@Override
1073
public String toString() {
1074
StringBuilder sb = new StringBuilder();
1075
sb.append("Issuer Info:\n");
1076
sb.append("Name: ").append(name.toString()).append("\n");
1077
sb.append("Public Key:\n").append(pubKey.toString()).append("\n");
1078
return sb.toString();
1079
}
1080
}
1081
}
1082
1083