Path: blob/master/src/java.base/share/classes/sun/security/provider/certpath/OCSPResponse.java
67848 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.security.util.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.of(KnownOIDs.OCSPBasicResponse);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// Default maximum clock skew in milliseconds (15 minutes)147// allowed when checking validity of OCSP responses148private static final int DEFAULT_MAX_CLOCK_SKEW = 900000;149150/**151* Integer value indicating the maximum allowable clock skew,152* in milliseconds, to be used for the OCSP check.153*/154private static final int MAX_CLOCK_SKEW = initializeClockSkew();155156/**157* Initialize the maximum allowable clock skew by getting the OCSP158* clock skew system property. If the property has not been set, or if its159* value is negative, set the skew to the default.160*/161private static int initializeClockSkew() {162@SuppressWarnings("removal")163Integer tmp = java.security.AccessController.doPrivileged(164new GetIntegerAction("com.sun.security.ocsp.clockSkew"));165if (tmp == null || tmp < 0) {166return DEFAULT_MAX_CLOCK_SKEW;167}168// Convert to milliseconds, as the system property will be169// specified in seconds170return tmp * 1000;171}172173// an array of all of the CRLReasons (used in SingleResponse)174private static final CRLReason[] values = CRLReason.values();175176private final ResponseStatus responseStatus;177private final Map<CertId, SingleResponse> singleResponseMap;178private final AlgorithmId sigAlgId;179private final byte[] signature;180private final byte[] tbsResponseData;181private final byte[] responseNonce;182private List<X509CertImpl> certs;183private X509CertImpl signerCert = null;184private final ResponderId respId;185private Date producedAtDate = null;186private final Map<String, java.security.cert.Extension> responseExtensions;187188/*189* Create an OCSP response from its ASN.1 DER encoding.190*191* @param bytes The DER-encoded bytes for an OCSP response192*/193public OCSPResponse(byte[] bytes) throws IOException {194if (dump) {195HexDumpEncoder hexEnc = new HexDumpEncoder();196debug.println("OCSPResponse bytes...\n\n" +197hexEnc.encode(bytes) + "\n");198}199DerValue der = new DerValue(bytes);200if (der.tag != DerValue.tag_Sequence) {201throw new IOException("Bad encoding in OCSP response: " +202"expected ASN.1 SEQUENCE tag.");203}204DerInputStream derIn = der.getData();205206// responseStatus207int status = derIn.getEnumerated();208if (status >= 0 && status < rsvalues.length) {209responseStatus = rsvalues[status];210} else {211// unspecified responseStatus212throw new IOException("Unknown OCSPResponse status: " + status);213}214if (debug != null) {215debug.println("OCSP response status: " + responseStatus);216}217if (responseStatus != ResponseStatus.SUCCESSFUL) {218// no need to continue, responseBytes are not set.219singleResponseMap = Collections.emptyMap();220certs = new ArrayList<X509CertImpl>();221sigAlgId = null;222signature = null;223tbsResponseData = null;224responseNonce = null;225responseExtensions = Collections.emptyMap();226respId = null;227return;228}229230// responseBytes231der = derIn.getDerValue();232if (!der.isContextSpecific((byte)0)) {233throw new IOException("Bad encoding in responseBytes element " +234"of OCSP response: expected ASN.1 context specific tag 0.");235}236DerValue tmp = der.data.getDerValue();237if (tmp.tag != DerValue.tag_Sequence) {238throw new IOException("Bad encoding in responseBytes element " +239"of OCSP response: expected ASN.1 SEQUENCE tag.");240}241242// responseType243derIn = tmp.data;244ObjectIdentifier responseType = derIn.getOID();245if (responseType.equals((Object)OCSP_BASIC_RESPONSE_OID)) {246if (debug != null) {247debug.println("OCSP response type: basic");248}249} else {250if (debug != null) {251debug.println("OCSP response type: " + responseType);252}253throw new IOException("Unsupported OCSP response type: " +254responseType);255}256257// BasicOCSPResponse258DerInputStream basicOCSPResponse =259new DerInputStream(derIn.getOctetString());260261DerValue[] seqTmp = basicOCSPResponse.getSequence(3);262if (seqTmp.length < 3) {263throw new IOException("Unexpected BasicOCSPResponse value");264}265266DerValue responseData = seqTmp[0];267268// Need the DER encoded ResponseData to verify the signature later269tbsResponseData = seqTmp[0].toByteArray();270271// tbsResponseData272if (responseData.tag != DerValue.tag_Sequence) {273throw new IOException("Bad encoding in tbsResponseData " +274"element of OCSP response: expected ASN.1 SEQUENCE tag.");275}276DerInputStream seqDerIn = responseData.data;277DerValue seq = seqDerIn.getDerValue();278279// version280if (seq.isContextSpecific((byte)0)) {281// seq[0] is version282if (seq.isConstructed() && seq.isContextSpecific()) {283//System.out.println ("version is available");284seq = seq.data.getDerValue();285int version = seq.getInteger();286if (seq.data.available() != 0) {287throw new IOException("Bad encoding in version " +288" element of OCSP response: bad format");289}290seq = seqDerIn.getDerValue();291}292}293294// responderID295respId = new ResponderId(seq.toByteArray());296if (debug != null) {297debug.println("Responder ID: " + respId);298}299300// producedAt301seq = seqDerIn.getDerValue();302producedAtDate = seq.getGeneralizedTime();303if (debug != null) {304debug.println("OCSP response produced at: " + producedAtDate);305}306307// responses308DerValue[] singleResponseDer = seqDerIn.getSequence(1);309singleResponseMap = new HashMap<>(singleResponseDer.length);310if (debug != null) {311debug.println("OCSP number of SingleResponses: "312+ singleResponseDer.length);313}314for (DerValue srDer : singleResponseDer) {315SingleResponse singleResponse = new SingleResponse(srDer);316singleResponseMap.put(singleResponse.getCertId(), singleResponse);317}318319// responseExtensions320Map<String, java.security.cert.Extension> tmpExtMap = new HashMap<>();321if (seqDerIn.available() > 0) {322seq = seqDerIn.getDerValue();323if (seq.isContextSpecific((byte)1)) {324tmpExtMap = parseExtensions(seq);325}326}327responseExtensions = tmpExtMap;328329// Attach the nonce value if found in the extension map330Extension nonceExt = (Extension)tmpExtMap.get(331PKIXExtensions.OCSPNonce_Id.toString());332responseNonce = (nonceExt != null) ?333nonceExt.getExtensionValue() : null;334if (debug != null && responseNonce != null) {335debug.println("Response nonce: " + Arrays.toString(responseNonce));336}337338// signatureAlgorithmId339sigAlgId = AlgorithmId.parse(seqTmp[1]);340341// signature342signature = seqTmp[2].getBitString();343344// if seq[3] is available , then it is a sequence of certificates345if (seqTmp.length > 3) {346// certs are available347DerValue seqCert = seqTmp[3];348if (!seqCert.isContextSpecific((byte)0)) {349throw new IOException("Bad encoding in certs element of " +350"OCSP response: expected ASN.1 context specific tag 0.");351}352DerValue[] derCerts = seqCert.getData().getSequence(3);353certs = new ArrayList<X509CertImpl>(derCerts.length);354try {355for (int i = 0; i < derCerts.length; i++) {356X509CertImpl cert =357new X509CertImpl(derCerts[i].toByteArray());358certs.add(cert);359360if (debug != null) {361debug.println("OCSP response cert #" + (i + 1) + ": " +362cert.getSubjectX500Principal());363}364}365} catch (CertificateException ce) {366throw new IOException("Bad encoding in X509 Certificate", ce);367}368} else {369certs = new ArrayList<X509CertImpl>();370}371}372373void verify(List<CertId> certIds, IssuerInfo issuerInfo,374X509Certificate responderCert, Date date, byte[] nonce,375String variant)376throws CertPathValidatorException377{378switch (responseStatus) {379case SUCCESSFUL:380break;381case TRY_LATER:382case INTERNAL_ERROR:383throw new CertPathValidatorException(384"OCSP response error: " + responseStatus, null, null, -1,385BasicReason.UNDETERMINED_REVOCATION_STATUS);386case UNAUTHORIZED:387default:388throw new CertPathValidatorException("OCSP response error: " +389responseStatus);390}391392// Check that the response includes a response for all of the393// certs that were supplied in the request394for (CertId certId : certIds) {395SingleResponse sr = getSingleResponse(certId);396if (sr == null) {397if (debug != null) {398debug.println("No response found for CertId: " + certId);399}400throw new CertPathValidatorException(401"OCSP response does not include a response for a " +402"certificate supplied in the OCSP request");403}404if (debug != null) {405debug.println("Status of certificate (with serial number " +406certId.getSerialNumber() + ") is: " + sr.getCertStatus());407}408}409410// Locate the signer cert411if (signerCert == null) {412// Add the Issuing CA cert and/or Trusted Responder cert to the list413// of certs from the OCSP response414try {415if (issuerInfo.getCertificate() != null) {416certs.add(X509CertImpl.toImpl(issuerInfo.getCertificate()));417}418if (responderCert != null) {419certs.add(X509CertImpl.toImpl(responderCert));420}421} catch (CertificateException ce) {422throw new CertPathValidatorException(423"Invalid issuer or trusted responder certificate", ce);424}425426if (respId.getType() == ResponderId.Type.BY_NAME) {427X500Principal rName = respId.getResponderName();428for (X509CertImpl cert : certs) {429if (cert.getSubjectX500Principal().equals(rName)) {430signerCert = cert;431break;432}433}434} else if (respId.getType() == ResponderId.Type.BY_KEY) {435KeyIdentifier ridKeyId = respId.getKeyIdentifier();436for (X509CertImpl cert : certs) {437// Match responder's key identifier against the cert's SKID438// This will match if the SKID is encoded using the 160-bit439// SHA-1 hash method as defined in RFC 5280.440KeyIdentifier certKeyId = cert.getSubjectKeyId();441if (certKeyId != null && ridKeyId.equals(certKeyId)) {442signerCert = cert;443break;444} else {445// The certificate does not have a SKID or may have446// been using a different algorithm (ex: see RFC 7093).447// Check if the responder's key identifier matches448// against a newly generated key identifier of the449// cert's public key using the 160-bit SHA-1 method.450try {451certKeyId = new KeyIdentifier(cert.getPublicKey());452} catch (IOException e) {453// ignore454}455if (ridKeyId.equals(certKeyId)) {456signerCert = cert;457break;458}459}460}461}462}463464// Check whether the signer cert returned by the responder is trusted465boolean signedByTrustedResponder = false;466if (signerCert != null) {467// Check if the response is signed by the issuing CA468if (signerCert.getSubjectX500Principal().equals(469issuerInfo.getName()) &&470signerCert.getPublicKey().equals(471issuerInfo.getPublicKey())) {472if (debug != null) {473debug.println("OCSP response is signed by the target's " +474"Issuing CA");475}476// cert is trusted, now verify the signed response477478// Check if the response is signed by a trusted responder479} else if (signerCert.equals(responderCert)) {480signedByTrustedResponder = true;481if (debug != null) {482debug.println("OCSP response is signed by a Trusted " +483"Responder");484}485// cert is trusted, now verify the signed response486487// Check if the response is signed by an authorized responder488} else if (signerCert.getIssuerX500Principal().equals(489issuerInfo.getName())) {490491// Check for the OCSPSigning key purpose492try {493List<String> keyPurposes = signerCert.getExtendedKeyUsage();494if (keyPurposes == null ||495!keyPurposes.contains(KnownOIDs.OCSPSigning.value())) {496throw new CertPathValidatorException(497"Responder's certificate not valid for signing " +498"OCSP responses");499}500} catch (CertificateParsingException cpe) {501// assume cert is not valid for signing502throw new CertPathValidatorException(503"Responder's certificate not valid for signing " +504"OCSP responses", cpe);505}506507// Check algorithm constraints specified in security property508// "jdk.certpath.disabledAlgorithms".509AlgorithmChecker algChecker =510new AlgorithmChecker(issuerInfo.getAnchor(), date,511variant);512algChecker.init(false);513algChecker.check(signerCert, Collections.<String>emptySet());514515// check the validity516try {517if (date == null) {518signerCert.checkValidity();519} else {520signerCert.checkValidity(date);521}522} catch (CertificateException e) {523throw new CertPathValidatorException(524"Responder's certificate not within the " +525"validity period", e);526}527528// check for revocation529//530// A CA may specify that an OCSP client can trust a531// responder for the lifetime of the responder's532// certificate. The CA does so by including the533// extension id-pkix-ocsp-nocheck.534//535Extension noCheck =536signerCert.getExtension(PKIXExtensions.OCSPNoCheck_Id);537if (noCheck != null) {538if (debug != null) {539debug.println("Responder's certificate includes " +540"the extension id-pkix-ocsp-nocheck.");541}542} else {543// we should do the revocation checking of the544// authorized responder in a future update.545}546547// verify the signature548try {549signerCert.verify(issuerInfo.getPublicKey());550if (debug != null) {551debug.println("OCSP response is signed by an " +552"Authorized Responder");553}554// cert is trusted, now verify the signed response555556} catch (GeneralSecurityException e) {557signerCert = null;558}559} else {560throw new CertPathValidatorException(561"Responder's certificate is not authorized to sign " +562"OCSP responses");563}564}565566// Confirm that the signed response was generated using the public567// key from the trusted responder cert568if (signerCert != null) {569// Check algorithm constraints specified in security property570// "jdk.certpath.disabledAlgorithms".571AlgorithmChecker.check(signerCert.getPublicKey(), sigAlgId, variant,572signedByTrustedResponder573? new TrustAnchor(responderCert, null)574: issuerInfo.getAnchor());575576if (!verifySignature(signerCert)) {577throw new CertPathValidatorException(578"Error verifying OCSP Response's signature");579}580} else {581// Need responder's cert in order to verify the signature582throw new CertPathValidatorException(583"Unable to verify OCSP Response's signature");584}585586if (nonce != null) {587if (responseNonce != null && !Arrays.equals(nonce, responseNonce)) {588throw new CertPathValidatorException("Nonces don't match");589}590}591592// Check freshness of OCSPResponse593long now = (date == null) ? System.currentTimeMillis() : date.getTime();594Date nowPlusSkew = new Date(now + MAX_CLOCK_SKEW);595Date nowMinusSkew = new Date(now - MAX_CLOCK_SKEW);596for (SingleResponse sr : singleResponseMap.values()) {597if (debug != null) {598String until = "";599if (sr.nextUpdate != null) {600until = " until " + sr.nextUpdate;601}602debug.println("OCSP response validity interval is from " +603sr.thisUpdate + until);604debug.println("Checking validity of OCSP response on " +605new Date(now) + " with allowed interval between " +606nowMinusSkew + " and " + nowPlusSkew);607}608609// Check that the test date is within the validity interval:610// [ thisUpdate - MAX_CLOCK_SKEW,611// MAX(thisUpdate, nextUpdate) + MAX_CLOCK_SKEW ]612if (nowPlusSkew.before(sr.thisUpdate) ||613nowMinusSkew.after(614sr.nextUpdate != null ? sr.nextUpdate : sr.thisUpdate))615{616throw new CertPathValidatorException(617"Response is unreliable: its validity " +618"interval is out-of-date");619}620}621}622623/**624* Returns the OCSP ResponseStatus.625*626* @return the {@code ResponseStatus} for this OCSP response627*/628public ResponseStatus getResponseStatus() {629return responseStatus;630}631632/*633* Verify the signature of the OCSP response.634*/635private boolean verifySignature(X509Certificate cert)636throws CertPathValidatorException {637638try {639Signature respSignature = Signature.getInstance(sigAlgId.getName());640SignatureUtil.initVerifyWithParam(respSignature,641cert.getPublicKey(),642SignatureUtil.getParamSpec(sigAlgId.getName(),643sigAlgId.getEncodedParams()));644respSignature.update(tbsResponseData);645646if (respSignature.verify(signature)) {647if (debug != null) {648debug.println("Verified signature of OCSP Response");649}650return true;651652} else {653if (debug != null) {654debug.println(655"Error verifying signature of OCSP Response");656}657return false;658}659} catch (InvalidAlgorithmParameterException | InvalidKeyException660| NoSuchAlgorithmException | SignatureException e)661{662throw new CertPathValidatorException(e);663}664}665666/**667* Returns the SingleResponse of the specified CertId, or null if668* there is no response for that CertId.669*670* @param certId the {@code CertId} for a {@code SingleResponse} to be671* searched for in the OCSP response.672*673* @return the {@code SingleResponse} for the provided {@code CertId},674* or {@code null} if it is not found.675*/676public SingleResponse getSingleResponse(CertId certId) {677return singleResponseMap.get(certId);678}679680/**681* Return a set of all CertIds in this {@code OCSPResponse}682*683* @return an unmodifiable set containing every {@code CertId} in this684* response.685*/686public Set<CertId> getCertIds() {687return Collections.unmodifiableSet(singleResponseMap.keySet());688}689690/*691* Returns the certificate for the authority that signed the OCSP response.692*/693X509Certificate getSignerCertificate() {694return signerCert; // set in verify()695}696697/**698* Get the {@code ResponderId} from this {@code OCSPResponse}699*700* @return the {@code ResponderId} from this response or {@code null}701* if no responder ID is in the body of the response (e.g. a702* response with a status other than SUCCESS.703*/704public ResponderId getResponderId() {705return respId;706}707708/**709* Provide a String representation of an OCSPResponse710*711* @return a human-readable representation of the OCSPResponse712*/713@Override714public String toString() {715StringBuilder sb = new StringBuilder();716sb.append("OCSP Response:\n");717sb.append("Response Status: ").append(responseStatus).append("\n");718sb.append("Responder ID: ").append(respId).append("\n");719sb.append("Produced at: ").append(producedAtDate).append("\n");720int count = singleResponseMap.size();721sb.append(count).append(count == 1 ?722" response:\n" : " responses:\n");723for (SingleResponse sr : singleResponseMap.values()) {724sb.append(sr).append("\n");725}726if (responseExtensions != null && responseExtensions.size() > 0) {727count = responseExtensions.size();728sb.append(count).append(count == 1 ?729" extension:\n" : " extensions:\n");730for (String extId : responseExtensions.keySet()) {731sb.append(responseExtensions.get(extId)).append("\n");732}733}734735return sb.toString();736}737738/**739* Build a String-Extension map from DER encoded data.740* @param derVal A {@code DerValue} object built from a SEQUENCE of741* extensions742*743* @return a {@code Map} using the OID in string form as the keys. If no744* extensions are found or an empty SEQUENCE is passed in, then745* an empty {@code Map} will be returned.746*747* @throws IOException if any decoding errors occur.748*/749private static Map<String, java.security.cert.Extension>750parseExtensions(DerValue derVal) throws IOException {751DerValue[] extDer = derVal.data.getSequence(3);752Map<String, java.security.cert.Extension> extMap =753new HashMap<>(extDer.length);754755for (DerValue extDerVal : extDer) {756Extension ext = new Extension(extDerVal);757if (debug != null) {758debug.println("Extension: " + ext);759}760// We don't support any extensions yet. Therefore, if it761// is critical we must throw an exception because we762// don't know how to process it.763if (ext.isCritical()) {764throw new IOException("Unsupported OCSP critical extension: " +765ext.getExtensionId());766}767extMap.put(ext.getId(), ext);768}769770return extMap;771}772773/*774* A class representing a single OCSP response.775*/776public static final class SingleResponse implements OCSP.RevocationStatus {777private final CertId certId;778private final CertStatus certStatus;779private final Date thisUpdate;780private final Date nextUpdate;781private final Date revocationTime;782private final CRLReason revocationReason;783private final Map<String, java.security.cert.Extension> singleExtensions;784785private SingleResponse(DerValue der) throws IOException {786if (der.tag != DerValue.tag_Sequence) {787throw new IOException("Bad ASN.1 encoding in SingleResponse");788}789DerInputStream tmp = der.data;790791certId = new CertId(tmp.getDerValue().data);792DerValue derVal = tmp.getDerValue();793short tag = (byte)(derVal.tag & 0x1f);794if (tag == CERT_STATUS_REVOKED) {795certStatus = CertStatus.REVOKED;796revocationTime = derVal.data.getGeneralizedTime();797if (derVal.data.available() != 0) {798DerValue dv = derVal.data.getDerValue();799tag = (byte)(dv.tag & 0x1f);800if (tag == 0) {801int reason = dv.data.getEnumerated();802// if reason out-of-range just leave as UNSPECIFIED803if (reason >= 0 && reason < values.length) {804revocationReason = values[reason];805} else {806revocationReason = CRLReason.UNSPECIFIED;807}808} else {809revocationReason = CRLReason.UNSPECIFIED;810}811} else {812revocationReason = CRLReason.UNSPECIFIED;813}814// RevokedInfo815if (debug != null) {816debug.println("Revocation time: " + revocationTime);817debug.println("Revocation reason: " + revocationReason);818}819} else {820revocationTime = null;821revocationReason = null;822if (tag == CERT_STATUS_GOOD) {823certStatus = CertStatus.GOOD;824} else if (tag == CERT_STATUS_UNKNOWN) {825certStatus = CertStatus.UNKNOWN;826} else {827throw new IOException("Invalid certificate status");828}829}830831thisUpdate = tmp.getGeneralizedTime();832if (debug != null) {833debug.println("thisUpdate: " + thisUpdate);834}835836// Parse optional fields like nextUpdate and singleExtensions837Date tmpNextUpdate = null;838Map<String, java.security.cert.Extension> tmpMap = null;839840// Check for the first optional item, it could be nextUpdate841// [CONTEXT 0] or singleExtensions [CONTEXT 1]842if (tmp.available() > 0) {843derVal = tmp.getDerValue();844845// nextUpdate processing846if (derVal.isContextSpecific((byte)0)) {847tmpNextUpdate = derVal.data.getGeneralizedTime();848if (debug != null) {849debug.println("nextUpdate: " + tmpNextUpdate);850}851852// If more data exists in the singleResponse, it853// can only be singleExtensions. Get this DER value854// for processing in the next block855derVal = tmp.available() > 0 ? tmp.getDerValue() : null;856}857858// singleExtensions processing859if (derVal != null) {860if (derVal.isContextSpecific((byte)1)) {861tmpMap = parseExtensions(derVal);862863// There should not be any other items in the864// singleResponse at this point.865if (tmp.available() > 0) {866throw new IOException(tmp.available() +867" bytes of additional data in singleResponse");868}869} else {870// Unknown item in the singleResponse871throw new IOException("Unsupported singleResponse " +872"item, tag = " + String.format("%02X", derVal.tag));873}874}875}876877nextUpdate = tmpNextUpdate;878singleExtensions = (tmpMap != null) ? tmpMap :879Collections.emptyMap();880if (debug != null) {881for (java.security.cert.Extension ext :882singleExtensions.values()) {883debug.println("singleExtension: " + ext);884}885}886}887888/*889* Return the certificate's revocation status code890*/891@Override892public CertStatus getCertStatus() {893return certStatus;894}895896/**897* Get the Cert ID that this SingleResponse is for.898*899* @return the {@code CertId} for this {@code SingleResponse}900*/901public CertId getCertId() {902return certId;903}904905/**906* Get the {@code thisUpdate} field from this {@code SingleResponse}.907*908* @return a {@link Date} object containing the thisUpdate date909*/910public Date getThisUpdate() {911return (thisUpdate != null ? (Date) thisUpdate.clone() : null);912}913914/**915* Get the {@code nextUpdate} field from this {@code SingleResponse}.916*917* @return a {@link Date} object containing the nexUpdate date or918* {@code null} if a nextUpdate field is not present in the response.919*/920public Date getNextUpdate() {921return (nextUpdate != null ? (Date) nextUpdate.clone() : null);922}923924/**925* Get the {@code revocationTime} field from this926* {@code SingleResponse}.927*928* @return a {@link Date} object containing the revocationTime date or929* {@code null} if the {@code SingleResponse} does not have a status930* of {@code REVOKED}.931*/932@Override933public Date getRevocationTime() {934return (revocationTime != null ? (Date) revocationTime.clone() :935null);936}937938/**939* Get the {@code revocationReason} field for the940* {@code SingleResponse}.941*942* @return a {@link CRLReason} containing the revocation reason, or943* {@code null} if a revocation reason was not provided or the944* response status is not {@code REVOKED}.945*/946@Override947public CRLReason getRevocationReason() {948return revocationReason;949}950951/**952* Get the {@code singleExtensions} for this {@code SingleResponse}.953*954* @return a {@link Map} of {@link Extension} objects, keyed by955* their OID value in string form.956*/957@Override958public Map<String, java.security.cert.Extension> getSingleExtensions() {959return Collections.unmodifiableMap(singleExtensions);960}961962/**963* Construct a string representation of a single OCSP response.964*/965@Override public String toString() {966StringBuilder sb = new StringBuilder();967sb.append("SingleResponse:\n");968sb.append(certId);969sb.append("\nCertStatus: ").append(certStatus).append("\n");970if (certStatus == CertStatus.REVOKED) {971sb.append("revocationTime is ");972sb.append(revocationTime).append("\n");973sb.append("revocationReason is ");974sb.append(revocationReason).append("\n");975}976sb.append("thisUpdate is ").append(thisUpdate).append("\n");977if (nextUpdate != null) {978sb.append("nextUpdate is ").append(nextUpdate).append("\n");979}980for (java.security.cert.Extension ext : singleExtensions.values()) {981sb.append("singleExtension: ");982sb.append(ext.toString()).append("\n");983}984return sb.toString();985}986}987988/**989* Helper class that allows consumers to pass in issuer information. This990* will always consist of the issuer's name and public key, but may also991* contain a certificate if the originating data is in that form. The992* trust anchor for the certificate chain will be included for certpath993* disabled algorithm checking.994*/995static final class IssuerInfo {996private final TrustAnchor anchor;997private final X509Certificate certificate;998private final X500Principal name;999private final PublicKey pubKey;10001001IssuerInfo(TrustAnchor anchor) {1002this(anchor, (anchor != null) ? anchor.getTrustedCert() : null);1003}10041005IssuerInfo(X509Certificate issuerCert) {1006this(null, issuerCert);1007}10081009IssuerInfo(TrustAnchor anchor, X509Certificate issuerCert) {1010if (anchor == null && issuerCert == null) {1011throw new NullPointerException("TrustAnchor and issuerCert " +1012"cannot be null");1013}1014this.anchor = anchor;1015if (issuerCert != null) {1016name = issuerCert.getSubjectX500Principal();1017pubKey = issuerCert.getPublicKey();1018certificate = issuerCert;1019} else {1020name = anchor.getCA();1021pubKey = anchor.getCAPublicKey();1022certificate = anchor.getTrustedCert();1023}1024}10251026/**1027* Get the certificate in this IssuerInfo if present.1028*1029* @return the {@code X509Certificate} used to create this IssuerInfo1030* object, or {@code null} if a certificate was not used in its1031* creation.1032*/1033X509Certificate getCertificate() {1034return certificate;1035}10361037/**1038* Get the name of this issuer.1039*1040* @return an {@code X500Principal} corresponding to this issuer's1041* name. If derived from an issuer's {@code X509Certificate} this1042* would be equivalent to the certificate subject name.1043*/1044X500Principal getName() {1045return name;1046}10471048/**1049* Get the public key for this issuer.1050*1051* @return a {@code PublicKey} for this issuer.1052*/1053PublicKey getPublicKey() {1054return pubKey;1055}10561057/**1058* Get the TrustAnchor for the certificate chain.1059*1060* @return a {@code TrustAnchor}.1061*/1062TrustAnchor getAnchor() {1063return anchor;1064}10651066/**1067* Create a string representation of this IssuerInfo.1068*1069* @return a {@code String} form of this IssuerInfo object.1070*/1071@Override1072public String toString() {1073StringBuilder sb = new StringBuilder();1074sb.append("Issuer Info:\n");1075sb.append("Name: ").append(name.toString()).append("\n");1076sb.append("Public Key:\n").append(pubKey.toString()).append("\n");1077return sb.toString();1078}1079}1080}108110821083