0N/A/*
4278N/A * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
0N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
0N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
1472N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
1472N/A *
1472N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
0N/A * or visit www.oracle.com if you need additional information or have any
0N/A * questions.
0N/A */
1879N/A
1879N/Apackage sun.security.provider.certpath;
1879N/A
1879N/Aimport java.io.*;
1879N/Aimport java.math.BigInteger;
1879N/Aimport java.security.*;
1879N/Aimport java.security.cert.CertificateException;
1879N/Aimport java.security.cert.CertificateParsingException;
1879N/Aimport java.security.cert.CertPathValidatorException;
1879N/Aimport java.security.cert.CRLReason;
1879N/Aimport java.security.cert.TrustAnchor;
1879N/Aimport java.security.cert.X509Certificate;
1879N/Aimport java.util.Arrays;
1879N/Aimport java.util.Collections;
1879N/Aimport java.util.Date;
1879N/Aimport java.util.HashMap;
1879N/Aimport java.util.List;
1879N/Aimport java.util.Map;
1879N/Aimport sun.misc.HexDumpEncoder;
1879N/Aimport sun.security.action.GetIntegerAction;
1879N/Aimport sun.security.x509.*;
4340N/Aimport sun.security.util.*;
1879N/A
1879N/A/**
1879N/A * This class is used to process an OCSP response.
1879N/A * The OCSP Response is defined
1879N/A * in RFC 2560 and the ASN.1 encoding is as follows:
1879N/A * <pre>
1879N/A *
1879N/A * OCSPResponse ::= SEQUENCE {
1879N/A * responseStatus OCSPResponseStatus,
1879N/A * responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
1879N/A *
1879N/A * OCSPResponseStatus ::= ENUMERATED {
4299N/A * successful (0), --Response has valid confirmations
1879N/A * malformedRequest (1), --Illegal confirmation request
1879N/A * internalError (2), --Internal error in issuer
1929N/A * tryLater (3), --Try again later
1879N/A * --(4) is not used
1879N/A * sigRequired (5), --Must sign the request
4340N/A * unauthorized (6) --Request unauthorized
1879N/A * }
1879N/A *
1879N/A * ResponseBytes ::= SEQUENCE {
1879N/A * responseType OBJECT IDENTIFIER,
1879N/A * response OCTET STRING }
1879N/A *
1879N/A * BasicOCSPResponse ::= SEQUENCE {
1879N/A * tbsResponseData ResponseData,
1879N/A * signatureAlgorithm AlgorithmIdentifier,
1879N/A * signature BIT STRING,
1879N/A * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
1879N/A *
1879N/A * The value for signature SHALL be computed on the hash of the DER
1879N/A * encoding ResponseData.
2073N/A *
2073N/A * ResponseData ::= SEQUENCE {
2073N/A * version [0] EXPLICIT Version DEFAULT v1,
2073N/A * responderID ResponderID,
2073N/A * producedAt GeneralizedTime,
2073N/A * responses SEQUENCE OF SingleResponse,
2073N/A * responseExtensions [1] EXPLICIT Extensions OPTIONAL }
2073N/A *
0N/A * ResponderID ::= CHOICE {
0N/A * byName [1] Name,
0N/A * byKey [2] KeyHash }
0N/A *
1601N/A * KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
1601N/A * (excluding the tag and length fields)
0N/A *
0N/A * SingleResponse ::= SEQUENCE {
0N/A * certID CertID,
0N/A * certStatus CertStatus,
0N/A * thisUpdate GeneralizedTime,
0N/A * nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
0N/A * singleExtensions [1] EXPLICIT Extensions OPTIONAL }
0N/A *
0N/A * CertStatus ::= CHOICE {
0N/A * good [0] IMPLICIT NULL,
0N/A * revoked [1] IMPLICIT RevokedInfo,
0N/A * unknown [2] IMPLICIT UnknownInfo }
0N/A *
0N/A * RevokedInfo ::= SEQUENCE {
0N/A * revocationTime GeneralizedTime,
0N/A * revocationReason [0] EXPLICIT CRLReason OPTIONAL }
0N/A *
0N/A * UnknownInfo ::= NULL -- this can be replaced with an enumeration
0N/A *
0N/A * </pre>
0N/A *
0N/A * @author Ram Marti
0N/A */
0N/A
0N/Apublic final class OCSPResponse {
1320N/A
1320N/A public enum ResponseStatus {
1940N/A SUCCESSFUL, // Response has valid confirmations
0N/A MALFORMED_REQUEST, // Illegal confirmation request
0N/A INTERNAL_ERROR, // Internal error in issuer
0N/A TRY_LATER, // Try again later
0N/A UNUSED, // is not used
0N/A SIG_REQUIRED, // Must sign the request
0N/A UNAUTHORIZED // Request unauthorized
2085N/A };
0N/A private static ResponseStatus[] rsvalues = ResponseStatus.values();
0N/A
0N/A private static final Debug DEBUG = Debug.getInstance("certpath");
0N/A private static final boolean dump = DEBUG.isOn("ocsp");
0N/A private static final ObjectIdentifier OCSP_BASIC_RESPONSE_OID =
0N/A ObjectIdentifier.newInternal(new int[] { 1, 3, 6, 1, 5, 5, 7, 48, 1, 1});
0N/A private static final ObjectIdentifier OCSP_NONCE_EXTENSION_OID =
0N/A ObjectIdentifier.newInternal(new int[] { 1, 3, 6, 1, 5, 5, 7, 48, 1, 2});
0N/A
0N/A private static final int CERT_STATUS_GOOD = 0;
0N/A private static final int CERT_STATUS_REVOKED = 1;
0N/A private static final int CERT_STATUS_UNKNOWN = 2;
4531N/A
0N/A // ResponderID CHOICE tags
0N/A private static final int NAME_TAG = 1;
0N/A private static final int KEY_TAG = 2;
199N/A
199N/A // Object identifier for the OCSPSigning key purpose
0N/A private static final String KP_OCSP_SIGNING_OID = "1.3.6.1.5.5.7.3.9";
0N/A
0N/A private final ResponseStatus responseStatus;
0N/A private final Map<CertId, SingleResponse> singleResponseMap;
0N/A
0N/A // Default maximum clock skew in milliseconds (15 minutes)
0N/A // allowed when checking validity of OCSP responses
0N/A private static final int DEFAULT_MAX_CLOCK_SKEW = 900000;
0N/A
0N/A /**
0N/A * Integer value indicating the maximum allowable clock skew, in seconds,
0N/A * to be used for the OCSP check.
0N/A */
0N/A private static final int MAX_CLOCK_SKEW = initializeClockSkew();
0N/A
0N/A /**
0N/A * Initialize the maximum allowable clock skew by getting the OCSP
242N/A * clock skew system property. If the property has not been set, or if its
242N/A * value is negative, set the skew to the default.
242N/A */
4433N/A private static int initializeClockSkew() {
4433N/A Integer tmp = java.security.AccessController.doPrivileged(
4433N/A new GetIntegerAction("com.sun.security.ocsp.clockSkew"));
2627N/A if (tmp == null || tmp < 0) {
2627N/A return DEFAULT_MAX_CLOCK_SKEW;
2627N/A }
2627N/A // Convert to milliseconds, as the system property will be
2627N/A // specified in seconds
2627N/A return tmp * 1000;
2627N/A }
2627N/A
2627N/A // an array of all of the CRLReasons (used in SingleResponse)
2627N/A private static CRLReason[] values = CRLReason.values();
2627N/A
2627N/A /*
2627N/A * Create an OCSP response from its ASN.1 DER encoding.
2627N/A */
2627N/A OCSPResponse(byte[] bytes, Date dateCheckedAgainst,
2627N/A List<X509Certificate> responderCerts)
2627N/A throws IOException, CertPathValidatorException {
2627N/A
2627N/A // OCSPResponse
2627N/A if (dump) {
2627N/A HexDumpEncoder hexEnc = new HexDumpEncoder();
2627N/A DEBUG.println("\nOCSPResponse bytes...");
2627N/A DEBUG.println(hexEnc.encode(bytes) + "\n");
2627N/A }
2627N/A DerValue der = new DerValue(bytes);
2627N/A if (der.tag != DerValue.tag_Sequence) {
2627N/A throw new IOException("Bad encoding in OCSP response: " +
2627N/A "expected ASN.1 SEQUENCE tag.");
2627N/A }
0N/A DerInputStream derIn = der.getData();
0N/A
0N/A // responseStatus
0N/A int status = derIn.getEnumerated();
0N/A if (status >= 0 && status < rsvalues.length) {
0N/A responseStatus = rsvalues[status];
0N/A } else {
0N/A // unspecified responseStatus
0N/A throw new IOException("Unknown OCSPResponse status: " + status);
0N/A }
0N/A if (DEBUG != null) {
0N/A DEBUG.println("OCSP response status: " + responseStatus);
0N/A }
0N/A if (responseStatus != ResponseStatus.SUCCESSFUL) {
0N/A // no need to continue, responseBytes are not set.
0N/A singleResponseMap = Collections.emptyMap();
0N/A return;
0N/A }
0N/A
0N/A // responseBytes
0N/A der = derIn.getDerValue();
20N/A if (!der.isContextSpecific((byte)0)) {
20N/A throw new IOException("Bad encoding in responseBytes element " +
20N/A "of OCSP response: expected ASN.1 context specific tag 0.");
20N/A }
20N/A DerValue tmp = der.data.getDerValue();
20N/A if (tmp.tag != DerValue.tag_Sequence) {
20N/A throw new IOException("Bad encoding in responseBytes element " +
20N/A "of OCSP response: expected ASN.1 SEQUENCE tag.");
20N/A }
20N/A
20N/A // responseType
20N/A derIn = tmp.data;
20N/A ObjectIdentifier responseType = derIn.getOID();
20N/A if (responseType.equals((Object)OCSP_BASIC_RESPONSE_OID)) {
0N/A if (DEBUG != null) {
0N/A DEBUG.println("OCSP response type: basic");
0N/A }
0N/A } else {
0N/A if (DEBUG != null) {
0N/A DEBUG.println("OCSP response type: " + responseType);
0N/A }
0N/A throw new IOException("Unsupported OCSP response type: " +
0N/A responseType);
0N/A }
0N/A
0N/A // BasicOCSPResponse
0N/A DerInputStream basicOCSPResponse =
0N/A new DerInputStream(derIn.getOctetString());
0N/A
0N/A DerValue[] seqTmp = basicOCSPResponse.getSequence(2);
0N/A if (seqTmp.length < 3) {
0N/A throw new IOException("Unexpected BasicOCSPResponse value");
0N/A }
0N/A
0N/A DerValue responseData = seqTmp[0];
0N/A
0N/A // Need the DER encoded ResponseData to verify the signature later
0N/A byte[] responseDataDer = seqTmp[0].toByteArray();
0N/A
0N/A // tbsResponseData
0N/A if (responseData.tag != DerValue.tag_Sequence) {
0N/A throw new IOException("Bad encoding in tbsResponseData " +
0N/A "element of OCSP response: expected ASN.1 SEQUENCE tag.");
0N/A }
0N/A DerInputStream seqDerIn = responseData.data;
0N/A DerValue seq = seqDerIn.getDerValue();
0N/A
0N/A // version
0N/A if (seq.isContextSpecific((byte)0)) {
0N/A // seq[0] is version
0N/A if (seq.isConstructed() && seq.isContextSpecific()) {
0N/A //System.out.println ("version is available");
0N/A seq = seq.data.getDerValue();
0N/A int version = seq.getInteger();
0N/A if (seq.data.available() != 0) {
0N/A throw new IOException("Bad encoding in version " +
0N/A " element of OCSP response: bad format");
1010N/A }
1010N/A seq = seqDerIn.getDerValue();
1010N/A }
0N/A }
0N/A
0N/A // responderID
0N/A short tag = (byte)(seq.tag & 0x1f);
0N/A if (tag == NAME_TAG) {
1601N/A if (DEBUG != null) {
1601N/A X500Name responderName = new X500Name(seq.getData());
1601N/A DEBUG.println("OCSP Responder name: " + responderName);
1601N/A }
0N/A } else if (tag == KEY_TAG) {
0N/A seq = seq.data.getDerValue(); // consume tag and length
0N/A if (DEBUG != null) {
0N/A byte[] responderKeyId = seq.getOctetString();
0N/A DEBUG.println("OCSP Responder key ID: " +
0N/A String.format("0x%0" +
0N/A (responderKeyId.length * 2) + "x",
0N/A new BigInteger(1, responderKeyId)));
0N/A }
0N/A } else {
0N/A throw new IOException("Bad encoding in responderID element of " +
0N/A "OCSP response: expected ASN.1 context specific tag 1 or 2");
0N/A }
0N/A
0N/A // producedAt
0N/A seq = seqDerIn.getDerValue();
0N/A if (DEBUG != null) {
0N/A Date producedAtDate = seq.getGeneralizedTime();
0N/A DEBUG.println("OCSP response produced at: " + producedAtDate);
0N/A }
0N/A
0N/A // responses
0N/A DerValue[] singleResponseDer = seqDerIn.getSequence(1);
0N/A singleResponseMap
0N/A = new HashMap<CertId, SingleResponse>(singleResponseDer.length);
0N/A if (DEBUG != null) {
0N/A DEBUG.println("OCSP number of SingleResponses: "
0N/A + singleResponseDer.length);
0N/A }
0N/A for (int i = 0; i < singleResponseDer.length; i++) {
0N/A SingleResponse singleResponse
0N/A = new SingleResponse(singleResponseDer[i], dateCheckedAgainst);
0N/A singleResponseMap.put(singleResponse.getCertId(), singleResponse);
0N/A }
199N/A
199N/A // responseExtensions
199N/A if (seqDerIn.available() > 0) {
0N/A seq = seqDerIn.getDerValue();
0N/A if (seq.isContextSpecific((byte)1)) {
1123N/A DerValue[] responseExtDer = seq.data.getSequence(3);
1123N/A for (int i = 0; i < responseExtDer.length; i++) {
0N/A Extension responseExtension
0N/A = new Extension(responseExtDer[i]);
0N/A if (DEBUG != null) {
0N/A DEBUG.println("OCSP extension: " + responseExtension);
0N/A }
0N/A if (responseExtension.getExtensionId().equals((Object)
0N/A OCSP_NONCE_EXTENSION_OID)) {
0N/A /*
0N/A ocspNonce =
0N/A responseExtension[i].getExtensionValue();
0N/A */
1123N/A } else if (responseExtension.isCritical()) {
0N/A throw new IOException(
0N/A "Unsupported OCSP critical extension: " +
0N/A responseExtension.getExtensionId());
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A // signatureAlgorithmId
0N/A AlgorithmId sigAlgId = AlgorithmId.parse(seqTmp[1]);
0N/A
0N/A // signature
0N/A byte[] signature = seqTmp[2].getBitString();
0N/A X509CertImpl[] x509Certs = null;
0N/A
0N/A // if seq[3] is available , then it is a sequence of certificates
0N/A if (seqTmp.length > 3) {
0N/A // certs are available
0N/A DerValue seqCert = seqTmp[3];
0N/A if (!seqCert.isContextSpecific((byte)0)) {
0N/A throw new IOException("Bad encoding in certs element of " +
0N/A "OCSP response: expected ASN.1 context specific tag 0.");
0N/A }
0N/A DerValue[] certs = seqCert.getData().getSequence(3);
0N/A x509Certs = new X509CertImpl[certs.length];
0N/A try {
0N/A for (int i = 0; i < certs.length; i++) {
0N/A x509Certs[i] = new X509CertImpl(certs[i].toByteArray());
0N/A }
0N/A } catch (CertificateException ce) {
0N/A throw new IOException("Bad encoding in X509 Certificate", ce);
0N/A }
0N/A }
3863N/A
0N/A // By default, the OCSP responder's cert is the same as the issuer of
0N/A // the cert being validated. The issuer cert is the first in the list.
0N/A X509Certificate trustedResponderCert = responderCerts.get(0);
0N/A
0N/A // Check whether the signer cert returned by the responder is trusted
0N/A if (x509Certs != null && x509Certs[0] != null) {
0N/A X509CertImpl signerCert = x509Certs[0];
0N/A
0N/A if (DEBUG != null) {
0N/A DEBUG.println("Signer certificate name: " +
509N/A signerCert.getSubjectX500Principal());
509N/A
509N/A byte[] signerKeyId = signerCert.getSubjectKeyIdentifier();
0N/A if (signerKeyId != null) {
509N/A DEBUG.println("Signer certificate key ID: " +
0N/A String.format("0x%0" + (signerKeyId.length * 2) + "x",
0N/A new BigInteger(1, signerKeyId)));
0N/A }
0N/A }
0N/A
0N/A byte[] certIssuerKeyId = null;
0N/A
0N/A for (X509Certificate responderCert : responderCerts) {
0N/A
0N/A // First check if signer cert matches a trusted responder cert
0N/A if (signerCert.equals(responderCert)) {
0N/A
0N/A // signer cert is trusted, now verify the signed response
0N/A trustedResponderCert = responderCert;
0N/A if (DEBUG != null) {
0N/A DEBUG.println("Signer certificate is a trusted " +
0N/A "responder");
0N/A }
0N/A break;
0N/A
0N/A // Next check if signer cert was issued by a trusted responder
0N/A // cert
0N/A } else if (signerCert.getIssuerX500Principal().equals(
0N/A responderCert.getSubjectX500Principal())) {
0N/A
0N/A // Retrieve the issuer's key identifier
0N/A if (certIssuerKeyId == null) {
0N/A certIssuerKeyId = signerCert.getIssuerKeyIdentifier();
0N/A if (certIssuerKeyId == null) {
0N/A if (DEBUG != null) {
0N/A DEBUG.println("No issuer key identifier (AKID) "
0N/A + "in the signer certificate");
0N/A }
0N/A }
0N/A }
0N/A
0N/A // Check that the key identifiers match, if both are present
0N/A byte[] responderKeyId = null;
0N/A if (certIssuerKeyId != null &&
0N/A (responderKeyId =
0N/A OCSPChecker.getKeyId(responderCert)) != null) {
0N/A if (!Arrays.equals(certIssuerKeyId, responderKeyId)) {
0N/A continue; // try next cert
0N/A }
0N/A
0N/A if (DEBUG != null) {
0N/A DEBUG.println("Issuer certificate key ID: " +
0N/A String.format("0x%0" +
0N/A (certIssuerKeyId.length * 2) + "x",
0N/A new BigInteger(1, certIssuerKeyId)));
0N/A }
0N/A }
0N/A
0N/A // Check for the OCSPSigning key purpose
0N/A try {
0N/A List<String> keyPurposes =
0N/A signerCert.getExtendedKeyUsage();
0N/A if (keyPurposes == null ||
0N/A !keyPurposes.contains(KP_OCSP_SIGNING_OID)) {
0N/A
0N/A continue; // try next cert
0N/A }
0N/A } catch (CertificateParsingException cpe) {
0N/A
0N/A continue; // try next cert
0N/A }
0N/A
0N/A // Check algorithm constraints specified in security
0N/A // property "jdk.certpath.disabledAlgorithms".
0N/A AlgorithmChecker algChecker = new AlgorithmChecker(
0N/A new TrustAnchor(responderCert, null));
0N/A algChecker.init(false);
0N/A algChecker.check(signerCert,
0N/A Collections.<String>emptySet());
0N/A
0N/A // Check the date validity
0N/A try {
0N/A if (dateCheckedAgainst == null) {
0N/A signerCert.checkValidity();
0N/A } else {
0N/A signerCert.checkValidity(dateCheckedAgainst);
0N/A }
0N/A } catch (GeneralSecurityException e) {
0N/A if (DEBUG != null) {
0N/A DEBUG.println("Responder's certificate not within" +
0N/A " the validity period " + e);
0N/A }
0N/A continue; // try next cert
0N/A }
0N/A
0N/A // Check for revocation
0N/A //
0N/A // A CA may specify that an OCSP client can trust a
0N/A // responder for the lifetime of the responder's
0N/A // certificate. The CA does so by including the
0N/A // extension id-pkix-ocsp-nocheck.
0N/A //
0N/A Extension noCheck =
0N/A signerCert.getExtension(PKIXExtensions.OCSPNoCheck_Id);
0N/A if (noCheck != null) {
0N/A if (DEBUG != null) {
0N/A DEBUG.println("Responder's certificate includes " +
0N/A "the extension id-pkix-ocsp-nocheck.");
0N/A }
0N/A } else {
0N/A // we should do the revocation checking of the
0N/A // authorized responder in a future update.
0N/A }
0N/A
0N/A // Verify the signature
0N/A try {
0N/A signerCert.verify(responderCert.getPublicKey());
0N/A trustedResponderCert = signerCert;
0N/A // cert is trusted, now verify the signed response
0N/A if (DEBUG != null) {
0N/A DEBUG.println("Signer certificate was issued by " +
0N/A "a trusted responder");
0N/A }
0N/A break;
0N/A
0N/A } catch (GeneralSecurityException e) {
0N/A trustedResponderCert = null;
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A // Confirm that the signed response was generated using the public
0N/A // key from the trusted responder cert
0N/A if (trustedResponderCert != null) {
0N/A // Check algorithm constraints specified in security property
0N/A // "jdk.certpath.disabledAlgorithms".
0N/A AlgorithmChecker.check(trustedResponderCert.getPublicKey(),
0N/A sigAlgId);
0N/A
0N/A if (!verifyResponse(responseDataDer, trustedResponderCert,
0N/A sigAlgId, signature)) {
0N/A throw new CertPathValidatorException(
0N/A "Error verifying OCSP Responder's signature");
0N/A }
0N/A } else {
0N/A // Need responder's cert in order to verify the signature
0N/A throw new CertPathValidatorException(
0N/A "Responder's certificate is not trusted for signing " +
0N/A "OCSP responses");
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns the OCSP ResponseStatus.
0N/A */
0N/A ResponseStatus getResponseStatus() {
0N/A return responseStatus;
0N/A }
0N/A
0N/A /*
0N/A * Verify the signature of the OCSP response.
0N/A * The responder's cert is implicitly trusted.
0N/A */
0N/A private boolean verifyResponse(byte[] responseData, X509Certificate cert,
0N/A AlgorithmId sigAlgId, byte[] signBytes)
0N/A throws CertPathValidatorException {
0N/A
0N/A try {
0N/A Signature respSignature = Signature.getInstance(sigAlgId.getName());
0N/A respSignature.initVerify(cert.getPublicKey());
0N/A respSignature.update(responseData);
0N/A
0N/A if (respSignature.verify(signBytes)) {
0N/A if (DEBUG != null) {
0N/A DEBUG.println("Verified signature of OCSP Responder");
0N/A }
0N/A return true;
0N/A
0N/A } else {
0N/A if (DEBUG != null) {
0N/A DEBUG.println(
0N/A "Error verifying signature of OCSP Responder");
0N/A }
0N/A return false;
0N/A }
0N/A } catch (InvalidKeyException ike) {
0N/A throw new CertPathValidatorException(ike);
0N/A } catch (NoSuchAlgorithmException nsae) {
0N/A throw new CertPathValidatorException(nsae);
0N/A } catch (SignatureException se) {
0N/A throw new CertPathValidatorException(se);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns the SingleResponse of the specified CertId, or null if
0N/A * there is no response for that CertId.
0N/A */
0N/A SingleResponse getSingleResponse(CertId certId) {
0N/A return singleResponseMap.get(certId);
0N/A }
0N/A
0N/A /*
0N/A * A class representing a single OCSP response.
0N/A */
0N/A final static class SingleResponse implements OCSP.RevocationStatus {
0N/A private final CertId certId;
0N/A private final CertStatus certStatus;
0N/A private final Date thisUpdate;
0N/A private final Date nextUpdate;
0N/A private final Date revocationTime;
0N/A private final CRLReason revocationReason;
0N/A private final Map<String, java.security.cert.Extension> singleExtensions;
0N/A
0N/A private SingleResponse(DerValue der) throws IOException {
0N/A this(der, null);
0N/A }
0N/A
0N/A private SingleResponse(DerValue der, Date dateCheckedAgainst)
0N/A throws IOException {
0N/A if (der.tag != DerValue.tag_Sequence) {
0N/A throw new IOException("Bad ASN.1 encoding in SingleResponse");
0N/A }
0N/A DerInputStream tmp = der.data;
0N/A
0N/A certId = new CertId(tmp.getDerValue().data);
0N/A DerValue derVal = tmp.getDerValue();
0N/A short tag = (byte)(derVal.tag & 0x1f);
0N/A if (tag == CERT_STATUS_REVOKED) {
0N/A certStatus = CertStatus.REVOKED;
0N/A revocationTime = derVal.data.getGeneralizedTime();
0N/A if (derVal.data.available() != 0) {
0N/A DerValue dv = derVal.data.getDerValue();
0N/A tag = (byte)(dv.tag & 0x1f);
0N/A if (tag == 0) {
0N/A int reason = dv.data.getEnumerated();
0N/A // if reason out-of-range just leave as UNSPECIFIED
0N/A if (reason >= 0 && reason < values.length) {
0N/A revocationReason = values[reason];
0N/A } else {
0N/A revocationReason = CRLReason.UNSPECIFIED;
0N/A }
0N/A } else {
0N/A revocationReason = CRLReason.UNSPECIFIED;
0N/A }
0N/A } else {
0N/A revocationReason = CRLReason.UNSPECIFIED;
0N/A }
0N/A // RevokedInfo
0N/A if (DEBUG != null) {
0N/A DEBUG.println("Revocation time: " + revocationTime);
0N/A DEBUG.println("Revocation reason: " + revocationReason);
0N/A }
0N/A } else {
0N/A revocationTime = null;
0N/A revocationReason = CRLReason.UNSPECIFIED;
3863N/A if (tag == CERT_STATUS_GOOD) {
0N/A certStatus = CertStatus.GOOD;
0N/A } else if (tag == CERT_STATUS_UNKNOWN) {
0N/A certStatus = CertStatus.UNKNOWN;
0N/A } else {
0N/A throw new IOException("Invalid certificate status");
0N/A }
0N/A }
0N/A
0N/A thisUpdate = tmp.getGeneralizedTime();
0N/A
0N/A if (tmp.available() == 0) {
0N/A // we are done
3863N/A nextUpdate = null;
0N/A } else {
0N/A derVal = tmp.getDerValue();
0N/A tag = (byte)(derVal.tag & 0x1f);
199N/A if (tag == 0) {
199N/A // next update
199N/A nextUpdate = derVal.data.getGeneralizedTime();
199N/A
0N/A if (tmp.available() == 0) {
199N/A // we are done
199N/A } else {
199N/A derVal = tmp.getDerValue();
199N/A tag = (byte)(derVal.tag & 0x1f);
199N/A }
199N/A } else {
0N/A nextUpdate = null;
0N/A }
199N/A }
199N/A // singleExtensions
0N/A if (tmp.available() > 0) {
0N/A derVal = tmp.getDerValue();
0N/A if (derVal.isContextSpecific((byte)1)) {
0N/A DerValue[] singleExtDer = derVal.data.getSequence(3);
0N/A singleExtensions =
0N/A new HashMap<String, java.security.cert.Extension>
0N/A (singleExtDer.length);
0N/A for (int i = 0; i < singleExtDer.length; i++) {
0N/A Extension ext = new Extension(singleExtDer[i]);
0N/A if (DEBUG != null) {
0N/A DEBUG.println("OCSP single extension: " + ext);
0N/A }
0N/A // We don't support any extensions yet. Therefore, if it
0N/A // is critical we must throw an exception because we
0N/A // don't know how to process it.
0N/A if (ext.isCritical()) {
0N/A throw new IOException(
0N/A "Unsupported OCSP critical extension: " +
0N/A ext.getExtensionId());
0N/A }
0N/A singleExtensions.put(ext.getId(), ext);
0N/A }
0N/A } else {
0N/A singleExtensions = Collections.emptyMap();
0N/A }
0N/A } else {
0N/A singleExtensions = Collections.emptyMap();
0N/A }
0N/A
0N/A long now = (dateCheckedAgainst == null) ?
0N/A System.currentTimeMillis() : dateCheckedAgainst.getTime();
0N/A Date nowPlusSkew = new Date(now + MAX_CLOCK_SKEW);
0N/A Date nowMinusSkew = new Date(now - MAX_CLOCK_SKEW);
0N/A if (DEBUG != null) {
0N/A String until = "";
0N/A if (nextUpdate != null) {
0N/A until = " until " + nextUpdate;
0N/A }
0N/A DEBUG.println("Response's validity interval is from " +
0N/A thisUpdate + until);
0N/A }
0N/A // Check that the test date is within the validity interval
0N/A if ((thisUpdate != null && nowPlusSkew.before(thisUpdate)) ||
0N/A (nextUpdate != null && nowMinusSkew.after(nextUpdate))) {
0N/A
0N/A if (DEBUG != null) {
0N/A DEBUG.println("Response is unreliable: its validity " +
0N/A "interval is out-of-date");
0N/A }
0N/A throw new IOException("Response is unreliable: its validity " +
0N/A "interval is out-of-date");
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Return the certificate's revocation status code
0N/A */
0N/A @Override public CertStatus getCertStatus() {
0N/A return certStatus;
0N/A }
0N/A
0N/A private CertId getCertId() {
0N/A return certId;
0N/A }
0N/A
0N/A @Override public Date getRevocationTime() {
0N/A return (Date) revocationTime.clone();
0N/A }
0N/A
0N/A @Override public CRLReason getRevocationReason() {
0N/A return revocationReason;
0N/A }
0N/A
0N/A @Override
0N/A public Map<String, java.security.cert.Extension> getSingleExtensions() {
0N/A return Collections.unmodifiableMap(singleExtensions);
0N/A }
0N/A
0N/A /**
0N/A * Construct a string representation of a single OCSP response.
0N/A */
0N/A @Override public String toString() {
0N/A StringBuilder sb = new StringBuilder();
0N/A sb.append("SingleResponse: \n");
0N/A sb.append(certId);
0N/A sb.append("\nCertStatus: "+ certStatus + "\n");
0N/A if (certStatus == CertStatus.REVOKED) {
0N/A sb.append("revocationTime is " + revocationTime + "\n");
0N/A sb.append("revocationReason is " + revocationReason + "\n");
0N/A }
0N/A sb.append("thisUpdate is " + thisUpdate + "\n");
0N/A if (nextUpdate != null) {
0N/A sb.append("nextUpdate is " + nextUpdate + "\n");
0N/A }
0N/A return sb.toString();
0N/A }
0N/A }
0N/A}
0N/A