History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/security/provider/certpath/OCSPResponse.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/security/provider/certpath/OCSPResponse.java+676−0
@@ -0,0 +1,676 @@
1+/*
2+ * Copyright (c) 2003, 2010, 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.math.BigInteger;
30+import java.security.*;
31+import java.security.cert.CertificateException;
32+import java.security.cert.CertificateParsingException;
33+import java.security.cert.CertPathValidatorException;
34+import java.security.cert.CRLReason;
35+import java.security.cert.TrustAnchor;
36+import java.security.cert.X509Certificate;
37+import java.util.Collections;
38+import java.util.Date;
39+import java.util.HashMap;
40+import java.util.List;
41+import java.util.Map;
42+import sun.misc.HexDumpEncoder;
43+import sun.security.x509.*;
44+import sun.security.util.*;
45+
46+/**
47+ * This class is used to process an OCSP response.
48+ * The OCSP Response is defined
49+ * in RFC 2560 and the ASN.1 encoding is as follows:
50+ * <pre>
51+ *
52+ * OCSPResponse ::= SEQUENCE {
53+ * responseStatus OCSPResponseStatus,
54+ * responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
55+ *
56+ * OCSPResponseStatus ::= ENUMERATED {
57+ * successful (0), --Response has valid confirmations
58+ * malformedRequest (1), --Illegal confirmation request
59+ * internalError (2), --Internal error in issuer
60+ * tryLater (3), --Try again later
61+ * --(4) is not used
62+ * sigRequired (5), --Must sign the request
63+ * unauthorized (6) --Request unauthorized
64+ * }
65+ *
66+ * ResponseBytes ::= SEQUENCE {
67+ * responseType OBJECT IDENTIFIER,
68+ * response OCTET STRING }
69+ *
70+ * BasicOCSPResponse ::= SEQUENCE {
71+ * tbsResponseData ResponseData,
72+ * signatureAlgorithm AlgorithmIdentifier,
73+ * signature BIT STRING,
74+ * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
75+ *
76+ * The value for signature SHALL be computed on the hash of the DER
77+ * encoding ResponseData.
78+ *
79+ * ResponseData ::= SEQUENCE {
80+ * version [0] EXPLICIT Version DEFAULT v1,
81+ * responderID ResponderID,
82+ * producedAt GeneralizedTime,
83+ * responses SEQUENCE OF SingleResponse,
84+ * responseExtensions [1] EXPLICIT Extensions OPTIONAL }
85+ *
86+ * ResponderID ::= CHOICE {
87+ * byName [1] Name,
88+ * byKey [2] KeyHash }
89+ *
90+ * KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
91+ * (excluding the tag and length fields)
92+ *
93+ * SingleResponse ::= SEQUENCE {
94+ * certID CertID,
95+ * certStatus CertStatus,
96+ * thisUpdate GeneralizedTime,
97+ * nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
98+ * singleExtensions [1] EXPLICIT Extensions OPTIONAL }
99+ *
100+ * CertStatus ::= CHOICE {
101+ * good [0] IMPLICIT NULL,
102+ * revoked [1] IMPLICIT RevokedInfo,
103+ * unknown [2] IMPLICIT UnknownInfo }
104+ *
105+ * RevokedInfo ::= SEQUENCE {
106+ * revocationTime GeneralizedTime,
107+ * revocationReason [0] EXPLICIT CRLReason OPTIONAL }
108+ *
109+ * UnknownInfo ::= NULL -- this can be replaced with an enumeration
110+ *
111+ * </pre>
112+ *
113+ * @author Ram Marti
114+ */
115+
116+public final class OCSPResponse {
117+
118+ public enum ResponseStatus {
119+ SUCCESSFUL, // Response has valid confirmations
120+ MALFORMED_REQUEST, // Illegal confirmation request
121+ INTERNAL_ERROR, // Internal error in issuer
122+ TRY_LATER, // Try again later
123+ UNUSED, // is not used
124+ SIG_REQUIRED, // Must sign the request
125+ UNAUTHORIZED // Request unauthorized
126+ };
127+ private static ResponseStatus[] rsvalues = ResponseStatus.values();
128+
129+ private static final Debug DEBUG = Debug.getInstance("certpath");
130+ private static final boolean dump = false;
131+ private static final ObjectIdentifier OCSP_BASIC_RESPONSE_OID =
132+ ObjectIdentifier.newInternal(new int[] { 1, 3, 6, 1, 5, 5, 7, 48, 1, 1});
133+ private static final ObjectIdentifier OCSP_NONCE_EXTENSION_OID =
134+ ObjectIdentifier.newInternal(new int[] { 1, 3, 6, 1, 5, 5, 7, 48, 1, 2});
135+
136+ private static final int CERT_STATUS_GOOD = 0;
137+ private static final int CERT_STATUS_REVOKED = 1;
138+ private static final int CERT_STATUS_UNKNOWN = 2;
139+
140+ // ResponderID CHOICE tags
141+ private static final int NAME_TAG = 1;
142+ private static final int KEY_TAG = 2;
143+
144+ // Object identifier for the OCSPSigning key purpose
145+ private static final String KP_OCSP_SIGNING_OID = "1.3.6.1.5.5.7.3.9";
146+
147+ private final ResponseStatus responseStatus;
148+ private final Map<CertId, SingleResponse> singleResponseMap;
149+
150+ // Maximum clock skew in milliseconds (15 minutes) allowed when checking
151+ // validity of OCSP responses
152+ private static final long MAX_CLOCK_SKEW = 900000;
153+
154+ // an array of all of the CRLReasons (used in SingleResponse)
155+ private static CRLReason[] values = CRLReason.values();
156+
157+ /*
158+ * Create an OCSP response from its ASN.1 DER encoding.
159+ */
160+ OCSPResponse(byte[] bytes, Date dateCheckedAgainst,
161+ X509Certificate responderCert)
162+ throws IOException, CertPathValidatorException {
163+
164+ // OCSPResponse
165+ if (dump) {
166+ HexDumpEncoder hexEnc = new HexDumpEncoder();
167+ System.out.println("OCSPResponse bytes are...");
168+ System.out.println(hexEnc.encode(bytes));
169+ }
170+ DerValue der = new DerValue(bytes);
171+ if (der.tag != DerValue.tag_Sequence) {
172+ throw new IOException("Bad encoding in OCSP response: " +
173+ "expected ASN.1 SEQUENCE tag.");
174+ }
175+ DerInputStream derIn = der.getData();
176+
177+ // responseStatus
178+ int status = derIn.getEnumerated();
179+ if (status >= 0 && status < rsvalues.length) {
180+ responseStatus = rsvalues[status];
181+ } else {
182+ // unspecified responseStatus
183+ throw new IOException("Unknown OCSPResponse status: " + status);
184+ }
185+ if (DEBUG != null) {
186+ DEBUG.println("OCSP response status: " + responseStatus);
187+ }
188+ if (responseStatus != ResponseStatus.SUCCESSFUL) {
189+ // no need to continue, responseBytes are not set.
190+ singleResponseMap = Collections.emptyMap();
191+ return;
192+ }
193+
194+ // responseBytes
195+ der = derIn.getDerValue();
196+ if (!der.isContextSpecific((byte)0)) {
197+ throw new IOException("Bad encoding in responseBytes element " +
198+ "of OCSP response: expected ASN.1 context specific tag 0.");
199+ }
200+ DerValue tmp = der.data.getDerValue();
201+ if (tmp.tag != DerValue.tag_Sequence) {
202+ throw new IOException("Bad encoding in responseBytes element " +
203+ "of OCSP response: expected ASN.1 SEQUENCE tag.");
204+ }
205+
206+ // responseType
207+ derIn = tmp.data;
208+ ObjectIdentifier responseType = derIn.getOID();
209+ if (responseType.equals(OCSP_BASIC_RESPONSE_OID)) {
210+ if (DEBUG != null) {
211+ DEBUG.println("OCSP response type: basic");
212+ }
213+ } else {
214+ if (DEBUG != null) {
215+ DEBUG.println("OCSP response type: " + responseType);
216+ }
217+ throw new IOException("Unsupported OCSP response type: " +
218+ responseType);
219+ }
220+
221+ // BasicOCSPResponse
222+ DerInputStream basicOCSPResponse =
223+ new DerInputStream(derIn.getOctetString());
224+
225+ DerValue[] seqTmp = basicOCSPResponse.getSequence(2);
226+ if (seqTmp.length < 3) {
227+ throw new IOException("Unexpected BasicOCSPResponse value");
228+ }
229+
230+ DerValue responseData = seqTmp[0];
231+
232+ // Need the DER encoded ResponseData to verify the signature later
233+ byte[] responseDataDer = seqTmp[0].toByteArray();
234+
235+ // tbsResponseData
236+ if (responseData.tag != DerValue.tag_Sequence) {
237+ throw new IOException("Bad encoding in tbsResponseData " +
238+ "element of OCSP response: expected ASN.1 SEQUENCE tag.");
239+ }
240+ DerInputStream seqDerIn = responseData.data;
241+ DerValue seq = seqDerIn.getDerValue();
242+
243+ // version
244+ if (seq.isContextSpecific((byte)0)) {
245+ // seq[0] is version
246+ if (seq.isConstructed() && seq.isContextSpecific()) {
247+ //System.out.println ("version is available");
248+ seq = seq.data.getDerValue();
249+ int version = seq.getInteger();
250+ if (seq.data.available() != 0) {
251+ throw new IOException("Bad encoding in version " +
252+ " element of OCSP response: bad format");
253+ }
254+ seq = seqDerIn.getDerValue();
255+ }
256+ }
257+
258+ // responderID
259+ short tag = (byte)(seq.tag & 0x1f);
260+ if (tag == NAME_TAG) {
261+ if (DEBUG != null) {
262+ X500Name responderName = new X500Name(seq.getData());
263+ DEBUG.println("OCSP Responder name: " + responderName);
264+ }
265+ } else if (tag == KEY_TAG) {
266+ // Ignore, for now
267+ } else {
268+ throw new IOException("Bad encoding in responderID element of " +
269+ "OCSP response: expected ASN.1 context specific tag 0 or 1");
270+ }
271+
272+ // producedAt
273+ seq = seqDerIn.getDerValue();
274+ if (DEBUG != null) {
275+ Date producedAtDate = seq.getGeneralizedTime();
276+ DEBUG.println("OCSP response produced at: " + producedAtDate);
277+ }
278+
279+ // responses
280+ DerValue[] singleResponseDer = seqDerIn.getSequence(1);
281+ singleResponseMap
282+ = new HashMap<CertId, SingleResponse>(singleResponseDer.length);
283+ if (DEBUG != null) {
284+ DEBUG.println("OCSP number of SingleResponses: "
285+ + singleResponseDer.length);
286+ }
287+ for (int i = 0; i < singleResponseDer.length; i++) {
288+ SingleResponse singleResponse
289+ = new SingleResponse(singleResponseDer[i]);
290+ singleResponseMap.put(singleResponse.getCertId(), singleResponse);
291+ }
292+
293+ // responseExtensions
294+ if (seqDerIn.available() > 0) {
295+ seq = seqDerIn.getDerValue();
296+ if (seq.isContextSpecific((byte)1)) {
297+ DerValue[] responseExtDer = seq.data.getSequence(3);
298+ for (int i = 0; i < responseExtDer.length; i++) {
299+ Extension responseExtension
300+ = new Extension(responseExtDer[i]);
301+ if (DEBUG != null) {
302+ DEBUG.println("OCSP extension: " + responseExtension);
303+ }
304+ if (responseExtension.getExtensionId().equals(
305+ OCSP_NONCE_EXTENSION_OID)) {
306+ /*
307+ ocspNonce =
308+ responseExtension[i].getExtensionValue();
309+ */
310+ } else if (responseExtension.isCritical()) {
311+ throw new IOException(
312+ "Unsupported OCSP critical extension: " +
313+ responseExtension.getExtensionId());
314+ }
315+ }
316+ }
317+ }
318+
319+ // signatureAlgorithmId
320+ AlgorithmId sigAlgId = AlgorithmId.parse(seqTmp[1]);
321+
322+ // signature
323+ byte[] signature = seqTmp[2].getBitString();
324+ X509CertImpl[] x509Certs = null;
325+
326+ // if seq[3] is available , then it is a sequence of certificates
327+ if (seqTmp.length > 3) {
328+ // certs are available
329+ DerValue seqCert = seqTmp[3];
330+ if (!seqCert.isContextSpecific((byte)0)) {
331+ throw new IOException("Bad encoding in certs element of " +
332+ "OCSP response: expected ASN.1 context specific tag 0.");
333+ }
334+ DerValue[] certs = seqCert.getData().getSequence(3);
335+ x509Certs = new X509CertImpl[certs.length];
336+ try {
337+ for (int i = 0; i < certs.length; i++) {
338+ x509Certs[i] = new X509CertImpl(certs[i].toByteArray());
339+ }
340+ } catch (CertificateException ce) {
341+ throw new IOException("Bad encoding in X509 Certificate", ce);
342+ }
343+ }
344+
345+ // Check whether the cert returned by the responder is trusted
346+ if (x509Certs != null && x509Certs[0] != null) {
347+ X509CertImpl cert = x509Certs[0];
348+
349+ // First check if the cert matches the responder cert which
350+ // was set locally.
351+ if (cert.equals(responderCert)) {
352+ // cert is trusted, now verify the signed response
353+
354+ // Next check if the cert was issued by the responder cert
355+ // which was set locally.
356+ } else if (cert.getIssuerX500Principal().equals(
357+ responderCert.getSubjectX500Principal())) {
358+
359+ // Check for the OCSPSigning key purpose
360+ try {
361+ List<String> keyPurposes = cert.getExtendedKeyUsage();
362+ if (keyPurposes == null ||
363+ !keyPurposes.contains(KP_OCSP_SIGNING_OID)) {
364+ throw new CertPathValidatorException(
365+ "Responder's certificate not valid for signing " +
366+ "OCSP responses");
367+ }
368+ } catch (CertificateParsingException cpe) {
369+ // assume cert is not valid for signing
370+ throw new CertPathValidatorException(
371+ "Responder's certificate not valid for signing " +
372+ "OCSP responses", cpe);
373+ }
374+
375+ // Check algorithm constraints specified in security property
376+ // "jdk.certpath.disabledAlgorithms".
377+ AlgorithmChecker algChecker = new AlgorithmChecker(
378+ new TrustAnchor(responderCert, null));
379+ algChecker.init(false);
380+ algChecker.check(cert, Collections.<String>emptySet());
381+
382+ // check the validity
383+ try {
384+ if (dateCheckedAgainst == null) {
385+ cert.checkValidity();
386+ } else {
387+ cert.checkValidity(dateCheckedAgainst);
388+ }
389+ } catch (GeneralSecurityException e) {
390+ throw new CertPathValidatorException(
391+ "Responder's certificate not within the " +
392+ "validity period", e);
393+ }
394+
395+ // check for revocation
396+ //
397+ // A CA may specify that an OCSP client can trust a
398+ // responder for the lifetime of the responder's
399+ // certificate. The CA does so by including the
400+ // extension id-pkix-ocsp-nocheck.
401+ //
402+ Extension noCheck =
403+ cert.getExtension(PKIXExtensions.OCSPNoCheck_Id);
404+ if (noCheck != null) {
405+ if (DEBUG != null) {
406+ DEBUG.println("Responder's certificate includes " +
407+ "the extension id-pkix-ocsp-nocheck.");
408+ }
409+ } else {
410+ // we should do the revocation checking of the
411+ // authorized responder in a future update.
412+ }
413+
414+ // verify the signature
415+ try {
416+ cert.verify(responderCert.getPublicKey());
417+ responderCert = cert;
418+ // cert is trusted, now verify the signed response
419+
420+ } catch (GeneralSecurityException e) {
421+ responderCert = null;
422+ }
423+ } else {
424+ throw new CertPathValidatorException(
425+ "Responder's certificate is not authorized to sign " +
426+ "OCSP responses");
427+ }
428+ }
429+
430+ // Confirm that the signed response was generated using the public
431+ // key from the trusted responder cert
432+ if (responderCert != null) {
433+ // Check algorithm constraints specified in security property
434+ // "jdk.certpath.disabledAlgorithms".
435+ AlgorithmChecker.check(responderCert.getPublicKey(), sigAlgId);
436+
437+ if (!verifyResponse(responseDataDer, responderCert,
438+ sigAlgId, signature)) {
439+ throw new CertPathValidatorException(
440+ "Error verifying OCSP Responder's signature");
441+ }
442+ } else {
443+ // Need responder's cert in order to verify the signature
444+ throw new CertPathValidatorException(
445+ "Unable to verify OCSP Responder's signature");
446+ }
447+ }
448+
449+ /**
450+ * Returns the OCSP ResponseStatus.
451+ */
452+ ResponseStatus getResponseStatus() {
453+ return responseStatus;
454+ }
455+
456+ /*
457+ * Verify the signature of the OCSP response.
458+ * The responder's cert is implicitly trusted.
459+ */
460+ private boolean verifyResponse(byte[] responseData, X509Certificate cert,
461+ AlgorithmId sigAlgId, byte[] signBytes)
462+ throws CertPathValidatorException {
463+
464+ try {
465+ Signature respSignature = Signature.getInstance(sigAlgId.getName());
466+ respSignature.initVerify(cert);
467+ respSignature.update(responseData);
468+
469+ if (respSignature.verify(signBytes)) {
470+ if (DEBUG != null) {
471+ DEBUG.println("Verified signature of OCSP Responder");
472+ }
473+ return true;
474+
475+ } else {
476+ if (DEBUG != null) {
477+ DEBUG.println(
478+ "Error verifying signature of OCSP Responder");
479+ }
480+ return false;
481+ }
482+ } catch (InvalidKeyException ike) {
483+ throw new CertPathValidatorException(ike);
484+ } catch (NoSuchAlgorithmException nsae) {
485+ throw new CertPathValidatorException(nsae);
486+ } catch (SignatureException se) {
487+ throw new CertPathValidatorException(se);
488+ }
489+ }
490+
491+ /**
492+ * Returns the SingleResponse of the specified CertId, or null if
493+ * there is no response for that CertId.
494+ */
495+ SingleResponse getSingleResponse(CertId certId) {
496+ return singleResponseMap.get(certId);
497+ }
498+
499+ /*
500+ * A class representing a single OCSP response.
501+ */
502+ final static class SingleResponse implements OCSP.RevocationStatus {
503+ private final CertId certId;
504+ private final CertStatus certStatus;
505+ private final Date thisUpdate;
506+ private final Date nextUpdate;
507+ private final Date revocationTime;
508+ private final CRLReason revocationReason;
509+ private final Map<String, java.security.cert.Extension> singleExtensions;
510+
511+ private SingleResponse(DerValue der) throws IOException {
512+ if (der.tag != DerValue.tag_Sequence) {
513+ throw new IOException("Bad ASN.1 encoding in SingleResponse");
514+ }
515+ DerInputStream tmp = der.data;
516+
517+ certId = new CertId(tmp.getDerValue().data);
518+ DerValue derVal = tmp.getDerValue();
519+ short tag = (byte)(derVal.tag & 0x1f);
520+ if (tag == CERT_STATUS_REVOKED) {
521+ certStatus = CertStatus.REVOKED;
522+ revocationTime = derVal.data.getGeneralizedTime();
523+ if (derVal.data.available() != 0) {
524+ DerValue dv = derVal.data.getDerValue();
525+ tag = (byte)(dv.tag & 0x1f);
526+ if (tag == 0) {
527+ int reason = dv.data.getEnumerated();
528+ // if reason out-of-range just leave as UNSPECIFIED
529+ if (reason >= 0 && reason < values.length) {
530+ revocationReason = values[reason];
531+ } else {
532+ revocationReason = CRLReason.UNSPECIFIED;
533+ }
534+ } else {
535+ revocationReason = CRLReason.UNSPECIFIED;
536+ }
537+ } else {
538+ revocationReason = CRLReason.UNSPECIFIED;
539+ }
540+ // RevokedInfo
541+ if (DEBUG != null) {
542+ DEBUG.println("Revocation time: " + revocationTime);
543+ DEBUG.println("Revocation reason: " + revocationReason);
544+ }
545+ } else {
546+ revocationTime = null;
547+ revocationReason = CRLReason.UNSPECIFIED;
548+ if (tag == CERT_STATUS_GOOD) {
549+ certStatus = CertStatus.GOOD;
550+ } else if (tag == CERT_STATUS_UNKNOWN) {
551+ certStatus = CertStatus.UNKNOWN;
552+ } else {
553+ throw new IOException("Invalid certificate status");
554+ }
555+ }
556+
557+ thisUpdate = tmp.getGeneralizedTime();
558+
559+ if (tmp.available() == 0) {
560+ // we are done
561+ nextUpdate = null;
562+ } else {
563+ derVal = tmp.getDerValue();
564+ tag = (byte)(derVal.tag & 0x1f);
565+ if (tag == 0) {
566+ // next update
567+ nextUpdate = derVal.data.getGeneralizedTime();
568+
569+ if (tmp.available() == 0) {
570+ // we are done
571+ } else {
572+ derVal = tmp.getDerValue();
573+ tag = (byte)(derVal.tag & 0x1f);
574+ }
575+ } else {
576+ nextUpdate = null;
577+ }
578+ }
579+ // singleExtensions
580+ if (tmp.available() > 0) {
581+ derVal = tmp.getDerValue();
582+ if (derVal.isContextSpecific((byte)1)) {
583+ DerValue[] singleExtDer = derVal.data.getSequence(3);
584+ singleExtensions =
585+ new HashMap<String, java.security.cert.Extension>
586+ (singleExtDer.length);
587+ for (int i = 0; i < singleExtDer.length; i++) {
588+ Extension ext = new Extension(singleExtDer[i]);
589+ if (DEBUG != null) {
590+ DEBUG.println("OCSP single extension: " + ext);
591+ }
592+ // We don't support any extensions yet. Therefore, if it
593+ // is critical we must throw an exception because we
594+ // don't know how to process it.
595+ if (ext.isCritical()) {
596+ throw new IOException(
597+ "Unsupported OCSP critical extension: " +
598+ ext.getExtensionId());
599+ }
600+ singleExtensions.put(ext.getId(), ext);
601+ }
602+ } else {
603+ singleExtensions = Collections.emptyMap();
604+ }
605+ } else {
606+ singleExtensions = Collections.emptyMap();
607+ }
608+
609+ long now = System.currentTimeMillis();
610+ Date nowPlusSkew = new Date(now + MAX_CLOCK_SKEW);
611+ Date nowMinusSkew = new Date(now - MAX_CLOCK_SKEW);
612+ if (DEBUG != null) {
613+ String until = "";
614+ if (nextUpdate != null) {
615+ until = " until " + nextUpdate;
616+ }
617+ DEBUG.println("Response's validity interval is from " +
618+ thisUpdate + until);
619+ }
620+ // Check that the test date is within the validity interval
621+ if ((thisUpdate != null && nowPlusSkew.before(thisUpdate)) ||
622+ (nextUpdate != null && nowMinusSkew.after(nextUpdate))) {
623+
624+ if (DEBUG != null) {
625+ DEBUG.println("Response is unreliable: its validity " +
626+ "interval is out-of-date");
627+ }
628+ throw new IOException("Response is unreliable: its validity " +
629+ "interval is out-of-date");
630+ }
631+ }
632+
633+ /*
634+ * Return the certificate's revocation status code
635+ */
636+ @Override public CertStatus getCertStatus() {
637+ return certStatus;
638+ }
639+
640+ private CertId getCertId() {
641+ return certId;
642+ }
643+
644+ @Override public Date getRevocationTime() {
645+ return (Date) revocationTime.clone();
646+ }
647+
648+ @Override public CRLReason getRevocationReason() {
649+ return revocationReason;
650+ }
651+
652+ @Override
653+ public Map<String, java.security.cert.Extension> getSingleExtensions() {
654+ return Collections.unmodifiableMap(singleExtensions);
655+ }
656+
657+ /**
658+ * Construct a string representation of a single OCSP response.
659+ */
660+ @Override public String toString() {
661+ StringBuilder sb = new StringBuilder();
662+ sb.append("SingleResponse: \n");
663+ sb.append(certId);
664+ sb.append("\nCertStatus: "+ certStatus + "\n");
665+ if (certStatus == CertStatus.REVOKED) {
666+ sb.append("revocationTime is " + revocationTime + "\n");
667+ sb.append("revocationReason is " + revocationReason + "\n");
668+ }
669+ sb.append("thisUpdate is " + thisUpdate + "\n");
670+ if (nextUpdate != null) {
671+ sb.append("nextUpdate is " + nextUpdate + "\n");
672+ }
673+ return sb.toString();
674+ }
675+ }
676+}