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