1 /*
   2  * Copyright (c) 2003, 2016, 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 java.util.Objects;
  45 import java.util.Set;
  46 import javax.security.auth.x500.X500Principal;
  47 
  48 import sun.security.util.HexDumpEncoder;
  49 import sun.security.action.GetIntegerAction;
  50 import sun.security.x509.*;
  51 import sun.security.util.*;
  52 
  53 /**
  54  * This class is used to process an OCSP response.
  55  * The OCSP Response is defined
  56  * in RFC 2560 and the ASN.1 encoding is as follows:
  57  * <pre>
  58  *
  59  *  OCSPResponse ::= SEQUENCE {
  60  *      responseStatus         OCSPResponseStatus,
  61  *      responseBytes          [0] EXPLICIT ResponseBytes OPTIONAL }
  62  *
  63  *   OCSPResponseStatus ::= ENUMERATED {
  64  *       successful            (0),  --Response has valid confirmations
  65  *       malformedRequest      (1),  --Illegal confirmation request
  66  *       internalError         (2),  --Internal error in issuer
  67  *       tryLater              (3),  --Try again later
  68  *                                   --(4) is not used
  69  *       sigRequired           (5),  --Must sign the request
  70  *       unauthorized          (6)   --Request unauthorized
  71  *   }
  72  *
  73  *   ResponseBytes ::=       SEQUENCE {
  74  *       responseType   OBJECT IDENTIFIER,
  75  *       response       OCTET STRING }
  76  *
  77  *   BasicOCSPResponse       ::= SEQUENCE {
  78  *      tbsResponseData      ResponseData,
  79  *      signatureAlgorithm   AlgorithmIdentifier,
  80  *      signature            BIT STRING,
  81  *      certs                [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
  82  *
  83  *   The value for signature SHALL be computed on the hash of the DER
  84  *   encoding ResponseData.
  85  *
  86  *   ResponseData ::= SEQUENCE {
  87  *      version              [0] EXPLICIT Version DEFAULT v1,
  88  *      responderID              ResponderID,
  89  *      producedAt               GeneralizedTime,
  90  *      responses                SEQUENCE OF SingleResponse,
  91  *      responseExtensions   [1] EXPLICIT Extensions OPTIONAL }
  92  *
  93  *   ResponderID ::= CHOICE {
  94  *      byName               [1] Name,
  95  *      byKey                [2] KeyHash }
  96  *
  97  *   KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
  98  *   (excluding the tag and length fields)
  99  *
 100  *   SingleResponse ::= SEQUENCE {
 101  *      certID                       CertID,
 102  *      certStatus                   CertStatus,
 103  *      thisUpdate                   GeneralizedTime,
 104  *      nextUpdate         [0]       EXPLICIT GeneralizedTime OPTIONAL,
 105  *      singleExtensions   [1]       EXPLICIT Extensions OPTIONAL }
 106  *
 107  *   CertStatus ::= CHOICE {
 108  *       good        [0]     IMPLICIT NULL,
 109  *       revoked     [1]     IMPLICIT RevokedInfo,
 110  *       unknown     [2]     IMPLICIT UnknownInfo }
 111  *
 112  *   RevokedInfo ::= SEQUENCE {
 113  *       revocationTime              GeneralizedTime,
 114  *       revocationReason    [0]     EXPLICIT CRLReason OPTIONAL }
 115  *
 116  *   UnknownInfo ::= NULL -- this can be replaced with an enumeration
 117  *
 118  * </pre>
 119  *
 120  * @author      Ram Marti
 121  */
 122 
 123 public final class OCSPResponse {
 124 
 125     public enum ResponseStatus {
 126         SUCCESSFUL,            // Response has valid confirmations
 127         MALFORMED_REQUEST,     // Illegal request
 128         INTERNAL_ERROR,        // Internal error in responder
 129         TRY_LATER,             // Try again later
 130         UNUSED,                // is not used
 131         SIG_REQUIRED,          // Must sign the request
 132         UNAUTHORIZED           // Request unauthorized
 133     };
 134     private static final ResponseStatus[] rsvalues = ResponseStatus.values();
 135 
 136     private static final Debug debug = Debug.getInstance("certpath");
 137     private static final boolean dump = debug != null && Debug.isOn("ocsp");
 138     private static final ObjectIdentifier OCSP_BASIC_RESPONSE_OID =
 139         ObjectIdentifier.newInternal(new int[] { 1, 3, 6, 1, 5, 5, 7, 48, 1, 1});
 140     private static final int CERT_STATUS_GOOD = 0;
 141     private static final int CERT_STATUS_REVOKED = 1;
 142     private static final int CERT_STATUS_UNKNOWN = 2;
 143 
 144     // ResponderID CHOICE tags
 145     private static final int NAME_TAG = 1;
 146     private static final int KEY_TAG = 2;
 147 
 148     // Object identifier for the OCSPSigning key purpose
 149     private static final String KP_OCSP_SIGNING_OID = "1.3.6.1.5.5.7.3.9";
 150 
 151     // Default maximum clock skew in milliseconds (15 minutes)
 152     // allowed when checking validity of OCSP responses
 153     private static final int DEFAULT_MAX_CLOCK_SKEW = 900000;
 154 
 155     /**
 156      * Integer value indicating the maximum allowable clock skew,
 157      * in milliseconds, to be used for the OCSP check.
 158      */
 159     private static final int MAX_CLOCK_SKEW = initializeClockSkew();
 160 
 161     /**
 162      * Initialize the maximum allowable clock skew by getting the OCSP
 163      * clock skew system property. If the property has not been set, or if its
 164      * value is negative, set the skew to the default.
 165      */
 166     private static int initializeClockSkew() {
 167         Integer tmp = java.security.AccessController.doPrivileged(
 168                 new GetIntegerAction("com.sun.security.ocsp.clockSkew"));
 169         if (tmp == null || tmp < 0) {
 170             return DEFAULT_MAX_CLOCK_SKEW;
 171         }
 172         // Convert to milliseconds, as the system property will be
 173         // specified in seconds
 174         return tmp * 1000;
 175     }
 176 
 177     // an array of all of the CRLReasons (used in SingleResponse)
 178     private static final CRLReason[] values = CRLReason.values();
 179 
 180     private final ResponseStatus responseStatus;
 181     private final Map<CertId, SingleResponse> singleResponseMap;
 182     private final AlgorithmId sigAlgId;
 183     private final byte[] signature;
 184     private final byte[] tbsResponseData;
 185     private final byte[] responseNonce;
 186     private List<X509CertImpl> certs;
 187     private X509CertImpl signerCert = null;
 188     private final ResponderId respId;
 189     private Date producedAtDate = null;
 190     private final Map<String, java.security.cert.Extension> responseExtensions;
 191 
 192     /*
 193      * Create an OCSP response from its ASN.1 DER encoding.
 194      *
 195      * @param bytes The DER-encoded bytes for an OCSP response
 196      */
 197     public OCSPResponse(byte[] bytes) throws IOException {
 198         if (dump) {
 199             HexDumpEncoder hexEnc = new HexDumpEncoder();
 200             debug.println("OCSPResponse bytes...\n\n" +
 201                 hexEnc.encode(bytes) + "\n");
 202         }
 203         DerValue der = new DerValue(bytes);
 204         if (der.tag != DerValue.tag_Sequence) {
 205             throw new IOException("Bad encoding in OCSP response: " +
 206                 "expected ASN.1 SEQUENCE tag.");
 207         }
 208         DerInputStream derIn = der.getData();
 209 
 210         // responseStatus
 211         int status = derIn.getEnumerated();
 212         if (status >= 0 && status < rsvalues.length) {
 213             responseStatus = rsvalues[status];
 214         } else {
 215             // unspecified responseStatus
 216             throw new IOException("Unknown OCSPResponse status: " + status);
 217         }
 218         if (debug != null) {
 219             debug.println("OCSP response status: " + responseStatus);
 220         }
 221         if (responseStatus != ResponseStatus.SUCCESSFUL) {
 222             // no need to continue, responseBytes are not set.
 223             singleResponseMap = Collections.emptyMap();
 224             certs = new ArrayList<X509CertImpl>();
 225             sigAlgId = null;
 226             signature = null;
 227             tbsResponseData = null;
 228             responseNonce = null;
 229             responseExtensions = Collections.emptyMap();
 230             respId = null;
 231             return;
 232         }
 233 
 234         // responseBytes
 235         der = derIn.getDerValue();
 236         if (!der.isContextSpecific((byte)0)) {
 237             throw new IOException("Bad encoding in responseBytes element " +
 238                 "of OCSP response: expected ASN.1 context specific tag 0.");
 239         }
 240         DerValue tmp = der.data.getDerValue();
 241         if (tmp.tag != DerValue.tag_Sequence) {
 242             throw new IOException("Bad encoding in responseBytes element " +
 243                 "of OCSP response: expected ASN.1 SEQUENCE tag.");
 244         }
 245 
 246         // responseType
 247         derIn = tmp.data;
 248         ObjectIdentifier responseType = derIn.getOID();
 249         if (responseType.equals((Object)OCSP_BASIC_RESPONSE_OID)) {
 250             if (debug != null) {
 251                 debug.println("OCSP response type: basic");
 252             }
 253         } else {
 254             if (debug != null) {
 255                 debug.println("OCSP response type: " + responseType);
 256             }
 257             throw new IOException("Unsupported OCSP response type: " +
 258                                   responseType);
 259         }
 260 
 261         // BasicOCSPResponse
 262         DerInputStream basicOCSPResponse =
 263             new DerInputStream(derIn.getOctetString());
 264 
 265         DerValue[] seqTmp = basicOCSPResponse.getSequence(2);
 266         if (seqTmp.length < 3) {
 267             throw new IOException("Unexpected BasicOCSPResponse value");
 268         }
 269 
 270         DerValue responseData = seqTmp[0];
 271 
 272         // Need the DER encoded ResponseData to verify the signature later
 273         tbsResponseData = seqTmp[0].toByteArray();
 274 
 275         // tbsResponseData
 276         if (responseData.tag != DerValue.tag_Sequence) {
 277             throw new IOException("Bad encoding in tbsResponseData " +
 278                 "element of OCSP response: expected ASN.1 SEQUENCE tag.");
 279         }
 280         DerInputStream seqDerIn = responseData.data;
 281         DerValue seq = seqDerIn.getDerValue();
 282 
 283         // version
 284         if (seq.isContextSpecific((byte)0)) {
 285             // seq[0] is version
 286             if (seq.isConstructed() && seq.isContextSpecific()) {
 287                 //System.out.println ("version is available");
 288                 seq = seq.data.getDerValue();
 289                 int version = seq.getInteger();
 290                 if (seq.data.available() != 0) {
 291                     throw new IOException("Bad encoding in version " +
 292                         " element of OCSP response: bad format");
 293                 }
 294                 seq = seqDerIn.getDerValue();
 295             }
 296         }
 297 
 298         // responderID
 299         respId = new ResponderId(seq.toByteArray());
 300         if (debug != null) {
 301             debug.println("Responder ID: " + respId);
 302         }
 303 
 304         // producedAt
 305         seq = seqDerIn.getDerValue();
 306         producedAtDate = seq.getGeneralizedTime();
 307         if (debug != null) {
 308             debug.println("OCSP response produced at: " + producedAtDate);
 309         }
 310 
 311         // responses
 312         DerValue[] singleResponseDer = seqDerIn.getSequence(1);
 313         singleResponseMap = new HashMap<>(singleResponseDer.length);
 314         if (debug != null) {
 315             debug.println("OCSP number of SingleResponses: "
 316                           + singleResponseDer.length);
 317         }
 318         for (DerValue srDer : singleResponseDer) {
 319             SingleResponse singleResponse = new SingleResponse(srDer);
 320             singleResponseMap.put(singleResponse.getCertId(), singleResponse);
 321         }
 322 
 323         // responseExtensions
 324         Map<String, java.security.cert.Extension> tmpExtMap = new HashMap<>();
 325         if (seqDerIn.available() > 0) {
 326             seq = seqDerIn.getDerValue();
 327             if (seq.isContextSpecific((byte)1)) {
 328                 tmpExtMap = parseExtensions(seq);
 329             }
 330         }
 331         responseExtensions = tmpExtMap;
 332 
 333         // Attach the nonce value if found in the extension map
 334         Extension nonceExt = (Extension)tmpExtMap.get(
 335                 PKIXExtensions.OCSPNonce_Id.toString());
 336         responseNonce = (nonceExt != null) ?
 337                 nonceExt.getExtensionValue() : null;
 338         if (debug != null && responseNonce != null) {
 339             debug.println("Response nonce: " + Arrays.toString(responseNonce));
 340         }
 341 
 342         // signatureAlgorithmId
 343         sigAlgId = AlgorithmId.parse(seqTmp[1]);
 344 
 345         // signature
 346         signature = seqTmp[2].getBitString();
 347 
 348         // if seq[3] is available , then it is a sequence of certificates
 349         if (seqTmp.length > 3) {
 350             // certs are available
 351             DerValue seqCert = seqTmp[3];
 352             if (!seqCert.isContextSpecific((byte)0)) {
 353                 throw new IOException("Bad encoding in certs element of " +
 354                     "OCSP response: expected ASN.1 context specific tag 0.");
 355             }
 356             DerValue[] derCerts = seqCert.getData().getSequence(3);
 357             certs = new ArrayList<X509CertImpl>(derCerts.length);
 358             try {
 359                 for (int i = 0; i < derCerts.length; i++) {
 360                     X509CertImpl cert =
 361                         new X509CertImpl(derCerts[i].toByteArray());
 362                     certs.add(cert);
 363 
 364                     if (debug != null) {
 365                         debug.println("OCSP response cert #" + (i + 1) + ": " +
 366                             cert.getSubjectX500Principal());
 367                     }
 368                 }
 369             } catch (CertificateException ce) {
 370                 throw new IOException("Bad encoding in X509 Certificate", ce);
 371             }
 372         } else {
 373             certs = new ArrayList<X509CertImpl>();
 374         }
 375     }
 376 
 377     void verify(List<CertId> certIds, IssuerInfo issuerInfo,
 378             X509Certificate responderCert, Date date, byte[] nonce)
 379         throws CertPathValidatorException
 380     {
 381         switch (responseStatus) {
 382             case SUCCESSFUL:
 383                 break;
 384             case TRY_LATER:
 385             case INTERNAL_ERROR:
 386                 throw new CertPathValidatorException(
 387                     "OCSP response error: " + responseStatus, null, null, -1,
 388                     BasicReason.UNDETERMINED_REVOCATION_STATUS);
 389             case UNAUTHORIZED:
 390             default:
 391                 throw new CertPathValidatorException("OCSP response error: " +
 392                                                      responseStatus);
 393         }
 394 
 395         // Check that the response includes a response for all of the
 396         // certs that were supplied in the request
 397         for (CertId certId : certIds) {
 398             SingleResponse sr = getSingleResponse(certId);
 399             if (sr == null) {
 400                 if (debug != null) {
 401                     debug.println("No response found for CertId: " + certId);
 402                 }
 403                 throw new CertPathValidatorException(
 404                     "OCSP response does not include a response for a " +
 405                     "certificate supplied in the OCSP request");
 406             }
 407             if (debug != null) {
 408                 debug.println("Status of certificate (with serial number " +
 409                     certId.getSerialNumber() + ") is: " + sr.getCertStatus());
 410             }
 411         }
 412 
 413         // Locate the signer cert
 414         if (signerCert == null) {
 415             // Add the Issuing CA cert and/or Trusted Responder cert to the list
 416             // of certs from the OCSP response
 417             try {
 418                 if (issuerInfo.getCertificate() != null) {
 419                     certs.add(X509CertImpl.toImpl(issuerInfo.getCertificate()));
 420                 }
 421                 if (responderCert != null) {
 422                     certs.add(X509CertImpl.toImpl(responderCert));
 423                 }
 424             } catch (CertificateException ce) {
 425                 throw new CertPathValidatorException(
 426                     "Invalid issuer or trusted responder certificate", ce);
 427             }
 428 
 429             if (respId.getType() == ResponderId.Type.BY_NAME) {
 430                 X500Principal rName = respId.getResponderName();
 431                 for (X509CertImpl cert : certs) {
 432                     if (cert.getSubjectX500Principal().equals(rName)) {
 433                         signerCert = cert;
 434                         break;
 435                     }
 436                 }
 437             } else if (respId.getType() == ResponderId.Type.BY_KEY) {
 438                 KeyIdentifier ridKeyId = respId.getKeyIdentifier();
 439                 for (X509CertImpl cert : certs) {
 440                     // Match responder's key identifier against the cert's SKID
 441                     // This will match if the SKID is encoded using the 160-bit
 442                     // SHA-1 hash method as defined in RFC 5280.
 443                     KeyIdentifier certKeyId = cert.getSubjectKeyId();
 444                     if (certKeyId != null && ridKeyId.equals(certKeyId)) {
 445                         signerCert = cert;
 446                         break;
 447                     } else {
 448                         // The certificate does not have a SKID or may have
 449                         // been using a different algorithm (ex: see RFC 7093).
 450                         // Check if the responder's key identifier matches
 451                         // against a newly generated key identifier of the
 452                         // cert's public key using the 160-bit SHA-1 method.
 453                         try {
 454                             certKeyId = new KeyIdentifier(cert.getPublicKey());
 455                         } catch (IOException e) {
 456                             // ignore
 457                         }
 458                         if (ridKeyId.equals(certKeyId)) {
 459                             signerCert = cert;
 460                             break;
 461                         }
 462                     }
 463                 }
 464             }
 465         }
 466 
 467         // Check whether the signer cert returned by the responder is trusted
 468         if (signerCert != null) {
 469             // Check if the response is signed by the issuing CA
 470             if (signerCert.getSubjectX500Principal().equals(
 471                     issuerInfo.getName()) &&
 472                     signerCert.getPublicKey().equals(
 473                             issuerInfo.getPublicKey())) {
 474                 if (debug != null) {
 475                     debug.println("OCSP response is signed by the target's " +
 476                         "Issuing CA");
 477                 }
 478                 // cert is trusted, now verify the signed response
 479 
 480             // Check if the response is signed by a trusted responder
 481             } else if (signerCert.equals(responderCert)) {
 482                 if (debug != null) {
 483                     debug.println("OCSP response is signed by a Trusted " +
 484                         "Responder");
 485                 }
 486                 // cert is trusted, now verify the signed response
 487 
 488             // Check if the response is signed by an authorized responder
 489             } else if (signerCert.getIssuerX500Principal().equals(
 490                     issuerInfo.getName())) {
 491 
 492                 // Check for the OCSPSigning key purpose
 493                 try {
 494                     List<String> keyPurposes = signerCert.getExtendedKeyUsage();
 495                     if (keyPurposes == null ||
 496                         !keyPurposes.contains(KP_OCSP_SIGNING_OID)) {
 497                         throw new CertPathValidatorException(
 498                             "Responder's certificate not valid for signing " +
 499                             "OCSP responses");
 500                     }
 501                 } catch (CertificateParsingException cpe) {
 502                     // assume cert is not valid for signing
 503                     throw new CertPathValidatorException(
 504                         "Responder's certificate not valid for signing " +
 505                         "OCSP responses", cpe);
 506                 }
 507 
 508                 // Check algorithm constraints specified in security property
 509                 // "jdk.certpath.disabledAlgorithms".
 510                 AlgorithmChecker algChecker =
 511                         new AlgorithmChecker(issuerInfo.getAnchor(), date);
 512                 algChecker.init(false);
 513                 algChecker.check(signerCert, Collections.<String>emptySet());
 514 
 515                 // check the validity
 516                 try {
 517                     if (date == null) {
 518                         signerCert.checkValidity();
 519                     } else {
 520                         signerCert.checkValidity(date);
 521                     }
 522                 } catch (CertificateException e) {
 523                     throw new CertPathValidatorException(
 524                         "Responder's certificate not within the " +
 525                         "validity period", e);
 526                 }
 527 
 528                 // check for revocation
 529                 //
 530                 // A CA may specify that an OCSP client can trust a
 531                 // responder for the lifetime of the responder's
 532                 // certificate. The CA does so by including the
 533                 // extension id-pkix-ocsp-nocheck.
 534                 //
 535                 Extension noCheck =
 536                     signerCert.getExtension(PKIXExtensions.OCSPNoCheck_Id);
 537                 if (noCheck != null) {
 538                     if (debug != null) {
 539                         debug.println("Responder's certificate includes " +
 540                             "the extension id-pkix-ocsp-nocheck.");
 541                     }
 542                 } else {
 543                     // we should do the revocation checking of the
 544                     // authorized responder in a future update.
 545                 }
 546 
 547                 // verify the signature
 548                 try {
 549                     signerCert.verify(issuerInfo.getPublicKey());
 550                     if (debug != null) {
 551                         debug.println("OCSP response is signed by an " +
 552                             "Authorized Responder");
 553                     }
 554                     // cert is trusted, now verify the signed response
 555 
 556                 } catch (GeneralSecurityException e) {
 557                     signerCert = null;
 558                 }
 559             } else {
 560                 throw new CertPathValidatorException(
 561                     "Responder's certificate is not authorized to sign " +
 562                     "OCSP responses");
 563             }
 564         }
 565 
 566         // Confirm that the signed response was generated using the public
 567         // key from the trusted responder cert
 568         if (signerCert != null) {
 569             // Check algorithm constraints specified in security property
 570             // "jdk.certpath.disabledAlgorithms".
 571             AlgorithmChecker.check(signerCert.getPublicKey(), sigAlgId);
 572 
 573             if (!verifySignature(signerCert)) {
 574                 throw new CertPathValidatorException(
 575                     "Error verifying OCSP Response's signature");
 576             }
 577         } else {
 578             // Need responder's cert in order to verify the signature
 579             throw new CertPathValidatorException(
 580                 "Unable to verify OCSP Response's signature");
 581         }
 582 
 583         if (nonce != null) {
 584             if (responseNonce != null && !Arrays.equals(nonce, responseNonce)) {
 585                 throw new CertPathValidatorException("Nonces don't match");
 586             }
 587         }
 588 
 589         // Check freshness of OCSPResponse
 590         long now = (date == null) ? System.currentTimeMillis() : date.getTime();
 591         Date nowPlusSkew = new Date(now + MAX_CLOCK_SKEW);
 592         Date nowMinusSkew = new Date(now - MAX_CLOCK_SKEW);
 593         for (SingleResponse sr : singleResponseMap.values()) {
 594             if (debug != null) {
 595                 String until = "";
 596                 if (sr.nextUpdate != null) {
 597                     until = " until " + sr.nextUpdate;
 598                 }
 599                 debug.println("OCSP response validity interval is from " +
 600                         sr.thisUpdate + until);
 601                 debug.println("Checking validity of OCSP response on: " +
 602                         new Date(now));
 603             }
 604 
 605             // Check that the test date is within the validity interval:
 606             //   [ thisUpdate - MAX_CLOCK_SKEW,
 607             //     MAX(thisUpdate, nextUpdate) + MAX_CLOCK_SKEW ]
 608             if (nowPlusSkew.before(sr.thisUpdate) ||
 609                     nowMinusSkew.after(
 610                     sr.nextUpdate != null ? sr.nextUpdate : sr.thisUpdate))
 611             {
 612                 throw new CertPathValidatorException(
 613                                       "Response is unreliable: its validity " +
 614                                       "interval is out-of-date");
 615             }
 616         }
 617     }
 618 
 619     /**
 620      * Returns the OCSP ResponseStatus.
 621      *
 622      * @return the {@code ResponseStatus} for this OCSP response
 623      */
 624     public ResponseStatus getResponseStatus() {
 625         return responseStatus;
 626     }
 627 
 628     /*
 629      * Verify the signature of the OCSP response.
 630      */
 631     private boolean verifySignature(X509Certificate cert)
 632         throws CertPathValidatorException {
 633 
 634         try {
 635             Signature respSignature = Signature.getInstance(sigAlgId.getName());
 636             respSignature.initVerify(cert.getPublicKey());
 637             respSignature.update(tbsResponseData);
 638 
 639             if (respSignature.verify(signature)) {
 640                 if (debug != null) {
 641                     debug.println("Verified signature of OCSP Response");
 642                 }
 643                 return true;
 644 
 645             } else {
 646                 if (debug != null) {
 647                     debug.println(
 648                         "Error verifying signature of OCSP Response");
 649                 }
 650                 return false;
 651             }
 652         } catch (InvalidKeyException | NoSuchAlgorithmException |
 653                  SignatureException e)
 654         {
 655             throw new CertPathValidatorException(e);
 656         }
 657     }
 658 
 659     /**
 660      * Returns the SingleResponse of the specified CertId, or null if
 661      * there is no response for that CertId.
 662      *
 663      * @param certId the {@code CertId} for a {@code SingleResponse} to be
 664      * searched for in the OCSP response.
 665      *
 666      * @return the {@code SingleResponse} for the provided {@code CertId},
 667      * or {@code null} if it is not found.
 668      */
 669     public SingleResponse getSingleResponse(CertId certId) {
 670         return singleResponseMap.get(certId);
 671     }
 672 
 673     /**
 674      * Return a set of all CertIds in this {@code OCSPResponse}
 675      *
 676      * @return an unmodifiable set containing every {@code CertId} in this
 677      *      response.
 678      */
 679     public Set<CertId> getCertIds() {
 680         return Collections.unmodifiableSet(singleResponseMap.keySet());
 681     }
 682 
 683     /*
 684      * Returns the certificate for the authority that signed the OCSP response.
 685      */
 686     X509Certificate getSignerCertificate() {
 687         return signerCert; // set in verify()
 688     }
 689 
 690     /**
 691      * Get the {@code ResponderId} from this {@code OCSPResponse}
 692      *
 693      * @return the {@code ResponderId} from this response or {@code null}
 694      *      if no responder ID is in the body of the response (e.g. a
 695      *      response with a status other than SUCCESS.
 696      */
 697     public ResponderId getResponderId() {
 698         return respId;
 699     }
 700 
 701     /**
 702      * Provide a String representation of an OCSPResponse
 703      *
 704      * @return a human-readable representation of the OCSPResponse
 705      */
 706     @Override
 707     public String toString() {
 708         StringBuilder sb = new StringBuilder();
 709         sb.append("OCSP Response:\n");
 710         sb.append("Response Status: ").append(responseStatus).append("\n");
 711         sb.append("Responder ID: ").append(respId).append("\n");
 712         sb.append("Produced at: ").append(producedAtDate).append("\n");
 713         int count = singleResponseMap.size();
 714         sb.append(count).append(count == 1 ?
 715                 " response:\n" : " responses:\n");
 716         for (SingleResponse sr : singleResponseMap.values()) {
 717             sb.append(sr).append("\n");
 718         }
 719         if (responseExtensions != null && responseExtensions.size() > 0) {
 720             count = responseExtensions.size();
 721             sb.append(count).append(count == 1 ?
 722                     " extension:\n" : " extensions:\n");
 723             for (String extId : responseExtensions.keySet()) {
 724                 sb.append(responseExtensions.get(extId)).append("\n");
 725             }
 726         }
 727 
 728         return sb.toString();
 729     }
 730 
 731     /**
 732      * Build a String-Extension map from DER encoded data.
 733      * @param derVal A {@code DerValue} object built from a SEQUENCE of
 734      *      extensions
 735      *
 736      * @return a {@code Map} using the OID in string form as the keys.  If no
 737      *      extensions are found or an empty SEQUENCE is passed in, then
 738      *      an empty {@code Map} will be returned.
 739      *
 740      * @throws IOException if any decoding errors occur.
 741      */
 742     private static Map<String, java.security.cert.Extension>
 743         parseExtensions(DerValue derVal) throws IOException {
 744         DerValue[] extDer = derVal.data.getSequence(3);
 745         Map<String, java.security.cert.Extension> extMap =
 746                 new HashMap<>(extDer.length);
 747 
 748         for (DerValue extDerVal : extDer) {
 749             Extension ext = new Extension(extDerVal);
 750             if (debug != null) {
 751                 debug.println("Extension: " + ext);
 752             }
 753             // We don't support any extensions yet. Therefore, if it
 754             // is critical we must throw an exception because we
 755             // don't know how to process it.
 756             if (ext.isCritical()) {
 757                 throw new IOException("Unsupported OCSP critical extension: " +
 758                         ext.getExtensionId());
 759             }
 760             extMap.put(ext.getId(), ext);
 761         }
 762 
 763         return extMap;
 764     }
 765 
 766     /*
 767      * A class representing a single OCSP response.
 768      */
 769     public static final class SingleResponse implements OCSP.RevocationStatus {
 770         private final CertId certId;
 771         private final CertStatus certStatus;
 772         private final Date thisUpdate;
 773         private final Date nextUpdate;
 774         private final Date revocationTime;
 775         private final CRLReason revocationReason;
 776         private final Map<String, java.security.cert.Extension> singleExtensions;
 777 
 778         private SingleResponse(DerValue der) throws IOException {
 779             if (der.tag != DerValue.tag_Sequence) {
 780                 throw new IOException("Bad ASN.1 encoding in SingleResponse");
 781             }
 782             DerInputStream tmp = der.data;
 783 
 784             certId = new CertId(tmp.getDerValue().data);
 785             DerValue derVal = tmp.getDerValue();
 786             short tag = (byte)(derVal.tag & 0x1f);
 787             if (tag ==  CERT_STATUS_REVOKED) {
 788                 certStatus = CertStatus.REVOKED;
 789                 revocationTime = derVal.data.getGeneralizedTime();
 790                 if (derVal.data.available() != 0) {
 791                     DerValue dv = derVal.data.getDerValue();
 792                     tag = (byte)(dv.tag & 0x1f);
 793                     if (tag == 0) {
 794                         int reason = dv.data.getEnumerated();
 795                         // if reason out-of-range just leave as UNSPECIFIED
 796                         if (reason >= 0 && reason < values.length) {
 797                             revocationReason = values[reason];
 798                         } else {
 799                             revocationReason = CRLReason.UNSPECIFIED;
 800                         }
 801                     } else {
 802                         revocationReason = CRLReason.UNSPECIFIED;
 803                     }
 804                 } else {
 805                     revocationReason = CRLReason.UNSPECIFIED;
 806                 }
 807                 // RevokedInfo
 808                 if (debug != null) {
 809                     debug.println("Revocation time: " + revocationTime);
 810                     debug.println("Revocation reason: " + revocationReason);
 811                 }
 812             } else {
 813                 revocationTime = null;
 814                 revocationReason = null;
 815                 if (tag == CERT_STATUS_GOOD) {
 816                     certStatus = CertStatus.GOOD;
 817                 } else if (tag == CERT_STATUS_UNKNOWN) {
 818                     certStatus = CertStatus.UNKNOWN;
 819                 } else {
 820                     throw new IOException("Invalid certificate status");
 821                 }
 822             }
 823 
 824             thisUpdate = tmp.getGeneralizedTime();
 825             if (debug != null) {
 826                 debug.println("thisUpdate: " + thisUpdate);
 827             }
 828 
 829             // Parse optional fields like nextUpdate and singleExtensions
 830             Date tmpNextUpdate = null;
 831             Map<String, java.security.cert.Extension> tmpMap = null;
 832 
 833             // Check for the first optional item, it could be nextUpdate
 834             // [CONTEXT 0] or singleExtensions [CONTEXT 1]
 835             if (tmp.available() > 0) {
 836                 derVal = tmp.getDerValue();
 837 
 838                 // nextUpdate processing
 839                 if (derVal.isContextSpecific((byte)0)) {
 840                     tmpNextUpdate = derVal.data.getGeneralizedTime();
 841                     if (debug != null) {
 842                         debug.println("nextUpdate: " + tmpNextUpdate);
 843                     }
 844 
 845                     // If more data exists in the singleResponse, it
 846                     // can only be singleExtensions.  Get this DER value
 847                     // for processing in the next block
 848                     derVal = tmp.available() > 0 ? tmp.getDerValue() : null;
 849                 }
 850 
 851                 // singleExtensions processing
 852                 if (derVal != null) {
 853                     if (derVal.isContextSpecific((byte)1)) {
 854                         tmpMap = parseExtensions(derVal);
 855 
 856                         // There should not be any other items in the
 857                         // singleResponse at this point.
 858                         if (tmp.available() > 0) {
 859                             throw new IOException(tmp.available() +
 860                                 " bytes of additional data in singleResponse");
 861                         }
 862                     } else {
 863                         // Unknown item in the singleResponse
 864                         throw new IOException("Unsupported singleResponse " +
 865                             "item, tag = " + String.format("%02X", derVal.tag));
 866                     }
 867                 }
 868             }
 869 
 870             nextUpdate = tmpNextUpdate;
 871             singleExtensions = (tmpMap != null) ? tmpMap :
 872                     Collections.emptyMap();
 873             if (debug != null) {
 874                 for (java.security.cert.Extension ext :
 875                         singleExtensions.values()) {
 876                    debug.println("singleExtension: " + ext);
 877                 }
 878             }
 879         }
 880 
 881         /*
 882          * Return the certificate's revocation status code
 883          */
 884         @Override
 885         public CertStatus getCertStatus() {
 886             return certStatus;
 887         }
 888 
 889         /**
 890          * Get the Cert ID that this SingleResponse is for.
 891          *
 892          * @return the {@code CertId} for this {@code SingleResponse}
 893          */
 894         public CertId getCertId() {
 895             return certId;
 896         }
 897 
 898         /**
 899          * Get the {@code thisUpdate} field from this {@code SingleResponse}.
 900          *
 901          * @return a {@link Date} object containing the thisUpdate date
 902          */
 903         public Date getThisUpdate() {
 904             return (thisUpdate != null ? (Date) thisUpdate.clone() : null);
 905         }
 906 
 907         /**
 908          * Get the {@code nextUpdate} field from this {@code SingleResponse}.
 909          *
 910          * @return a {@link Date} object containing the nexUpdate date or
 911          * {@code null} if a nextUpdate field is not present in the response.
 912          */
 913         public Date getNextUpdate() {
 914             return (nextUpdate != null ? (Date) nextUpdate.clone() : null);
 915         }
 916 
 917         /**
 918          * Get the {@code revocationTime} field from this
 919          * {@code SingleResponse}.
 920          *
 921          * @return a {@link Date} object containing the revocationTime date or
 922          * {@code null} if the {@code SingleResponse} does not have a status
 923          * of {@code REVOKED}.
 924          */
 925         @Override
 926         public Date getRevocationTime() {
 927             return (revocationTime != null ? (Date) revocationTime.clone() :
 928                     null);
 929         }
 930 
 931         /**
 932          * Get the {@code revocationReason} field for the
 933          * {@code SingleResponse}.
 934          *
 935          * @return a {@link CRLReason} containing the revocation reason, or
 936          * {@code null} if a revocation reason was not provided or the
 937          * response status is not {@code REVOKED}.
 938          */
 939         @Override
 940         public CRLReason getRevocationReason() {
 941             return revocationReason;
 942         }
 943 
 944         /**
 945          * Get the {@code singleExtensions} for this {@code SingleResponse}.
 946          *
 947          * @return a {@link Map} of {@link Extension} objects, keyed by
 948          * their OID value in string form.
 949          */
 950         @Override
 951         public Map<String, java.security.cert.Extension> getSingleExtensions() {
 952             return Collections.unmodifiableMap(singleExtensions);
 953         }
 954 
 955         /**
 956          * Construct a string representation of a single OCSP response.
 957          */
 958         @Override public String toString() {
 959             StringBuilder sb = new StringBuilder();
 960             sb.append("SingleResponse:\n");
 961             sb.append(certId);
 962             sb.append("\nCertStatus: ").append(certStatus).append("\n");
 963             if (certStatus == CertStatus.REVOKED) {
 964                 sb.append("revocationTime is ");
 965                 sb.append(revocationTime).append("\n");
 966                 sb.append("revocationReason is ");
 967                 sb.append(revocationReason).append("\n");
 968             }
 969             sb.append("thisUpdate is ").append(thisUpdate).append("\n");
 970             if (nextUpdate != null) {
 971                 sb.append("nextUpdate is ").append(nextUpdate).append("\n");
 972             }
 973             for (java.security.cert.Extension ext : singleExtensions.values()) {
 974                 sb.append("singleExtension: ");
 975                 sb.append(ext.toString()).append("\n");
 976             }
 977             return sb.toString();
 978         }
 979     }
 980 
 981     /**
 982      * Helper class that allows consumers to pass in issuer information.  This
 983      * will always consist of the issuer's name and public key, but may also
 984      * contain a certificate if the originating data is in that form.  The
 985      * trust anchor for the certificate chain will be included for certpath
 986      * disabled algorithm checking.
 987      */
 988     static final class IssuerInfo {
 989         private final TrustAnchor anchor;
 990         private final X509Certificate certificate;
 991         private final X500Principal name;
 992         private final PublicKey pubKey;
 993 
 994         IssuerInfo(TrustAnchor anchor) {
 995             this(anchor, (anchor != null) ? anchor.getTrustedCert() : null);
 996         }
 997 
 998         IssuerInfo(X509Certificate issuerCert) {
 999             this(null, issuerCert);
1000         }
1001 
1002         IssuerInfo(TrustAnchor anchor, X509Certificate issuerCert) {
1003             if (anchor == null && issuerCert == null) {
1004                 throw new NullPointerException("TrustAnchor and issuerCert " +
1005                         "cannot be null");
1006             }
1007             this.anchor = anchor;
1008             if (issuerCert != null) {
1009                 name = issuerCert.getSubjectX500Principal();
1010                 pubKey = issuerCert.getPublicKey();
1011                 certificate = issuerCert;
1012             } else {
1013                 name = anchor.getCA();
1014                 pubKey = anchor.getCAPublicKey();
1015                 certificate = anchor.getTrustedCert();
1016             }
1017         }
1018 
1019         /**
1020          * Get the certificate in this IssuerInfo if present.
1021          *
1022          * @return the {@code X509Certificate} used to create this IssuerInfo
1023          * object, or {@code null} if a certificate was not used in its
1024          * creation.
1025          */
1026         X509Certificate getCertificate() {
1027             return certificate;
1028         }
1029 
1030         /**
1031          * Get the name of this issuer.
1032          *
1033          * @return an {@code X500Principal} corresponding to this issuer's
1034          * name.  If derived from an issuer's {@code X509Certificate} this
1035          * would be equivalent to the certificate subject name.
1036          */
1037         X500Principal getName() {
1038             return name;
1039         }
1040 
1041         /**
1042          * Get the public key for this issuer.
1043          *
1044          * @return a {@code PublicKey} for this issuer.
1045          */
1046         PublicKey getPublicKey() {
1047             return pubKey;
1048         }
1049 
1050         /**
1051          * Get the TrustAnchor for the certificate chain.
1052          *
1053          * @return a {@code TrustAnchor}.
1054          */
1055         TrustAnchor getAnchor() {
1056             return anchor;
1057         }
1058 
1059         /**
1060          * Create a string representation of this IssuerInfo.
1061          *
1062          * @return a {@code String} form of this IssuerInfo object.
1063          */
1064         @Override
1065         public String toString() {
1066             StringBuilder sb = new StringBuilder();
1067             sb.append("Issuer Info:\n");
1068             sb.append("Name: ").append(name.toString()).append("\n");
1069             sb.append("Public Key:\n").append(pubKey.toString()).append("\n");
1070             return sb.toString();
1071         }
1072     }
1073 }