Home | History | Annotate | Download | only in certpath
      1 /*
      2  * Copyright (c) 2003, 2014, 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, in seconds,
    155      * 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         // Check freshness of OCSPResponse
    590         if (nonce != null) {
    591             if (responseNonce != null && !Arrays.equals(nonce, responseNonce)) {
    592                 throw new CertPathValidatorException("Nonces don't match");
    593             }
    594         }
    595 
    596         long now = (date == null) ? System.currentTimeMillis() : date.getTime();
    597         Date nowPlusSkew = new Date(now + MAX_CLOCK_SKEW);
    598         Date nowMinusSkew = new Date(now - MAX_CLOCK_SKEW);
    599         for (SingleResponse sr : singleResponseMap.values()) {
    600             if (debug != null) {
    601                 String until = "";
    602                 if (sr.nextUpdate != null) {
    603                     until = " until " + sr.nextUpdate;
    604                 }
    605                 debug.println("Response's validity interval is from " +
    606                               sr.thisUpdate + until);
    607             }
    608 
    609             // Check that the test date is within the validity interval
    610             if ((sr.thisUpdate != null && nowPlusSkew.before(sr.thisUpdate)) ||
    611                 (sr.nextUpdate != null && nowMinusSkew.after(sr.nextUpdate)))
    612             {
    613                 throw new CertPathValidatorException(
    614                                       "Response is unreliable: its validity " +
    615                                       "interval is out-of-date");
    616             }
    617         }
    618     }
    619 
    620     /**
    621      * Returns the OCSP ResponseStatus.
    622      */
    623     ResponseStatus getResponseStatus() {
    624         return responseStatus;
    625     }
    626 
    627     /*
    628      * Verify the signature of the OCSP response.
    629      */
    630     private boolean verifySignature(X509Certificate cert)
    631         throws CertPathValidatorException {
    632 
    633         try {
    634             Signature respSignature = Signature.getInstance(sigAlgId.getName());
    635             respSignature.initVerify(cert.getPublicKey());
    636             respSignature.update(tbsResponseData);
    637 
    638             if (respSignature.verify(signature)) {
    639                 if (debug != null) {
    640                     debug.println("Verified signature of OCSP Response");
    641                 }
    642                 return true;
    643 
    644             } else {
    645                 if (debug != null) {
    646                     debug.println(
    647                         "Error verifying signature of OCSP Response");
    648                 }
    649                 return false;
    650             }
    651         } catch (InvalidKeyException | NoSuchAlgorithmException |
    652                  SignatureException e)
    653         {
    654             throw new CertPathValidatorException(e);
    655         }
    656     }
    657 
    658     /**
    659      * Returns the SingleResponse of the specified CertId, or null if
    660      * there is no response for that CertId.
    661      */
    662     SingleResponse getSingleResponse(CertId certId) {
    663         return singleResponseMap.get(certId);
    664     }
    665 
    666     /*
    667      * Returns the certificate for the authority that signed the OCSP response.
    668      */
    669     X509Certificate getSignerCertificate() {
    670         return signerCert; // set in verify()
    671     }
    672 
    673     /*
    674      * A class representing a single OCSP response.
    675      */
    676     final static class SingleResponse implements OCSP.RevocationStatus {
    677         private final CertId certId;
    678         private final CertStatus certStatus;
    679         private final Date thisUpdate;
    680         private final Date nextUpdate;
    681         private final Date revocationTime;
    682         private final CRLReason revocationReason;
    683         private final Map<String, java.security.cert.Extension> singleExtensions;
    684 
    685         private SingleResponse(DerValue der) throws IOException {
    686             if (der.tag != DerValue.tag_Sequence) {
    687                 throw new IOException("Bad ASN.1 encoding in SingleResponse");
    688             }
    689             DerInputStream tmp = der.data;
    690 
    691             certId = new CertId(tmp.getDerValue().data);
    692             DerValue derVal = tmp.getDerValue();
    693             short tag = (byte)(derVal.tag & 0x1f);
    694             if (tag ==  CERT_STATUS_REVOKED) {
    695                 certStatus = CertStatus.REVOKED;
    696                 revocationTime = derVal.data.getGeneralizedTime();
    697                 if (derVal.data.available() != 0) {
    698                     DerValue dv = derVal.data.getDerValue();
    699                     tag = (byte)(dv.tag & 0x1f);
    700                     if (tag == 0) {
    701                         int reason = dv.data.getEnumerated();
    702                         // if reason out-of-range just leave as UNSPECIFIED
    703                         if (reason >= 0 && reason < values.length) {
    704                             revocationReason = values[reason];
    705                         } else {
    706                             revocationReason = CRLReason.UNSPECIFIED;
    707                         }
    708                     } else {
    709                         revocationReason = CRLReason.UNSPECIFIED;
    710                     }
    711                 } else {
    712                     revocationReason = CRLReason.UNSPECIFIED;
    713                 }
    714                 // RevokedInfo
    715                 if (debug != null) {
    716                     debug.println("Revocation time: " + revocationTime);
    717                     debug.println("Revocation reason: " + revocationReason);
    718                 }
    719             } else {
    720                 revocationTime = null;
    721                 revocationReason = CRLReason.UNSPECIFIED;
    722                 if (tag == CERT_STATUS_GOOD) {
    723                     certStatus = CertStatus.GOOD;
    724                 } else if (tag == CERT_STATUS_UNKNOWN) {
    725                     certStatus = CertStatus.UNKNOWN;
    726                 } else {
    727                     throw new IOException("Invalid certificate status");
    728                 }
    729             }
    730 
    731             thisUpdate = tmp.getGeneralizedTime();
    732 
    733             if (tmp.available() == 0)  {
    734                 // we are done
    735                 nextUpdate = null;
    736             } else {
    737                 derVal = tmp.getDerValue();
    738                 tag = (byte)(derVal.tag & 0x1f);
    739                 if (tag == 0) {
    740                     // next update
    741                     nextUpdate = derVal.data.getGeneralizedTime();
    742 
    743                     if (tmp.available() == 0)  {
    744                         // we are done
    745                     } else {
    746                         derVal = tmp.getDerValue();
    747                         tag = (byte)(derVal.tag & 0x1f);
    748                     }
    749                 } else {
    750                     nextUpdate = null;
    751                 }
    752             }
    753             // singleExtensions
    754             if (tmp.available() > 0) {
    755                 derVal = tmp.getDerValue();
    756                 if (derVal.isContextSpecific((byte)1)) {
    757                     DerValue[] singleExtDer = derVal.data.getSequence(3);
    758                     singleExtensions =
    759                         new HashMap<String, java.security.cert.Extension>
    760                             (singleExtDer.length);
    761                     for (int i = 0; i < singleExtDer.length; i++) {
    762                         Extension ext = new Extension(singleExtDer[i]);
    763                         if (debug != null) {
    764                             debug.println("OCSP single extension: " + ext);
    765                         }
    766                         // We don't support any extensions yet. Therefore, if it
    767                         // is critical we must throw an exception because we
    768                         // don't know how to process it.
    769                         if (ext.isCritical()) {
    770                             throw new IOException(
    771                                 "Unsupported OCSP critical extension: " +
    772                                 ext.getExtensionId());
    773                         }
    774                         singleExtensions.put(ext.getId(), ext);
    775                     }
    776                 } else {
    777                     singleExtensions = Collections.emptyMap();
    778                 }
    779             } else {
    780                 singleExtensions = Collections.emptyMap();
    781             }
    782         }
    783 
    784         /*
    785          * Return the certificate's revocation status code
    786          */
    787         @Override public CertStatus getCertStatus() {
    788             return certStatus;
    789         }
    790 
    791         private CertId getCertId() {
    792             return certId;
    793         }
    794 
    795         @Override public Date getRevocationTime() {
    796             return (Date) revocationTime.clone();
    797         }
    798 
    799         @Override public CRLReason getRevocationReason() {
    800             return revocationReason;
    801         }
    802 
    803         @Override
    804         public Map<String, java.security.cert.Extension> getSingleExtensions() {
    805             return Collections.unmodifiableMap(singleExtensions);
    806         }
    807 
    808         /**
    809          * Construct a string representation of a single OCSP response.
    810          */
    811         @Override public String toString() {
    812             StringBuilder sb = new StringBuilder();
    813             sb.append("SingleResponse:  \n");
    814             sb.append(certId);
    815             sb.append("\nCertStatus: "+ certStatus + "\n");
    816             if (certStatus == CertStatus.REVOKED) {
    817                 sb.append("revocationTime is " + revocationTime + "\n");
    818                 sb.append("revocationReason is " + revocationReason + "\n");
    819             }
    820             sb.append("thisUpdate is " + thisUpdate + "\n");
    821             if (nextUpdate != null) {
    822                 sb.append("nextUpdate is " + nextUpdate + "\n");
    823             }
    824             return sb.toString();
    825         }
    826     }
    827 }
    828