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