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: ").append(certStatus).append('\n');
 816             if (certStatus == CertStatus.REVOKED) {
 817                 sb.append("revocationTime is ").append(revocationTime).append('\n');
 818                 sb.append("revocationReason is ").append(revocationReason).append('\n');
 819             }
 820             sb.append("thisUpdate is ").append(thisUpdate).append('\n');
 821             if (nextUpdate != null) {
 822                 sb.append("nextUpdate is ").append(nextUpdate).append('\n');
 823             }
 824             return sb.toString();
 825         }
 826     }
 827 }