Home | History | Annotate | Download | only in certpath
      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      */
    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      */
    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 
    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      */
    629     ResponseStatus getResponseStatus() {
    630         return responseStatus;
    631     }
    632 
    633     /*
    634      * Verify the signature of the OCSP response.
    635      */
    636     private boolean verifySignature(X509Certificate cert)
    637         throws CertPathValidatorException {
    638 
    639         try {
    640             Signature respSignature = Signature.getInstance(sigAlgId.getName());
    641             respSignature.initVerify(cert.getPublicKey());
    642             respSignature.update(tbsResponseData);
    643 
    644             if (respSignature.verify(signature)) {
    645                 if (debug != null) {
    646                     debug.println("Verified signature of OCSP Response");
    647                 }
    648                 return true;
    649 
    650             } else {
    651                 if (debug != null) {
    652                     debug.println(
    653                         "Error verifying signature of OCSP Response");
    654                 }
    655                 return false;
    656             }
    657         } catch (InvalidKeyException | NoSuchAlgorithmException |
    658                  SignatureException e)
    659         {
    660             throw new CertPathValidatorException(e);
    661         }
    662     }
    663 
    664     /**
    665      * Returns the SingleResponse of the specified CertId, or null if
    666      * there is no response for that CertId.
    667      */
    668     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      */
    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 
    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          */
    793         @Override public CertStatus getCertStatus() {
    794             return certStatus;
    795         }
    796 
    797         private CertId getCertId() {
    798             return certId;
    799         }
    800 
    801         @Override public Date getRevocationTime() {
    802             return (Date) revocationTime.clone();
    803         }
    804 
    805         @Override public CRLReason getRevocationReason() {
    806             return revocationReason;
    807         }
    808 
    809         @Override
    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          */
    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