1 /*
2  * Copyright (c) 2003, 2015, 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 javax.security.auth.x500.X500Principal;
45 
46 import sun.misc.HexDumpEncoder;
47 import sun.security.action.GetIntegerAction;
48 import sun.security.x509.*;
49 import sun.security.util.*;
50 
51 /**
52  * This class is used to process an OCSP response.
53  * The OCSP Response is defined
54  * in RFC 2560 and the ASN.1 encoding is as follows:
55  * <pre>
56  *
57  *  OCSPResponse ::= SEQUENCE {
58  *      responseStatus         OCSPResponseStatus,
59  *      responseBytes          [0] EXPLICIT ResponseBytes OPTIONAL }
60  *
61  *   OCSPResponseStatus ::= ENUMERATED {
62  *       successful            (0),  --Response has valid confirmations
63  *       malformedRequest      (1),  --Illegal confirmation request
64  *       internalError         (2),  --Internal error in issuer
65  *       tryLater              (3),  --Try again later
66  *                                   --(4) is not used
67  *       sigRequired           (5),  --Must sign the request
68  *       unauthorized          (6)   --Request unauthorized
69  *   }
70  *
71  *   ResponseBytes ::=       SEQUENCE {
72  *       responseType   OBJECT IDENTIFIER,
73  *       response       OCTET STRING }
74  *
75  *   BasicOCSPResponse       ::= SEQUENCE {
76  *      tbsResponseData      ResponseData,
77  *      signatureAlgorithm   AlgorithmIdentifier,
78  *      signature            BIT STRING,
79  *      certs                [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
80  *
81  *   The value for signature SHALL be computed on the hash of the DER
82  *   encoding ResponseData.
83  *
84  *   ResponseData ::= SEQUENCE {
85  *      version              [0] EXPLICIT Version DEFAULT v1,
86  *      responderID              ResponderID,
87  *      producedAt               GeneralizedTime,
88  *      responses                SEQUENCE OF SingleResponse,
89  *      responseExtensions   [1] EXPLICIT Extensions OPTIONAL }
90  *
91  *   ResponderID ::= CHOICE {
92  *      byName               [1] Name,
93  *      byKey                [2] KeyHash }
94  *
95  *   KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
96  *   (excluding the tag and length fields)
97  *
98  *   SingleResponse ::= SEQUENCE {
99  *      certID                       CertID,
100  *      certStatus                   CertStatus,
101  *      thisUpdate                   GeneralizedTime,
102  *      nextUpdate         [0]       EXPLICIT GeneralizedTime OPTIONAL,
103  *      singleExtensions   [1]       EXPLICIT Extensions OPTIONAL }
104  *
105  *   CertStatus ::= CHOICE {
106  *       good        [0]     IMPLICIT NULL,
107  *       revoked     [1]     IMPLICIT RevokedInfo,
108  *       unknown     [2]     IMPLICIT UnknownInfo }
109  *
110  *   RevokedInfo ::= SEQUENCE {
111  *       revocationTime              GeneralizedTime,
112  *       revocationReason    [0]     EXPLICIT CRLReason OPTIONAL }
113  *
114  *   UnknownInfo ::= NULL -- this can be replaced with an enumeration
115  *
116  * </pre>
117  *
118  * @author      Ram Marti
119  */
120 
121 public final class OCSPResponse {
122 
123     public enum ResponseStatus {
124         SUCCESSFUL,            // Response has valid confirmations
125         MALFORMED_REQUEST,     // Illegal request
126         INTERNAL_ERROR,        // Internal error in responder
127         TRY_LATER,             // Try again later
128         UNUSED,                // is not used
129         SIG_REQUIRED,          // Must sign the request
130         UNAUTHORIZED           // Request unauthorized
131     };
132     private static ResponseStatus[] rsvalues = ResponseStatus.values();
133 
134     private static final Debug debug = Debug.getInstance("certpath");
135     private static final boolean dump = debug != null && Debug.isOn("ocsp");
136     private static final ObjectIdentifier OCSP_BASIC_RESPONSE_OID =
137         ObjectIdentifier.newInternal(new int[] { 1, 3, 6, 1, 5, 5, 7, 48, 1, 1});
138     private static final int CERT_STATUS_GOOD = 0;
139     private static final int CERT_STATUS_REVOKED = 1;
140     private static final int CERT_STATUS_UNKNOWN = 2;
141 
142     // ResponderID CHOICE tags
143     private static final int NAME_TAG = 1;
144     private static final int KEY_TAG = 2;
145 
146     // Object identifier for the OCSPSigning key purpose
147     private static final String KP_OCSP_SIGNING_OID = "1.3.6.1.5.5.7.3.9";
148 
149     // Default maximum clock skew in milliseconds (15 minutes)
150     // allowed when checking validity of OCSP responses
151     private static final int DEFAULT_MAX_CLOCK_SKEW = 900000;
152 
153     /**
154      * Integer value indicating the maximum allowable clock skew,
155      * in milliseconds, to be used for the OCSP check.
156      */
157     private static final int MAX_CLOCK_SKEW = initializeClockSkew();
158 
159     /**
160      * Initialize the maximum allowable clock skew by getting the OCSP
161      * clock skew system property. If the property has not been set, or if its
162      * value is negative, set the skew to the default.
163      */
initializeClockSkew()164     private static int initializeClockSkew() {
165         Integer tmp = java.security.AccessController.doPrivileged(
166                 new GetIntegerAction("com.sun.security.ocsp.clockSkew"));
167         if (tmp == null || tmp < 0) {
168             return DEFAULT_MAX_CLOCK_SKEW;
169         }
170         // Convert to milliseconds, as the system property will be
171         // specified in seconds
172         return tmp * 1000;
173     }
174 
175     // an array of all of the CRLReasons (used in SingleResponse)
176     private static CRLReason[] values = CRLReason.values();
177 
178     private final ResponseStatus responseStatus;
179     private final Map<CertId, SingleResponse> singleResponseMap;
180     private final AlgorithmId sigAlgId;
181     private final byte[] signature;
182     private final byte[] tbsResponseData;
183     private final byte[] responseNonce;
184     private List<X509CertImpl> certs;
185     private X509CertImpl signerCert = null;
186     private X500Principal responderName = null;
187     private KeyIdentifier responderKeyId = null;
188 
189     /*
190      * Create an OCSP response from its ASN.1 DER encoding.
191      */
OCSPResponse(byte[] bytes)192     OCSPResponse(byte[] bytes) throws IOException {
193         if (dump) {
194             HexDumpEncoder hexEnc = new HexDumpEncoder();
195             debug.println("OCSPResponse bytes...\n\n" +
196                 hexEnc.encode(bytes) + "\n");
197         }
198         DerValue der = new DerValue(bytes);
199         if (der.tag != DerValue.tag_Sequence) {
200             throw new IOException("Bad encoding in OCSP response: " +
201                 "expected ASN.1 SEQUENCE tag.");
202         }
203         DerInputStream derIn = der.getData();
204 
205         // responseStatus
206         int status = derIn.getEnumerated();
207         if (status >= 0 && status < rsvalues.length) {
208             responseStatus = rsvalues[status];
209         } else {
210             // unspecified responseStatus
211             throw new IOException("Unknown OCSPResponse status: " + status);
212         }
213         if (debug != null) {
214             debug.println("OCSP response status: " + responseStatus);
215         }
216         if (responseStatus != ResponseStatus.SUCCESSFUL) {
217             // no need to continue, responseBytes are not set.
218             singleResponseMap = Collections.emptyMap();
219             certs = new ArrayList<X509CertImpl>();
220             sigAlgId = null;
221             signature = null;
222             tbsResponseData = null;
223             responseNonce = null;
224             return;
225         }
226 
227         // responseBytes
228         der = derIn.getDerValue();
229         if (!der.isContextSpecific((byte)0)) {
230             throw new IOException("Bad encoding in responseBytes element " +
231                 "of OCSP response: expected ASN.1 context specific tag 0.");
232         }
233         DerValue tmp = der.data.getDerValue();
234         if (tmp.tag != DerValue.tag_Sequence) {
235             throw new IOException("Bad encoding in responseBytes element " +
236                 "of OCSP response: expected ASN.1 SEQUENCE tag.");
237         }
238 
239         // responseType
240         derIn = tmp.data;
241         ObjectIdentifier responseType = derIn.getOID();
242         if (responseType.equals((Object)OCSP_BASIC_RESPONSE_OID)) {
243             if (debug != null) {
244                 debug.println("OCSP response type: basic");
245             }
246         } else {
247             if (debug != null) {
248                 debug.println("OCSP response type: " + responseType);
249             }
250             throw new IOException("Unsupported OCSP response type: " +
251                                   responseType);
252         }
253 
254         // BasicOCSPResponse
255         DerInputStream basicOCSPResponse =
256             new DerInputStream(derIn.getOctetString());
257 
258         DerValue[] seqTmp = basicOCSPResponse.getSequence(2);
259         if (seqTmp.length < 3) {
260             throw new IOException("Unexpected BasicOCSPResponse value");
261         }
262 
263         DerValue responseData = seqTmp[0];
264 
265         // Need the DER encoded ResponseData to verify the signature later
266         tbsResponseData = seqTmp[0].toByteArray();
267 
268         // tbsResponseData
269         if (responseData.tag != DerValue.tag_Sequence) {
270             throw new IOException("Bad encoding in tbsResponseData " +
271                 "element of OCSP response: expected ASN.1 SEQUENCE tag.");
272         }
273         DerInputStream seqDerIn = responseData.data;
274         DerValue seq = seqDerIn.getDerValue();
275 
276         // version
277         if (seq.isContextSpecific((byte)0)) {
278             // seq[0] is version
279             if (seq.isConstructed() && seq.isContextSpecific()) {
280                 //System.out.println ("version is available");
281                 seq = seq.data.getDerValue();
282                 int version = seq.getInteger();
283                 if (seq.data.available() != 0) {
284                     throw new IOException("Bad encoding in version " +
285                         " element of OCSP response: bad format");
286                 }
287                 seq = seqDerIn.getDerValue();
288             }
289         }
290 
291         // responderID
292         short tag = (byte)(seq.tag & 0x1f);
293         if (tag == NAME_TAG) {
294             responderName = new X500Principal(seq.getData().toByteArray());
295             if (debug != null) {
296                 debug.println("Responder's name: " + responderName);
297             }
298         } else if (tag == KEY_TAG) {
299             responderKeyId = new KeyIdentifier(seq.getData().getOctetString());
300             if (debug != null) {
301                 debug.println("Responder's key ID: " +
302                     Debug.toString(responderKeyId.getIdentifier()));
303             }
304         } else {
305             throw new IOException("Bad encoding in responderID element of " +
306                 "OCSP response: expected ASN.1 context specific tag 0 or 1");
307         }
308 
309         // producedAt
310         seq = seqDerIn.getDerValue();
311         if (debug != null) {
312             Date producedAtDate = seq.getGeneralizedTime();
313             debug.println("OCSP response produced at: " + producedAtDate);
314         }
315 
316         // responses
317         DerValue[] singleResponseDer = seqDerIn.getSequence(1);
318         singleResponseMap = new HashMap<>(singleResponseDer.length);
319         if (debug != null) {
320             debug.println("OCSP number of SingleResponses: "
321                           + singleResponseDer.length);
322         }
323         for (int i = 0; i < singleResponseDer.length; i++) {
324             SingleResponse singleResponse =
325                 new SingleResponse(singleResponseDer[i]);
326             singleResponseMap.put(singleResponse.getCertId(), singleResponse);
327         }
328 
329         // responseExtensions
330         byte[] nonce = null;
331         if (seqDerIn.available() > 0) {
332             seq = seqDerIn.getDerValue();
333             if (seq.isContextSpecific((byte)1)) {
334                 DerValue[] responseExtDer = seq.data.getSequence(3);
335                 for (int i = 0; i < responseExtDer.length; i++) {
336                     Extension ext = new Extension(responseExtDer[i]);
337                     if (debug != null) {
338                         debug.println("OCSP extension: " + ext);
339                     }
340                     // Only the NONCE extension is recognized
341                     if (ext.getExtensionId().equals((Object)
342                         OCSP.NONCE_EXTENSION_OID))
343                     {
344                         nonce = ext.getExtensionValue();
345                     } else if (ext.isCritical())  {
346                         throw new IOException(
347                             "Unsupported OCSP critical extension: " +
348                             ext.getExtensionId());
349                     }
350                 }
351             }
352         }
353         responseNonce = nonce;
354 
355         // signatureAlgorithmId
356         sigAlgId = AlgorithmId.parse(seqTmp[1]);
357 
358         // signature
359         signature = seqTmp[2].getBitString();
360 
361         // if seq[3] is available , then it is a sequence of certificates
362         if (seqTmp.length > 3) {
363             // certs are available
364             DerValue seqCert = seqTmp[3];
365             if (!seqCert.isContextSpecific((byte)0)) {
366                 throw new IOException("Bad encoding in certs element of " +
367                     "OCSP response: expected ASN.1 context specific tag 0.");
368             }
369             DerValue[] derCerts = seqCert.getData().getSequence(3);
370             certs = new ArrayList<X509CertImpl>(derCerts.length);
371             try {
372                 for (int i = 0; i < derCerts.length; i++) {
373                     X509CertImpl cert =
374                         new X509CertImpl(derCerts[i].toByteArray());
375                     certs.add(cert);
376 
377                     if (debug != null) {
378                         debug.println("OCSP response cert #" + (i + 1) + ": " +
379                             cert.getSubjectX500Principal());
380                     }
381                 }
382             } catch (CertificateException ce) {
383                 throw new IOException("Bad encoding in X509 Certificate", ce);
384             }
385         } else {
386             certs = new ArrayList<X509CertImpl>();
387         }
388     }
389 
verify(List<CertId> certIds, X509Certificate issuerCert, X509Certificate responderCert, Date date, byte[] nonce)390     void verify(List<CertId> certIds, X509Certificate issuerCert,
391                 X509Certificate responderCert, Date date, byte[] nonce)
392         throws CertPathValidatorException
393     {
394         switch (responseStatus) {
395             case SUCCESSFUL:
396                 break;
397             case TRY_LATER:
398             case INTERNAL_ERROR:
399                 throw new CertPathValidatorException(
400                     "OCSP response error: " + responseStatus, null, null, -1,
401                     BasicReason.UNDETERMINED_REVOCATION_STATUS);
402             case UNAUTHORIZED:
403             default:
404                 throw new CertPathValidatorException("OCSP response error: " +
405                                                      responseStatus);
406         }
407 
408         // Check that the response includes a response for all of the
409         // certs that were supplied in the request
410         for (CertId certId : certIds) {
411             SingleResponse sr = getSingleResponse(certId);
412             if (sr == null) {
413                 if (debug != null) {
414                     debug.println("No response found for CertId: " + certId);
415                 }
416                 throw new CertPathValidatorException(
417                     "OCSP response does not include a response for a " +
418                     "certificate supplied in the OCSP request");
419             }
420             if (debug != null) {
421                 debug.println("Status of certificate (with serial number " +
422                     certId.getSerialNumber() + ") is: " + sr.getCertStatus());
423             }
424         }
425 
426         // Locate the signer cert
427         if (signerCert == null) {
428             // Add the Issuing CA cert and/or Trusted Responder cert to the list
429             // of certs from the OCSP response
430             try {
431                 certs.add(X509CertImpl.toImpl(issuerCert));
432                 if (responderCert != null) {
433                     certs.add(X509CertImpl.toImpl(responderCert));
434                 }
435             } catch (CertificateException ce) {
436                 throw new CertPathValidatorException(
437                     "Invalid issuer or trusted responder certificate", ce);
438             }
439 
440             if (responderName != null) {
441                 for (X509CertImpl cert : certs) {
442                     if (cert.getSubjectX500Principal().equals(responderName)) {
443                         signerCert = cert;
444                         break;
445                     }
446                 }
447             } else if (responderKeyId != null) {
448                 for (X509CertImpl cert : certs) {
449                     // Match responder's key identifier against the cert's SKID
450                     // This will match if the SKID is encoded using the 160-bit
451                     // SHA-1 hash method as defined in RFC 5280.
452                     KeyIdentifier certKeyId = cert.getSubjectKeyId();
453                     if (certKeyId != null && responderKeyId.equals(certKeyId)) {
454                         signerCert = cert;
455                         break;
456                     } else {
457                         // The certificate does not have a SKID or may have
458                         // been using a different algorithm (ex: see RFC 7093).
459                         // Check if the responder's key identifier matches
460                         // against a newly generated key identifier of the
461                         // cert's public key using the 160-bit SHA-1 method.
462                         try {
463                             certKeyId = new KeyIdentifier(cert.getPublicKey());
464                         } catch (IOException e) {
465                             // ignore
466                         }
467                         if (responderKeyId.equals(certKeyId)) {
468                             signerCert = cert;
469                             break;
470                         }
471                     }
472                 }
473             }
474         }
475 
476         // Check whether the signer cert returned by the responder is trusted
477         if (signerCert != null) {
478             // Check if the response is signed by the issuing CA
479             if (signerCert.equals(issuerCert)) {
480                 if (debug != null) {
481                     debug.println("OCSP response is signed by the target's " +
482                         "Issuing CA");
483                 }
484                 // cert is trusted, now verify the signed response
485 
486             // Check if the response is signed by a trusted responder
487             } else if (signerCert.equals(responderCert)) {
488                 if (debug != null) {
489                     debug.println("OCSP response is signed by a Trusted " +
490                         "Responder");
491                 }
492                 // cert is trusted, now verify the signed response
493 
494             // Check if the response is signed by an authorized responder
495             } else if (signerCert.getIssuerX500Principal().equals(
496                        issuerCert.getSubjectX500Principal())) {
497 
498                 // Check for the OCSPSigning key purpose
499                 try {
500                     List<String> keyPurposes = signerCert.getExtendedKeyUsage();
501                     if (keyPurposes == null ||
502                         !keyPurposes.contains(KP_OCSP_SIGNING_OID)) {
503                         throw new CertPathValidatorException(
504                             "Responder's certificate not valid for signing " +
505                             "OCSP responses");
506                     }
507                 } catch (CertificateParsingException cpe) {
508                     // assume cert is not valid for signing
509                     throw new CertPathValidatorException(
510                         "Responder's certificate not valid for signing " +
511                         "OCSP responses", cpe);
512                 }
513 
514                 // Check algorithm constraints specified in security property
515                 // "jdk.certpath.disabledAlgorithms".
516                 AlgorithmChecker algChecker = new AlgorithmChecker(
517                                     new TrustAnchor(issuerCert, null));
518                 algChecker.init(false);
519                 algChecker.check(signerCert, Collections.<String>emptySet());
520 
521                 // check the validity
522                 try {
523                     if (date == null) {
524                         signerCert.checkValidity();
525                     } else {
526                         signerCert.checkValidity(date);
527                     }
528                 } catch (CertificateException e) {
529                     throw new CertPathValidatorException(
530                         "Responder's certificate not within the " +
531                         "validity period", e);
532                 }
533 
534                 // check for revocation
535                 //
536                 // A CA may specify that an OCSP client can trust a
537                 // responder for the lifetime of the responder's
538                 // certificate. The CA does so by including the
539                 // extension id-pkix-ocsp-nocheck.
540                 //
541                 Extension noCheck =
542                     signerCert.getExtension(PKIXExtensions.OCSPNoCheck_Id);
543                 if (noCheck != null) {
544                     if (debug != null) {
545                         debug.println("Responder's certificate includes " +
546                             "the extension id-pkix-ocsp-nocheck.");
547                     }
548                 } else {
549                     // we should do the revocation checking of the
550                     // authorized responder in a future update.
551                 }
552 
553                 // verify the signature
554                 try {
555                     signerCert.verify(issuerCert.getPublicKey());
556                     if (debug != null) {
557                         debug.println("OCSP response is signed by an " +
558                             "Authorized Responder");
559                     }
560                     // cert is trusted, now verify the signed response
561 
562                 } catch (GeneralSecurityException e) {
563                     signerCert = null;
564                 }
565             } else {
566                 throw new CertPathValidatorException(
567                     "Responder's certificate is not authorized to sign " +
568                     "OCSP responses");
569             }
570         }
571 
572         // Confirm that the signed response was generated using the public
573         // key from the trusted responder cert
574         if (signerCert != null) {
575             // Check algorithm constraints specified in security property
576             // "jdk.certpath.disabledAlgorithms".
577             AlgorithmChecker.check(signerCert.getPublicKey(), sigAlgId);
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 
597         long now = (date == null) ? System.currentTimeMillis() : date.getTime();
598         Date nowPlusSkew = new Date(now + MAX_CLOCK_SKEW);
599         Date nowMinusSkew = new Date(now - MAX_CLOCK_SKEW);
600         for (SingleResponse sr : singleResponseMap.values()) {
601             if (debug != null) {
602                 String until = "";
603                 if (sr.nextUpdate != null) {
604                     until = " until " + sr.nextUpdate;
605                 }
606                 debug.println("OCSP response validity interval is from " +
607                                sr.thisUpdate + until);
608                 debug.println("Checking validity of OCSP response on: " +
609                     new Date(now));
610             }
611 
612             // Check that the test date is within the validity interval:
613             //   [ thisUpdate - MAX_CLOCK_SKEW,
614             //     MAX(thisUpdate, nextUpdate) + MAX_CLOCK_SKEW ]
615             if (nowPlusSkew.before(sr.thisUpdate) ||
616                 nowMinusSkew.after(
617                     sr.nextUpdate != null ? sr.nextUpdate : sr.thisUpdate))
618             {
619                 throw new CertPathValidatorException(
620                                       "Response is unreliable: its validity " +
621                                       "interval is out-of-date");
622             }
623         }
624     }
625 
626     /**
627      * Returns the OCSP ResponseStatus.
628      */
getResponseStatus()629     ResponseStatus getResponseStatus() {
630         return responseStatus;
631     }
632 
633     /*
634      * Verify the signature of the OCSP response.
635      */
verifySignature(X509Certificate cert)636     private boolean verifySignature(X509Certificate cert)
637         throws CertPathValidatorException {
638 
639         try {
640             Signature respSignature = Signature.getInstance(sigAlgId.getName());
641             respSignature.initVerify(cert.getPublicKey());
642             respSignature.update(tbsResponseData);
643 
644             if (respSignature.verify(signature)) {
645                 if (debug != null) {
646                     debug.println("Verified signature of OCSP Response");
647                 }
648                 return true;
649 
650             } else {
651                 if (debug != null) {
652                     debug.println(
653                         "Error verifying signature of OCSP Response");
654                 }
655                 return false;
656             }
657         } catch (InvalidKeyException | NoSuchAlgorithmException |
658                  SignatureException e)
659         {
660             throw new CertPathValidatorException(e);
661         }
662     }
663 
664     /**
665      * Returns the SingleResponse of the specified CertId, or null if
666      * there is no response for that CertId.
667      */
getSingleResponse(CertId certId)668     SingleResponse getSingleResponse(CertId certId) {
669         return singleResponseMap.get(certId);
670     }
671 
672     /*
673      * Returns the certificate for the authority that signed the OCSP response.
674      */
getSignerCertificate()675     X509Certificate getSignerCertificate() {
676         return signerCert; // set in verify()
677     }
678 
679     /*
680      * A class representing a single OCSP response.
681      */
682     final static class SingleResponse implements OCSP.RevocationStatus {
683         private final CertId certId;
684         private final CertStatus certStatus;
685         private final Date thisUpdate;
686         private final Date nextUpdate;
687         private final Date revocationTime;
688         private final CRLReason revocationReason;
689         private final Map<String, java.security.cert.Extension> singleExtensions;
690 
SingleResponse(DerValue der)691         private SingleResponse(DerValue der) throws IOException {
692             if (der.tag != DerValue.tag_Sequence) {
693                 throw new IOException("Bad ASN.1 encoding in SingleResponse");
694             }
695             DerInputStream tmp = der.data;
696 
697             certId = new CertId(tmp.getDerValue().data);
698             DerValue derVal = tmp.getDerValue();
699             short tag = (byte)(derVal.tag & 0x1f);
700             if (tag ==  CERT_STATUS_REVOKED) {
701                 certStatus = CertStatus.REVOKED;
702                 revocationTime = derVal.data.getGeneralizedTime();
703                 if (derVal.data.available() != 0) {
704                     DerValue dv = derVal.data.getDerValue();
705                     tag = (byte)(dv.tag & 0x1f);
706                     if (tag == 0) {
707                         int reason = dv.data.getEnumerated();
708                         // if reason out-of-range just leave as UNSPECIFIED
709                         if (reason >= 0 && reason < values.length) {
710                             revocationReason = values[reason];
711                         } else {
712                             revocationReason = CRLReason.UNSPECIFIED;
713                         }
714                     } else {
715                         revocationReason = CRLReason.UNSPECIFIED;
716                     }
717                 } else {
718                     revocationReason = CRLReason.UNSPECIFIED;
719                 }
720                 // RevokedInfo
721                 if (debug != null) {
722                     debug.println("Revocation time: " + revocationTime);
723                     debug.println("Revocation reason: " + revocationReason);
724                 }
725             } else {
726                 revocationTime = null;
727                 revocationReason = CRLReason.UNSPECIFIED;
728                 if (tag == CERT_STATUS_GOOD) {
729                     certStatus = CertStatus.GOOD;
730                 } else if (tag == CERT_STATUS_UNKNOWN) {
731                     certStatus = CertStatus.UNKNOWN;
732                 } else {
733                     throw new IOException("Invalid certificate status");
734                 }
735             }
736 
737             thisUpdate = tmp.getGeneralizedTime();
738 
739             if (tmp.available() == 0)  {
740                 // we are done
741                 nextUpdate = null;
742             } else {
743                 derVal = tmp.getDerValue();
744                 tag = (byte)(derVal.tag & 0x1f);
745                 if (tag == 0) {
746                     // next update
747                     nextUpdate = derVal.data.getGeneralizedTime();
748 
749                     if (tmp.available() == 0)  {
750                         // we are done
751                     } else {
752                         derVal = tmp.getDerValue();
753                         tag = (byte)(derVal.tag & 0x1f);
754                     }
755                 } else {
756                     nextUpdate = null;
757                 }
758             }
759             // singleExtensions
760             if (tmp.available() > 0) {
761                 derVal = tmp.getDerValue();
762                 if (derVal.isContextSpecific((byte)1)) {
763                     DerValue[] singleExtDer = derVal.data.getSequence(3);
764                     singleExtensions =
765                         new HashMap<String, java.security.cert.Extension>
766                             (singleExtDer.length);
767                     for (int i = 0; i < singleExtDer.length; i++) {
768                         Extension ext = new Extension(singleExtDer[i]);
769                         if (debug != null) {
770                             debug.println("OCSP single extension: " + ext);
771                         }
772                         // We don't support any extensions yet. Therefore, if it
773                         // is critical we must throw an exception because we
774                         // don't know how to process it.
775                         if (ext.isCritical()) {
776                             throw new IOException(
777                                 "Unsupported OCSP critical extension: " +
778                                 ext.getExtensionId());
779                         }
780                         singleExtensions.put(ext.getId(), ext);
781                     }
782                 } else {
783                     singleExtensions = Collections.emptyMap();
784                 }
785             } else {
786                 singleExtensions = Collections.emptyMap();
787             }
788         }
789 
790         /*
791          * Return the certificate's revocation status code
792          */
getCertStatus()793         @Override public CertStatus getCertStatus() {
794             return certStatus;
795         }
796 
getCertId()797         private CertId getCertId() {
798             return certId;
799         }
800 
getRevocationTime()801         @Override public Date getRevocationTime() {
802             return (Date) revocationTime.clone();
803         }
804 
getRevocationReason()805         @Override public CRLReason getRevocationReason() {
806             return revocationReason;
807         }
808 
809         @Override
getSingleExtensions()810         public Map<String, java.security.cert.Extension> getSingleExtensions() {
811             return Collections.unmodifiableMap(singleExtensions);
812         }
813 
814         /**
815          * Construct a string representation of a single OCSP response.
816          */
toString()817         @Override public String toString() {
818             StringBuilder sb = new StringBuilder();
819             sb.append("SingleResponse:  \n");
820             sb.append(certId);
821             sb.append("\nCertStatus: "+ certStatus + "\n");
822             if (certStatus == CertStatus.REVOKED) {
823                 sb.append("revocationTime is " + revocationTime + "\n");
824                 sb.append("revocationReason is " + revocationReason + "\n");
825             }
826             sb.append("thisUpdate is " + thisUpdate + "\n");
827             if (nextUpdate != null) {
828                 sb.append("nextUpdate is " + nextUpdate + "\n");
829             }
830             return sb.toString();
831         }
832     }
833 }
834