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 = new AlgorithmChecker(
 511                         new TrustAnchor(issuerInfo.getName(),
 512                                 issuerInfo.getPublicKey(), null));
 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);
 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.
 986      */
 987     static final class IssuerInfo {
 988         private final X509Certificate certificate;
 989         private final X500Principal name;
 990         private final PublicKey pubKey;
 991 
 992         IssuerInfo(X509Certificate issuerCert) {
 993             certificate = Objects.requireNonNull(issuerCert,
 994                     "Constructor requires non-null certificate");
 995             name = certificate.getSubjectX500Principal();
 996             pubKey = certificate.getPublicKey();
 997         }
 998 
 999         IssuerInfo(X500Principal subjectName, PublicKey key) {
1000             certificate = null;
1001             name = Objects.requireNonNull(subjectName,
1002                     "Constructor requires non-null subject");
1003             pubKey = Objects.requireNonNull(key,
1004                     "Constructor requires non-null public key");
1005         }
1006 
1007         IssuerInfo(TrustAnchor anchor) {
1008             certificate = anchor.getTrustedCert();
1009             if (certificate != null) {
1010                 name = certificate.getSubjectX500Principal();
1011                 pubKey = certificate.getPublicKey();
1012             } else {
1013                 name = anchor.getCA();
1014                 pubKey = anchor.getCAPublicKey();
1015             }
1016         }
1017 
1018         /**
1019          * Get the certificate in this IssuerInfo if present.
1020          *
1021          * @return the {@code X509Certificate} used to create this IssuerInfo
1022          * object, or {@code null} if a certificate was not used in its
1023          * creation.
1024          */
1025         X509Certificate getCertificate() {
1026             return certificate;
1027         }
1028 
1029         /**
1030          * Get the name of this issuer.
1031          *
1032          * @return an {@code X500Principal} corresponding to this issuer's
1033          * name.  If derived from an issuer's {@code X509Certificate} this
1034          * would be equivalent to the certificate subject name.
1035          */
1036         X500Principal getName() {
1037             return name;
1038         }
1039 
1040         /**
1041          * Get the public key for this issuer.
1042          *
1043          * @return a {@code PublicKey} for this issuer.
1044          */
1045         PublicKey getPublicKey() {
1046             return pubKey;
1047         }
1048 
1049         /**
1050          * Create a string representation of this IssuerInfo.
1051          *
1052          * @return a {@code String} form of this IssuerInfo object.
1053          */
1054         @Override
1055         public String toString() {
1056             StringBuilder sb = new StringBuilder();
1057             sb.append("Issuer Info:\n");
1058             sb.append("Name: ").append(name.toString()).append("\n");
1059             sb.append("Public Key:\n").append(pubKey.toString()).append("\n");
1060             return sb.toString();
1061         }
1062     }
1063 }