Path: blob/master/src/java.base/share/classes/sun/security/provider/certpath/OCSPResponse.java
41161 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());640respSignature.initVerify(cert.getPublicKey());641respSignature.update(tbsResponseData);642643if (respSignature.verify(signature)) {644if (debug != null) {645debug.println("Verified signature of OCSP Response");646}647return true;648649} else {650if (debug != null) {651debug.println(652"Error verifying signature of OCSP Response");653}654return false;655}656} catch (InvalidKeyException | NoSuchAlgorithmException |657SignatureException e)658{659throw new CertPathValidatorException(e);660}661}662663/**664* Returns the SingleResponse of the specified CertId, or null if665* there is no response for that CertId.666*667* @param certId the {@code CertId} for a {@code SingleResponse} to be668* searched for in the OCSP response.669*670* @return the {@code SingleResponse} for the provided {@code CertId},671* or {@code null} if it is not found.672*/673public SingleResponse getSingleResponse(CertId certId) {674return singleResponseMap.get(certId);675}676677/**678* Return a set of all CertIds in this {@code OCSPResponse}679*680* @return an unmodifiable set containing every {@code CertId} in this681* response.682*/683public Set<CertId> getCertIds() {684return Collections.unmodifiableSet(singleResponseMap.keySet());685}686687/*688* Returns the certificate for the authority that signed the OCSP response.689*/690X509Certificate getSignerCertificate() {691return signerCert; // set in verify()692}693694/**695* Get the {@code ResponderId} from this {@code OCSPResponse}696*697* @return the {@code ResponderId} from this response or {@code null}698* if no responder ID is in the body of the response (e.g. a699* response with a status other than SUCCESS.700*/701public ResponderId getResponderId() {702return respId;703}704705/**706* Provide a String representation of an OCSPResponse707*708* @return a human-readable representation of the OCSPResponse709*/710@Override711public String toString() {712StringBuilder sb = new StringBuilder();713sb.append("OCSP Response:\n");714sb.append("Response Status: ").append(responseStatus).append("\n");715sb.append("Responder ID: ").append(respId).append("\n");716sb.append("Produced at: ").append(producedAtDate).append("\n");717int count = singleResponseMap.size();718sb.append(count).append(count == 1 ?719" response:\n" : " responses:\n");720for (SingleResponse sr : singleResponseMap.values()) {721sb.append(sr).append("\n");722}723if (responseExtensions != null && responseExtensions.size() > 0) {724count = responseExtensions.size();725sb.append(count).append(count == 1 ?726" extension:\n" : " extensions:\n");727for (String extId : responseExtensions.keySet()) {728sb.append(responseExtensions.get(extId)).append("\n");729}730}731732return sb.toString();733}734735/**736* Build a String-Extension map from DER encoded data.737* @param derVal A {@code DerValue} object built from a SEQUENCE of738* extensions739*740* @return a {@code Map} using the OID in string form as the keys. If no741* extensions are found or an empty SEQUENCE is passed in, then742* an empty {@code Map} will be returned.743*744* @throws IOException if any decoding errors occur.745*/746private static Map<String, java.security.cert.Extension>747parseExtensions(DerValue derVal) throws IOException {748DerValue[] extDer = derVal.data.getSequence(3);749Map<String, java.security.cert.Extension> extMap =750new HashMap<>(extDer.length);751752for (DerValue extDerVal : extDer) {753Extension ext = new Extension(extDerVal);754if (debug != null) {755debug.println("Extension: " + ext);756}757// We don't support any extensions yet. Therefore, if it758// is critical we must throw an exception because we759// don't know how to process it.760if (ext.isCritical()) {761throw new IOException("Unsupported OCSP critical extension: " +762ext.getExtensionId());763}764extMap.put(ext.getId(), ext);765}766767return extMap;768}769770/*771* A class representing a single OCSP response.772*/773public static final class SingleResponse implements OCSP.RevocationStatus {774private final CertId certId;775private final CertStatus certStatus;776private final Date thisUpdate;777private final Date nextUpdate;778private final Date revocationTime;779private final CRLReason revocationReason;780private final Map<String, java.security.cert.Extension> singleExtensions;781782private SingleResponse(DerValue der) throws IOException {783if (der.tag != DerValue.tag_Sequence) {784throw new IOException("Bad ASN.1 encoding in SingleResponse");785}786DerInputStream tmp = der.data;787788certId = new CertId(tmp.getDerValue().data);789DerValue derVal = tmp.getDerValue();790short tag = (byte)(derVal.tag & 0x1f);791if (tag == CERT_STATUS_REVOKED) {792certStatus = CertStatus.REVOKED;793revocationTime = derVal.data.getGeneralizedTime();794if (derVal.data.available() != 0) {795DerValue dv = derVal.data.getDerValue();796tag = (byte)(dv.tag & 0x1f);797if (tag == 0) {798int reason = dv.data.getEnumerated();799// if reason out-of-range just leave as UNSPECIFIED800if (reason >= 0 && reason < values.length) {801revocationReason = values[reason];802} else {803revocationReason = CRLReason.UNSPECIFIED;804}805} else {806revocationReason = CRLReason.UNSPECIFIED;807}808} else {809revocationReason = CRLReason.UNSPECIFIED;810}811// RevokedInfo812if (debug != null) {813debug.println("Revocation time: " + revocationTime);814debug.println("Revocation reason: " + revocationReason);815}816} else {817revocationTime = null;818revocationReason = null;819if (tag == CERT_STATUS_GOOD) {820certStatus = CertStatus.GOOD;821} else if (tag == CERT_STATUS_UNKNOWN) {822certStatus = CertStatus.UNKNOWN;823} else {824throw new IOException("Invalid certificate status");825}826}827828thisUpdate = tmp.getGeneralizedTime();829if (debug != null) {830debug.println("thisUpdate: " + thisUpdate);831}832833// Parse optional fields like nextUpdate and singleExtensions834Date tmpNextUpdate = null;835Map<String, java.security.cert.Extension> tmpMap = null;836837// Check for the first optional item, it could be nextUpdate838// [CONTEXT 0] or singleExtensions [CONTEXT 1]839if (tmp.available() > 0) {840derVal = tmp.getDerValue();841842// nextUpdate processing843if (derVal.isContextSpecific((byte)0)) {844tmpNextUpdate = derVal.data.getGeneralizedTime();845if (debug != null) {846debug.println("nextUpdate: " + tmpNextUpdate);847}848849// If more data exists in the singleResponse, it850// can only be singleExtensions. Get this DER value851// for processing in the next block852derVal = tmp.available() > 0 ? tmp.getDerValue() : null;853}854855// singleExtensions processing856if (derVal != null) {857if (derVal.isContextSpecific((byte)1)) {858tmpMap = parseExtensions(derVal);859860// There should not be any other items in the861// singleResponse at this point.862if (tmp.available() > 0) {863throw new IOException(tmp.available() +864" bytes of additional data in singleResponse");865}866} else {867// Unknown item in the singleResponse868throw new IOException("Unsupported singleResponse " +869"item, tag = " + String.format("%02X", derVal.tag));870}871}872}873874nextUpdate = tmpNextUpdate;875singleExtensions = (tmpMap != null) ? tmpMap :876Collections.emptyMap();877if (debug != null) {878for (java.security.cert.Extension ext :879singleExtensions.values()) {880debug.println("singleExtension: " + ext);881}882}883}884885/*886* Return the certificate's revocation status code887*/888@Override889public CertStatus getCertStatus() {890return certStatus;891}892893/**894* Get the Cert ID that this SingleResponse is for.895*896* @return the {@code CertId} for this {@code SingleResponse}897*/898public CertId getCertId() {899return certId;900}901902/**903* Get the {@code thisUpdate} field from this {@code SingleResponse}.904*905* @return a {@link Date} object containing the thisUpdate date906*/907public Date getThisUpdate() {908return (thisUpdate != null ? (Date) thisUpdate.clone() : null);909}910911/**912* Get the {@code nextUpdate} field from this {@code SingleResponse}.913*914* @return a {@link Date} object containing the nexUpdate date or915* {@code null} if a nextUpdate field is not present in the response.916*/917public Date getNextUpdate() {918return (nextUpdate != null ? (Date) nextUpdate.clone() : null);919}920921/**922* Get the {@code revocationTime} field from this923* {@code SingleResponse}.924*925* @return a {@link Date} object containing the revocationTime date or926* {@code null} if the {@code SingleResponse} does not have a status927* of {@code REVOKED}.928*/929@Override930public Date getRevocationTime() {931return (revocationTime != null ? (Date) revocationTime.clone() :932null);933}934935/**936* Get the {@code revocationReason} field for the937* {@code SingleResponse}.938*939* @return a {@link CRLReason} containing the revocation reason, or940* {@code null} if a revocation reason was not provided or the941* response status is not {@code REVOKED}.942*/943@Override944public CRLReason getRevocationReason() {945return revocationReason;946}947948/**949* Get the {@code singleExtensions} for this {@code SingleResponse}.950*951* @return a {@link Map} of {@link Extension} objects, keyed by952* their OID value in string form.953*/954@Override955public Map<String, java.security.cert.Extension> getSingleExtensions() {956return Collections.unmodifiableMap(singleExtensions);957}958959/**960* Construct a string representation of a single OCSP response.961*/962@Override public String toString() {963StringBuilder sb = new StringBuilder();964sb.append("SingleResponse:\n");965sb.append(certId);966sb.append("\nCertStatus: ").append(certStatus).append("\n");967if (certStatus == CertStatus.REVOKED) {968sb.append("revocationTime is ");969sb.append(revocationTime).append("\n");970sb.append("revocationReason is ");971sb.append(revocationReason).append("\n");972}973sb.append("thisUpdate is ").append(thisUpdate).append("\n");974if (nextUpdate != null) {975sb.append("nextUpdate is ").append(nextUpdate).append("\n");976}977for (java.security.cert.Extension ext : singleExtensions.values()) {978sb.append("singleExtension: ");979sb.append(ext.toString()).append("\n");980}981return sb.toString();982}983}984985/**986* Helper class that allows consumers to pass in issuer information. This987* will always consist of the issuer's name and public key, but may also988* contain a certificate if the originating data is in that form. The989* trust anchor for the certificate chain will be included for certpath990* disabled algorithm checking.991*/992static final class IssuerInfo {993private final TrustAnchor anchor;994private final X509Certificate certificate;995private final X500Principal name;996private final PublicKey pubKey;997998IssuerInfo(TrustAnchor anchor) {999this(anchor, (anchor != null) ? anchor.getTrustedCert() : null);1000}10011002IssuerInfo(X509Certificate issuerCert) {1003this(null, issuerCert);1004}10051006IssuerInfo(TrustAnchor anchor, X509Certificate issuerCert) {1007if (anchor == null && issuerCert == null) {1008throw new NullPointerException("TrustAnchor and issuerCert " +1009"cannot be null");1010}1011this.anchor = anchor;1012if (issuerCert != null) {1013name = issuerCert.getSubjectX500Principal();1014pubKey = issuerCert.getPublicKey();1015certificate = issuerCert;1016} else {1017name = anchor.getCA();1018pubKey = anchor.getCAPublicKey();1019certificate = anchor.getTrustedCert();1020}1021}10221023/**1024* Get the certificate in this IssuerInfo if present.1025*1026* @return the {@code X509Certificate} used to create this IssuerInfo1027* object, or {@code null} if a certificate was not used in its1028* creation.1029*/1030X509Certificate getCertificate() {1031return certificate;1032}10331034/**1035* Get the name of this issuer.1036*1037* @return an {@code X500Principal} corresponding to this issuer's1038* name. If derived from an issuer's {@code X509Certificate} this1039* would be equivalent to the certificate subject name.1040*/1041X500Principal getName() {1042return name;1043}10441045/**1046* Get the public key for this issuer.1047*1048* @return a {@code PublicKey} for this issuer.1049*/1050PublicKey getPublicKey() {1051return pubKey;1052}10531054/**1055* Get the TrustAnchor for the certificate chain.1056*1057* @return a {@code TrustAnchor}.1058*/1059TrustAnchor getAnchor() {1060return anchor;1061}10621063/**1064* Create a string representation of this IssuerInfo.1065*1066* @return a {@code String} form of this IssuerInfo object.1067*/1068@Override1069public String toString() {1070StringBuilder sb = new StringBuilder();1071sb.append("Issuer Info:\n");1072sb.append("Name: ").append(name.toString()).append("\n");1073sb.append("Public Key:\n").append(pubKey.toString()).append("\n");1074return sb.toString();1075}1076}1077}107810791080