Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/provider/certpath/OCSPResponse.java
38923 views
/*1* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.provider.certpath;2627import java.io.*;28import java.security.*;29import java.security.cert.CertificateException;30import java.security.cert.CertificateParsingException;31import java.security.cert.CertPathValidatorException;32import java.security.cert.CertPathValidatorException.BasicReason;33import java.security.cert.CRLReason;34import java.security.cert.TrustAnchor;35import java.security.cert.X509Certificate;36import java.util.ArrayList;37import java.util.Arrays;38import java.util.Collections;39import java.util.Date;40import java.util.HashMap;41import java.util.List;42import java.util.Map;43import java.util.Set;44import javax.security.auth.x500.X500Principal;4546import sun.misc.HexDumpEncoder;47import sun.security.action.GetIntegerAction;48import sun.security.x509.*;49import sun.security.util.*;5051/**52* This class is used to process an OCSP response.53* The OCSP Response is defined54* in RFC 2560 and the ASN.1 encoding is as follows:55* <pre>56*57* OCSPResponse ::= SEQUENCE {58* responseStatus OCSPResponseStatus,59* responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }60*61* OCSPResponseStatus ::= ENUMERATED {62* successful (0), --Response has valid confirmations63* malformedRequest (1), --Illegal confirmation request64* internalError (2), --Internal error in issuer65* tryLater (3), --Try again later66* --(4) is not used67* sigRequired (5), --Must sign the request68* unauthorized (6) --Request unauthorized69* }70*71* ResponseBytes ::= SEQUENCE {72* responseType OBJECT IDENTIFIER,73* response OCTET STRING }74*75* BasicOCSPResponse ::= SEQUENCE {76* tbsResponseData ResponseData,77* signatureAlgorithm AlgorithmIdentifier,78* signature BIT STRING,79* certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }80*81* The value for signature SHALL be computed on the hash of the DER82* encoding ResponseData.83*84* ResponseData ::= SEQUENCE {85* version [0] EXPLICIT Version DEFAULT v1,86* responderID ResponderID,87* producedAt GeneralizedTime,88* responses SEQUENCE OF SingleResponse,89* responseExtensions [1] EXPLICIT Extensions OPTIONAL }90*91* ResponderID ::= CHOICE {92* byName [1] Name,93* byKey [2] KeyHash }94*95* KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key96* (excluding the tag and length fields)97*98* SingleResponse ::= SEQUENCE {99* certID CertID,100* certStatus CertStatus,101* thisUpdate GeneralizedTime,102* nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,103* singleExtensions [1] EXPLICIT Extensions OPTIONAL }104*105* CertStatus ::= CHOICE {106* good [0] IMPLICIT NULL,107* revoked [1] IMPLICIT RevokedInfo,108* unknown [2] IMPLICIT UnknownInfo }109*110* RevokedInfo ::= SEQUENCE {111* revocationTime GeneralizedTime,112* revocationReason [0] EXPLICIT CRLReason OPTIONAL }113*114* UnknownInfo ::= NULL -- this can be replaced with an enumeration115*116* </pre>117*118* @author Ram Marti119*/120121public final class OCSPResponse {122123public enum ResponseStatus {124SUCCESSFUL, // Response has valid confirmations125MALFORMED_REQUEST, // Illegal request126INTERNAL_ERROR, // Internal error in responder127TRY_LATER, // Try again later128UNUSED, // is not used129SIG_REQUIRED, // Must sign the request130UNAUTHORIZED // Request unauthorized131};132private static final ResponseStatus[] rsvalues = ResponseStatus.values();133134private static final Debug debug = Debug.getInstance("certpath");135private static final boolean dump = debug != null && Debug.isOn("ocsp");136private static final ObjectIdentifier OCSP_BASIC_RESPONSE_OID =137ObjectIdentifier.newInternal(new int[] { 1, 3, 6, 1, 5, 5, 7, 48, 1, 1});138private static final int CERT_STATUS_GOOD = 0;139private static final int CERT_STATUS_REVOKED = 1;140private static final int CERT_STATUS_UNKNOWN = 2;141142// ResponderID CHOICE tags143private static final int NAME_TAG = 1;144private static final int KEY_TAG = 2;145146// Object identifier for the OCSPSigning key purpose147private static final String KP_OCSP_SIGNING_OID = "1.3.6.1.5.5.7.3.9";148149// Default maximum clock skew in milliseconds (15 minutes)150// allowed when checking validity of OCSP responses151private static final int DEFAULT_MAX_CLOCK_SKEW = 900000;152153/**154* Integer value indicating the maximum allowable clock skew,155* in milliseconds, to be used for the OCSP check.156*/157private static final int MAX_CLOCK_SKEW = initializeClockSkew();158159/**160* Initialize the maximum allowable clock skew by getting the OCSP161* clock skew system property. If the property has not been set, or if its162* value is negative, set the skew to the default.163*/164private static int initializeClockSkew() {165Integer tmp = java.security.AccessController.doPrivileged(166new GetIntegerAction("com.sun.security.ocsp.clockSkew"));167if (tmp == null || tmp < 0) {168return DEFAULT_MAX_CLOCK_SKEW;169}170// Convert to milliseconds, as the system property will be171// specified in seconds172return tmp * 1000;173}174175// an array of all of the CRLReasons (used in SingleResponse)176private static final CRLReason[] values = CRLReason.values();177178private final ResponseStatus responseStatus;179private final Map<CertId, SingleResponse> singleResponseMap;180private final AlgorithmId sigAlgId;181private final byte[] signature;182private final byte[] tbsResponseData;183private final byte[] responseNonce;184private List<X509CertImpl> certs;185private X509CertImpl signerCert = null;186private final ResponderId respId;187private Date producedAtDate = null;188private final Map<String, java.security.cert.Extension> responseExtensions;189190/*191* Create an OCSP response from its ASN.1 DER encoding.192*193* @param bytes The DER-encoded bytes for an OCSP response194*/195public OCSPResponse(byte[] bytes) throws IOException {196if (dump) {197HexDumpEncoder hexEnc = new HexDumpEncoder();198debug.println("OCSPResponse bytes...\n\n" +199hexEnc.encode(bytes) + "\n");200}201DerValue der = new DerValue(bytes);202if (der.tag != DerValue.tag_Sequence) {203throw new IOException("Bad encoding in OCSP response: " +204"expected ASN.1 SEQUENCE tag.");205}206DerInputStream derIn = der.getData();207208// responseStatus209int status = derIn.getEnumerated();210if (status >= 0 && status < rsvalues.length) {211responseStatus = rsvalues[status];212} else {213// unspecified responseStatus214throw new IOException("Unknown OCSPResponse status: " + status);215}216if (debug != null) {217debug.println("OCSP response status: " + responseStatus);218}219if (responseStatus != ResponseStatus.SUCCESSFUL) {220// no need to continue, responseBytes are not set.221singleResponseMap = Collections.emptyMap();222certs = new ArrayList<X509CertImpl>();223sigAlgId = null;224signature = null;225tbsResponseData = null;226responseNonce = null;227responseExtensions = Collections.emptyMap();228respId = null;229return;230}231232// responseBytes233der = derIn.getDerValue();234if (!der.isContextSpecific((byte)0)) {235throw new IOException("Bad encoding in responseBytes element " +236"of OCSP response: expected ASN.1 context specific tag 0.");237}238DerValue tmp = der.data.getDerValue();239if (tmp.tag != DerValue.tag_Sequence) {240throw new IOException("Bad encoding in responseBytes element " +241"of OCSP response: expected ASN.1 SEQUENCE tag.");242}243244// responseType245derIn = tmp.data;246ObjectIdentifier responseType = derIn.getOID();247if (responseType.equals((Object)OCSP_BASIC_RESPONSE_OID)) {248if (debug != null) {249debug.println("OCSP response type: basic");250}251} else {252if (debug != null) {253debug.println("OCSP response type: " + responseType);254}255throw new IOException("Unsupported OCSP response type: " +256responseType);257}258259// BasicOCSPResponse260DerInputStream basicOCSPResponse =261new DerInputStream(derIn.getOctetString());262263DerValue[] seqTmp = basicOCSPResponse.getSequence(3);264if (seqTmp.length < 3) {265throw new IOException("Unexpected BasicOCSPResponse value");266}267268DerValue responseData = seqTmp[0];269270// Need the DER encoded ResponseData to verify the signature later271tbsResponseData = seqTmp[0].toByteArray();272273// tbsResponseData274if (responseData.tag != DerValue.tag_Sequence) {275throw new IOException("Bad encoding in tbsResponseData " +276"element of OCSP response: expected ASN.1 SEQUENCE tag.");277}278DerInputStream seqDerIn = responseData.data;279DerValue seq = seqDerIn.getDerValue();280281// version282if (seq.isContextSpecific((byte)0)) {283// seq[0] is version284if (seq.isConstructed() && seq.isContextSpecific()) {285//System.out.println ("version is available");286seq = seq.data.getDerValue();287int version = seq.getInteger();288if (seq.data.available() != 0) {289throw new IOException("Bad encoding in version " +290" element of OCSP response: bad format");291}292seq = seqDerIn.getDerValue();293}294}295296// responderID297respId = new ResponderId(seq.toByteArray());298if (debug != null) {299debug.println("Responder ID: " + respId);300}301302// producedAt303seq = seqDerIn.getDerValue();304producedAtDate = seq.getGeneralizedTime();305if (debug != null) {306debug.println("OCSP response produced at: " + producedAtDate);307}308309// responses310DerValue[] singleResponseDer = seqDerIn.getSequence(1);311singleResponseMap = new HashMap<>(singleResponseDer.length);312if (debug != null) {313debug.println("OCSP number of SingleResponses: "314+ singleResponseDer.length);315}316for (DerValue srDer : singleResponseDer) {317SingleResponse singleResponse = new SingleResponse(srDer);318singleResponseMap.put(singleResponse.getCertId(), singleResponse);319}320321// responseExtensions322Map<String, java.security.cert.Extension> tmpExtMap = new HashMap<>();323if (seqDerIn.available() > 0) {324seq = seqDerIn.getDerValue();325if (seq.isContextSpecific((byte)1)) {326tmpExtMap = parseExtensions(seq);327}328}329responseExtensions = tmpExtMap;330331// Attach the nonce value if found in the extension map332Extension nonceExt = (Extension)tmpExtMap.get(333PKIXExtensions.OCSPNonce_Id.toString());334responseNonce = (nonceExt != null) ?335nonceExt.getExtensionValue() : null;336if (debug != null && responseNonce != null) {337debug.println("Response nonce: " + Arrays.toString(responseNonce));338}339340// signatureAlgorithmId341sigAlgId = AlgorithmId.parse(seqTmp[1]);342343// signature344signature = seqTmp[2].getBitString();345346// if seq[3] is available , then it is a sequence of certificates347if (seqTmp.length > 3) {348// certs are available349DerValue seqCert = seqTmp[3];350if (!seqCert.isContextSpecific((byte)0)) {351throw new IOException("Bad encoding in certs element of " +352"OCSP response: expected ASN.1 context specific tag 0.");353}354DerValue[] derCerts = seqCert.getData().getSequence(3);355certs = new ArrayList<X509CertImpl>(derCerts.length);356try {357for (int i = 0; i < derCerts.length; i++) {358X509CertImpl cert =359new X509CertImpl(derCerts[i].toByteArray());360certs.add(cert);361362if (debug != null) {363debug.println("OCSP response cert #" + (i + 1) + ": " +364cert.getSubjectX500Principal());365}366}367} catch (CertificateException ce) {368throw new IOException("Bad encoding in X509 Certificate", ce);369}370} else {371certs = new ArrayList<X509CertImpl>();372}373}374375void verify(List<CertId> certIds, IssuerInfo issuerInfo,376X509Certificate responderCert, Date date, byte[] nonce,377String variant)378throws CertPathValidatorException379{380switch (responseStatus) {381case SUCCESSFUL:382break;383case TRY_LATER:384case INTERNAL_ERROR:385throw new CertPathValidatorException(386"OCSP response error: " + responseStatus, null, null, -1,387BasicReason.UNDETERMINED_REVOCATION_STATUS);388case UNAUTHORIZED:389default:390throw new CertPathValidatorException("OCSP response error: " +391responseStatus);392}393394// Check that the response includes a response for all of the395// certs that were supplied in the request396for (CertId certId : certIds) {397SingleResponse sr = getSingleResponse(certId);398if (sr == null) {399if (debug != null) {400debug.println("No response found for CertId: " + certId);401}402throw new CertPathValidatorException(403"OCSP response does not include a response for a " +404"certificate supplied in the OCSP request");405}406if (debug != null) {407debug.println("Status of certificate (with serial number " +408certId.getSerialNumber() + ") is: " + sr.getCertStatus());409}410}411412// Locate the signer cert413if (signerCert == null) {414// Add the Issuing CA cert and/or Trusted Responder cert to the list415// of certs from the OCSP response416try {417if (issuerInfo.getCertificate() != null) {418certs.add(X509CertImpl.toImpl(issuerInfo.getCertificate()));419}420if (responderCert != null) {421certs.add(X509CertImpl.toImpl(responderCert));422}423} catch (CertificateException ce) {424throw new CertPathValidatorException(425"Invalid issuer or trusted responder certificate", ce);426}427428if (respId.getType() == ResponderId.Type.BY_NAME) {429X500Principal rName = respId.getResponderName();430for (X509CertImpl cert : certs) {431if (cert.getSubjectX500Principal().equals(rName)) {432signerCert = cert;433break;434}435}436} else if (respId.getType() == ResponderId.Type.BY_KEY) {437KeyIdentifier ridKeyId = respId.getKeyIdentifier();438for (X509CertImpl cert : certs) {439// Match responder's key identifier against the cert's SKID440// This will match if the SKID is encoded using the 160-bit441// SHA-1 hash method as defined in RFC 5280.442KeyIdentifier certKeyId = cert.getSubjectKeyId();443if (certKeyId != null && ridKeyId.equals(certKeyId)) {444signerCert = cert;445break;446} else {447// The certificate does not have a SKID or may have448// been using a different algorithm (ex: see RFC 7093).449// Check if the responder's key identifier matches450// against a newly generated key identifier of the451// cert's public key using the 160-bit SHA-1 method.452try {453certKeyId = new KeyIdentifier(cert.getPublicKey());454} catch (IOException e) {455// ignore456}457if (ridKeyId.equals(certKeyId)) {458signerCert = cert;459break;460}461}462}463}464}465466// Check whether the signer cert returned by the responder is trusted467boolean signedByTrustedResponder = false;468if (signerCert != null) {469// Check if the response is signed by the issuing CA470if (signerCert.getSubjectX500Principal().equals(471issuerInfo.getName()) &&472signerCert.getPublicKey().equals(473issuerInfo.getPublicKey())) {474if (debug != null) {475debug.println("OCSP response is signed by the target's " +476"Issuing CA");477}478// cert is trusted, now verify the signed response479480// Check if the response is signed by a trusted responder481} else if (signerCert.equals(responderCert)) {482signedByTrustedResponder = true;483if (debug != null) {484debug.println("OCSP response is signed by a Trusted " +485"Responder");486}487// cert is trusted, now verify the signed response488489// Check if the response is signed by an authorized responder490} else if (signerCert.getIssuerX500Principal().equals(491issuerInfo.getName())) {492493// Check for the OCSPSigning key purpose494try {495List<String> keyPurposes = signerCert.getExtendedKeyUsage();496if (keyPurposes == null ||497!keyPurposes.contains(KP_OCSP_SIGNING_OID)) {498throw new CertPathValidatorException(499"Responder's certificate not valid for signing " +500"OCSP responses");501}502} catch (CertificateParsingException cpe) {503// assume cert is not valid for signing504throw new CertPathValidatorException(505"Responder's certificate not valid for signing " +506"OCSP responses", cpe);507}508509// Check algorithm constraints specified in security property510// "jdk.certpath.disabledAlgorithms".511AlgorithmChecker algChecker =512new AlgorithmChecker(issuerInfo.getAnchor(), date,513variant);514algChecker.init(false);515algChecker.check(signerCert, Collections.<String>emptySet());516517// check the validity518try {519if (date == null) {520signerCert.checkValidity();521} else {522signerCert.checkValidity(date);523}524} catch (CertificateException e) {525throw new CertPathValidatorException(526"Responder's certificate not within the " +527"validity period", e);528}529530// check for revocation531//532// A CA may specify that an OCSP client can trust a533// responder for the lifetime of the responder's534// certificate. The CA does so by including the535// extension id-pkix-ocsp-nocheck.536//537Extension noCheck =538signerCert.getExtension(PKIXExtensions.OCSPNoCheck_Id);539if (noCheck != null) {540if (debug != null) {541debug.println("Responder's certificate includes " +542"the extension id-pkix-ocsp-nocheck.");543}544} else {545// we should do the revocation checking of the546// authorized responder in a future update.547}548549// verify the signature550try {551signerCert.verify(issuerInfo.getPublicKey());552if (debug != null) {553debug.println("OCSP response is signed by an " +554"Authorized Responder");555}556// cert is trusted, now verify the signed response557558} catch (GeneralSecurityException e) {559signerCert = null;560}561} else {562throw new CertPathValidatorException(563"Responder's certificate is not authorized to sign " +564"OCSP responses");565}566}567568// Confirm that the signed response was generated using the public569// key from the trusted responder cert570if (signerCert != null) {571// Check algorithm constraints specified in security property572// "jdk.certpath.disabledAlgorithms".573AlgorithmChecker.check(signerCert.getPublicKey(), sigAlgId, variant,574signedByTrustedResponder575? new TrustAnchor(responderCert, null)576: issuerInfo.getAnchor());577578if (!verifySignature(signerCert)) {579throw new CertPathValidatorException(580"Error verifying OCSP Response's signature");581}582} else {583// Need responder's cert in order to verify the signature584throw new CertPathValidatorException(585"Unable to verify OCSP Response's signature");586}587588if (nonce != null) {589if (responseNonce != null && !Arrays.equals(nonce, responseNonce)) {590throw new CertPathValidatorException("Nonces don't match");591}592}593594// Check freshness of OCSPResponse595long now = (date == null) ? System.currentTimeMillis() : date.getTime();596Date nowPlusSkew = new Date(now + MAX_CLOCK_SKEW);597Date nowMinusSkew = new Date(now - MAX_CLOCK_SKEW);598for (SingleResponse sr : singleResponseMap.values()) {599if (debug != null) {600String until = "";601if (sr.nextUpdate != null) {602until = " until " + sr.nextUpdate;603}604debug.println("OCSP response validity interval is from " +605sr.thisUpdate + until);606debug.println("Checking validity of OCSP response on: " +607new Date(now));608}609610// Check that the test date is within the validity interval:611// [ thisUpdate - MAX_CLOCK_SKEW,612// MAX(thisUpdate, nextUpdate) + MAX_CLOCK_SKEW ]613if (nowPlusSkew.before(sr.thisUpdate) ||614nowMinusSkew.after(615sr.nextUpdate != null ? sr.nextUpdate : sr.thisUpdate))616{617throw new CertPathValidatorException(618"Response is unreliable: its validity " +619"interval is out-of-date");620}621}622}623624/**625* Returns the OCSP ResponseStatus.626*627* @return the {@code ResponseStatus} for this OCSP response628*/629public ResponseStatus getResponseStatus() {630return responseStatus;631}632633/*634* Verify the signature of the OCSP response.635*/636private boolean verifySignature(X509Certificate cert)637throws CertPathValidatorException {638639try {640Signature respSignature = Signature.getInstance(sigAlgId.getName());641respSignature.initVerify(cert.getPublicKey());642respSignature.update(tbsResponseData);643644if (respSignature.verify(signature)) {645if (debug != null) {646debug.println("Verified signature of OCSP Response");647}648return true;649650} else {651if (debug != null) {652debug.println(653"Error verifying signature of OCSP Response");654}655return false;656}657} catch (InvalidKeyException | NoSuchAlgorithmException |658SignatureException e)659{660throw new CertPathValidatorException(e);661}662}663664/**665* Returns the SingleResponse of the specified CertId, or null if666* there is no response for that CertId.667*668* @param certId the {@code CertId} for a {@code SingleResponse} to be669* searched for in the OCSP response.670*671* @return the {@code SingleResponse} for the provided {@code CertId},672* or {@code null} if it is not found.673*/674public SingleResponse getSingleResponse(CertId certId) {675return singleResponseMap.get(certId);676}677678/**679* Return a set of all CertIds in this {@code OCSPResponse}680*681* @return an unmodifiable set containing every {@code CertId} in this682* response.683*/684public Set<CertId> getCertIds() {685return Collections.unmodifiableSet(singleResponseMap.keySet());686}687688/*689* Returns the certificate for the authority that signed the OCSP response.690*/691X509Certificate getSignerCertificate() {692return signerCert; // set in verify()693}694695/**696* Get the {@code ResponderId} from this {@code OCSPResponse}697*698* @return the {@code ResponderId} from this response or {@code null}699* if no responder ID is in the body of the response (e.g. a700* response with a status other than SUCCESS.701*/702public ResponderId getResponderId() {703return respId;704}705706/**707* Provide a String representation of an OCSPResponse708*709* @return a human-readable representation of the OCSPResponse710*/711@Override712public String toString() {713StringBuilder sb = new StringBuilder();714sb.append("OCSP Response:\n");715sb.append("Response Status: ").append(responseStatus).append("\n");716sb.append("Responder ID: ").append(respId).append("\n");717sb.append("Produced at: ").append(producedAtDate).append("\n");718int count = singleResponseMap.size();719sb.append(count).append(count == 1 ?720" response:\n" : " responses:\n");721for (SingleResponse sr : singleResponseMap.values()) {722sb.append(sr).append("\n");723}724if (responseExtensions != null && responseExtensions.size() > 0) {725count = responseExtensions.size();726sb.append(count).append(count == 1 ?727" extension:\n" : " extensions:\n");728for (String extId : responseExtensions.keySet()) {729sb.append(responseExtensions.get(extId)).append("\n");730}731}732733return sb.toString();734}735736/**737* Build a String-Extension map from DER encoded data.738* @param derVal A {@code DerValue} object built from a SEQUENCE of739* extensions740*741* @return a {@code Map} using the OID in string form as the keys. If no742* extensions are found or an empty SEQUENCE is passed in, then743* an empty {@code Map} will be returned.744*745* @throws IOException if any decoding errors occur.746*/747private static Map<String, java.security.cert.Extension>748parseExtensions(DerValue derVal) throws IOException {749DerValue[] extDer = derVal.data.getSequence(3);750Map<String, java.security.cert.Extension> extMap =751new HashMap<>(extDer.length);752753for (DerValue extDerVal : extDer) {754Extension ext = new Extension(extDerVal);755if (debug != null) {756debug.println("Extension: " + ext);757}758// We don't support any extensions yet. Therefore, if it759// is critical we must throw an exception because we760// don't know how to process it.761if (ext.isCritical()) {762throw new IOException("Unsupported OCSP critical extension: " +763ext.getExtensionId());764}765extMap.put(ext.getId(), ext);766}767768return extMap;769}770771/*772* A class representing a single OCSP response.773*/774public static final class SingleResponse implements OCSP.RevocationStatus {775private final CertId certId;776private final CertStatus certStatus;777private final Date thisUpdate;778private final Date nextUpdate;779private final Date revocationTime;780private final CRLReason revocationReason;781private final Map<String, java.security.cert.Extension> singleExtensions;782783private SingleResponse(DerValue der) throws IOException {784if (der.tag != DerValue.tag_Sequence) {785throw new IOException("Bad ASN.1 encoding in SingleResponse");786}787DerInputStream tmp = der.data;788789certId = new CertId(tmp.getDerValue().data);790DerValue derVal = tmp.getDerValue();791short tag = (byte)(derVal.tag & 0x1f);792if (tag == CERT_STATUS_REVOKED) {793certStatus = CertStatus.REVOKED;794revocationTime = derVal.data.getGeneralizedTime();795if (derVal.data.available() != 0) {796DerValue dv = derVal.data.getDerValue();797tag = (byte)(dv.tag & 0x1f);798if (tag == 0) {799int reason = dv.data.getEnumerated();800// if reason out-of-range just leave as UNSPECIFIED801if (reason >= 0 && reason < values.length) {802revocationReason = values[reason];803} else {804revocationReason = CRLReason.UNSPECIFIED;805}806} else {807revocationReason = CRLReason.UNSPECIFIED;808}809} else {810revocationReason = CRLReason.UNSPECIFIED;811}812// RevokedInfo813if (debug != null) {814debug.println("Revocation time: " + revocationTime);815debug.println("Revocation reason: " + revocationReason);816}817} else {818revocationTime = null;819revocationReason = null;820if (tag == CERT_STATUS_GOOD) {821certStatus = CertStatus.GOOD;822} else if (tag == CERT_STATUS_UNKNOWN) {823certStatus = CertStatus.UNKNOWN;824} else {825throw new IOException("Invalid certificate status");826}827}828829thisUpdate = tmp.getGeneralizedTime();830if (debug != null) {831debug.println("thisUpdate: " + thisUpdate);832}833834// Parse optional fields like nextUpdate and singleExtensions835Date tmpNextUpdate = null;836Map<String, java.security.cert.Extension> tmpMap = null;837838// Check for the first optional item, it could be nextUpdate839// [CONTEXT 0] or singleExtensions [CONTEXT 1]840if (tmp.available() > 0) {841derVal = tmp.getDerValue();842843// nextUpdate processing844if (derVal.isContextSpecific((byte)0)) {845tmpNextUpdate = derVal.data.getGeneralizedTime();846if (debug != null) {847debug.println("nextUpdate: " + tmpNextUpdate);848}849850// If more data exists in the singleResponse, it851// can only be singleExtensions. Get this DER value852// for processing in the next block853derVal = tmp.available() > 0 ? tmp.getDerValue() : null;854}855856// singleExtensions processing857if (derVal != null) {858if (derVal.isContextSpecific((byte)1)) {859tmpMap = parseExtensions(derVal);860861// There should not be any other items in the862// singleResponse at this point.863if (tmp.available() > 0) {864throw new IOException(tmp.available() +865" bytes of additional data in singleResponse");866}867} else {868// Unknown item in the singleResponse869throw new IOException("Unsupported singleResponse " +870"item, tag = " + String.format("%02X", derVal.tag));871}872}873}874875nextUpdate = tmpNextUpdate;876singleExtensions = (tmpMap != null) ? tmpMap :877Collections.emptyMap();878if (debug != null) {879for (java.security.cert.Extension ext :880singleExtensions.values()) {881debug.println("singleExtension: " + ext);882}883}884}885886/*887* Return the certificate's revocation status code888*/889@Override890public CertStatus getCertStatus() {891return certStatus;892}893894/**895* Get the Cert ID that this SingleResponse is for.896*897* @return the {@code CertId} for this {@code SingleResponse}898*/899public CertId getCertId() {900return certId;901}902903/**904* Get the {@code thisUpdate} field from this {@code SingleResponse}.905*906* @return a {@link Date} object containing the thisUpdate date907*/908public Date getThisUpdate() {909return (thisUpdate != null ? (Date) thisUpdate.clone() : null);910}911912/**913* Get the {@code nextUpdate} field from this {@code SingleResponse}.914*915* @return a {@link Date} object containing the nexUpdate date or916* {@code null} if a nextUpdate field is not present in the response.917*/918public Date getNextUpdate() {919return (nextUpdate != null ? (Date) nextUpdate.clone() : null);920}921922/**923* Get the {@code revocationTime} field from this924* {@code SingleResponse}.925*926* @return a {@link Date} object containing the revocationTime date or927* {@code null} if the {@code SingleResponse} does not have a status928* of {@code REVOKED}.929*/930@Override931public Date getRevocationTime() {932return (revocationTime != null ? (Date) revocationTime.clone() :933null);934}935936/**937* Get the {@code revocationReason} field for the938* {@code SingleResponse}.939*940* @return a {@link CRLReason} containing the revocation reason, or941* {@code null} if a revocation reason was not provided or the942* response status is not {@code REVOKED}.943*/944@Override945public CRLReason getRevocationReason() {946return revocationReason;947}948949/**950* Get the {@code singleExtensions} for this {@code SingleResponse}.951*952* @return a {@link Map} of {@link Extension} objects, keyed by953* their OID value in string form.954*/955@Override956public Map<String, java.security.cert.Extension> getSingleExtensions() {957return Collections.unmodifiableMap(singleExtensions);958}959960/**961* Construct a string representation of a single OCSP response.962*/963@Override public String toString() {964StringBuilder sb = new StringBuilder();965sb.append("SingleResponse:\n");966sb.append(certId);967sb.append("\nCertStatus: ").append(certStatus).append("\n");968if (certStatus == CertStatus.REVOKED) {969sb.append("revocationTime is ");970sb.append(revocationTime).append("\n");971sb.append("revocationReason is ");972sb.append(revocationReason).append("\n");973}974sb.append("thisUpdate is ").append(thisUpdate).append("\n");975if (nextUpdate != null) {976sb.append("nextUpdate is ").append(nextUpdate).append("\n");977}978for (java.security.cert.Extension ext : singleExtensions.values()) {979sb.append("singleExtension: ");980sb.append(ext.toString()).append("\n");981}982return sb.toString();983}984}985986/**987* Helper class that allows consumers to pass in issuer information. This988* will always consist of the issuer's name and public key, but may also989* contain a certificate if the originating data is in that form. The990* trust anchor for the certificate chain will be included for certpath991* disabled algorithm checking.992*/993static final class IssuerInfo {994private final TrustAnchor anchor;995private final X509Certificate certificate;996private final X500Principal name;997private final PublicKey pubKey;998999IssuerInfo(TrustAnchor anchor) {1000this(anchor, (anchor != null) ? anchor.getTrustedCert() : null);1001}10021003IssuerInfo(X509Certificate issuerCert) {1004this(null, issuerCert);1005}10061007IssuerInfo(TrustAnchor anchor, X509Certificate issuerCert) {1008if (anchor == null && issuerCert == null) {1009throw new NullPointerException("TrustAnchor and issuerCert " +1010"cannot be null");1011}1012this.anchor = anchor;1013if (issuerCert != null) {1014name = issuerCert.getSubjectX500Principal();1015pubKey = issuerCert.getPublicKey();1016certificate = issuerCert;1017} else {1018name = anchor.getCA();1019pubKey = anchor.getCAPublicKey();1020certificate = anchor.getTrustedCert();1021}1022}10231024/**1025* Get the certificate in this IssuerInfo if present.1026*1027* @return the {@code X509Certificate} used to create this IssuerInfo1028* object, or {@code null} if a certificate was not used in its1029* creation.1030*/1031X509Certificate getCertificate() {1032return certificate;1033}10341035/**1036* Get the name of this issuer.1037*1038* @return an {@code X500Principal} corresponding to this issuer's1039* name. If derived from an issuer's {@code X509Certificate} this1040* would be equivalent to the certificate subject name.1041*/1042X500Principal getName() {1043return name;1044}10451046/**1047* Get the public key for this issuer.1048*1049* @return a {@code PublicKey} for this issuer.1050*/1051PublicKey getPublicKey() {1052return pubKey;1053}10541055/**1056* Get the TrustAnchor for the certificate chain.1057*1058* @return a {@code TrustAnchor}.1059*/1060TrustAnchor getAnchor() {1061return anchor;1062}10631064/**1065* Create a string representation of this IssuerInfo.1066*1067* @return a {@code String} form of this IssuerInfo object.1068*/1069@Override1070public String toString() {1071StringBuilder sb = new StringBuilder();1072sb.append("Issuer Info:\n");1073sb.append("Name: ").append(name.toString()).append("\n");1074sb.append("Public Key:\n").append(pubKey.toString()).append("\n");1075return sb.toString();1076}1077}1078}107910801081