/* * Copyright 2003-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ package sun.security.provider.certpath; import java.io.*; import java.math.BigInteger; import java.security.*; import java.security.cert.CertPathValidatorException; import java.security.cert.CRLReason; import java.security.cert.X509Certificate; import java.security.cert.PKIXParameters; import javax.security.auth.x500.X500Principal; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Iterator; import sun.misc.HexDumpEncoder; import sun.security.x509.*; import sun.security.util.*; /** * This class is used to process an OCSP response. * The OCSP Response is defined * in RFC 2560 and the ASN.1 encoding is as follows: *
*
* OCSPResponse ::= SEQUENCE {
* responseStatus OCSPResponseStatus,
* responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
*
* OCSPResponseStatus ::= ENUMERATED {
* successful (0), --Response has valid confirmations
* malformedRequest (1), --Illegal confirmation request
* internalError (2), --Internal error in issuer
* tryLater (3), --Try again later
* --(4) is not used
* sigRequired (5), --Must sign the request
* unauthorized (6) --Request unauthorized
* }
*
* ResponseBytes ::= SEQUENCE {
* responseType OBJECT IDENTIFIER,
* response OCTET STRING }
*
* BasicOCSPResponse ::= SEQUENCE {
* tbsResponseData ResponseData,
* signatureAlgorithm AlgorithmIdentifier,
* signature BIT STRING,
* certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
*
* The value for signature SHALL be computed on the hash of the DER
* encoding ResponseData.
*
* ResponseData ::= SEQUENCE {
* version [0] EXPLICIT Version DEFAULT v1,
* responderID ResponderID,
* producedAt GeneralizedTime,
* responses SEQUENCE OF SingleResponse,
* responseExtensions [1] EXPLICIT Extensions OPTIONAL }
*
* ResponderID ::= CHOICE {
* byName [1] Name,
* byKey [2] KeyHash }
*
* KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
* (excluding the tag and length fields)
*
* SingleResponse ::= SEQUENCE {
* certID CertID,
* certStatus CertStatus,
* thisUpdate GeneralizedTime,
* nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
* singleExtensions [1] EXPLICIT Extensions OPTIONAL }
*
* CertStatus ::= CHOICE {
* good [0] IMPLICIT NULL,
* revoked [1] IMPLICIT RevokedInfo,
* unknown [2] IMPLICIT UnknownInfo }
*
* RevokedInfo ::= SEQUENCE {
* revocationTime GeneralizedTime,
* revocationReason [0] EXPLICIT CRLReason OPTIONAL }
*
* UnknownInfo ::= NULL -- this can be replaced with an enumeration
*
*
*
* @author Ram Marti
*/
class OCSPResponse {
// Certificate status CHOICE
public static final int CERT_STATUS_GOOD = 0;
public static final int CERT_STATUS_REVOKED = 1;
public static final int CERT_STATUS_UNKNOWN = 2;
private static final Debug DEBUG = Debug.getInstance("certpath");
private static final boolean dump = false;
private static final ObjectIdentifier OCSP_BASIC_RESPONSE_OID;
private static final ObjectIdentifier OCSP_NONCE_EXTENSION_OID;
static {
ObjectIdentifier tmp1 = null;
ObjectIdentifier tmp2 = null;
try {
tmp1 = new ObjectIdentifier("1.3.6.1.5.5.7.48.1.1");
tmp2 = new ObjectIdentifier("1.3.6.1.5.5.7.48.1.2");
} catch (Exception e) {
// should not happen; log and exit
}
OCSP_BASIC_RESPONSE_OID = tmp1;
OCSP_NONCE_EXTENSION_OID = tmp2;
}
// OCSP response status code
private static final int OCSP_RESPONSE_OK = 0;
// ResponderID CHOICE tags
private static final int NAME_TAG = 1;
private static final int KEY_TAG = 2;
// Object identifier for the OCSPSigning key purpose
private static final String KP_OCSP_SIGNING_OID = "1.3.6.1.5.5.7.3.9";
private SingleResponse singleResponse;
// Maximum clock skew in milliseconds (10 minutes) allowed when checking
// validity of OCSP responses
private static final long MAX_CLOCK_SKEW = 600000;
// an array of all of the CRLReasons (used in SingleResponse)
private static CRLReason[] values = CRLReason.values();
/*
* Create an OCSP response from its ASN.1 DER encoding.
*/
// used by OCSPChecker
OCSPResponse(byte[] bytes, PKIXParameters params,
X509Certificate responderCert)
throws IOException, CertPathValidatorException {
try {
int responseStatus;
ObjectIdentifier responseType;
int version;
CertificateIssuerName responderName = null;
Date producedAtDate;
AlgorithmId sigAlgId;
byte[] ocspNonce;
// OCSPResponse
if (dump) {
HexDumpEncoder hexEnc = new HexDumpEncoder();
System.out.println("OCSPResponse bytes are...");
System.out.println(hexEnc.encode(bytes));
}
DerValue der = new DerValue(bytes);
if (der.tag != DerValue.tag_Sequence) {
throw new IOException("Bad encoding in OCSP response: " +
"expected ASN.1 SEQUENCE tag.");
}
DerInputStream derIn = der.getData();
// responseStatus
responseStatus = derIn.getEnumerated();
if (DEBUG != null) {
DEBUG.println("OCSP response: " +
responseToText(responseStatus));
}
if (responseStatus != OCSP_RESPONSE_OK) {
throw new CertPathValidatorException(
"OCSP Response Failure: " +
responseToText(responseStatus));
}
// responseBytes
der = derIn.getDerValue();
if (! der.isContextSpecific((byte)0)) {
throw new IOException("Bad encoding in responseBytes element " +
"of OCSP response: expected ASN.1 context specific tag 0.");
};
DerValue tmp = der.data.getDerValue();
if (tmp.tag != DerValue.tag_Sequence) {
throw new IOException("Bad encoding in responseBytes element " +
"of OCSP response: expected ASN.1 SEQUENCE tag.");
}
// responseType
derIn = tmp.data;
responseType = derIn.getOID();
if (responseType.equals(OCSP_BASIC_RESPONSE_OID)) {
if (DEBUG != null) {
DEBUG.println("OCSP response type: basic");
}
} else {
if (DEBUG != null) {
DEBUG.println("OCSP response type: " + responseType);
}
throw new IOException("Unsupported OCSP response type: " +
responseType);
}
// BasicOCSPResponse
DerInputStream basicOCSPResponse =
new DerInputStream(derIn.getOctetString());
DerValue[] seqTmp = basicOCSPResponse.getSequence(2);
DerValue responseData = seqTmp[0];
// Need the DER encoded ResponseData to verify the signature later
byte[] responseDataDer = seqTmp[0].toByteArray();
// tbsResponseData
if (responseData.tag != DerValue.tag_Sequence) {
throw new IOException("Bad encoding in tbsResponseData " +
" element of OCSP response: expected ASN.1 SEQUENCE tag.");
}
DerInputStream seqDerIn = responseData.data;
DerValue seq = seqDerIn.getDerValue();
// version
if (seq.isContextSpecific((byte)0)) {
// seq[0] is version
if (seq.isConstructed() && seq.isContextSpecific()) {
//System.out.println ("version is available");
seq = seq.data.getDerValue();
version = seq.getInteger();
if (seq.data.available() != 0) {
throw new IOException("Bad encoding in version " +
" element of OCSP response: bad format");
}
seq = seqDerIn.getDerValue();
}
}
// responderID
short tag = (byte)(seq.tag & 0x1f);
if (tag == NAME_TAG) {
responderName = new CertificateIssuerName(seq.getData());
if (DEBUG != null) {
DEBUG.println("OCSP Responder name: " + responderName);
}
} else if (tag == KEY_TAG) {
// Ignore, for now
} else {
throw new IOException("Bad encoding in responderID element " +
"of OCSP response: expected ASN.1 context specific tag 0 " +
"or 1");
}
// producedAt
seq = seqDerIn.getDerValue();
producedAtDate = seq.getGeneralizedTime();
// responses
DerValue[] singleResponseDer = seqDerIn.getSequence(1);
// Examine only the first response
singleResponse = new SingleResponse(singleResponseDer[0]);
// responseExtensions
if (seqDerIn.available() > 0) {
seq = seqDerIn.getDerValue();
if (seq.isContextSpecific((byte)1)) {
DerValue[] responseExtDer = seq.data.getSequence(3);
Extension[] responseExtension =
new Extension[responseExtDer.length];
for (int i = 0; i < responseExtDer.length; i++) {
responseExtension[i] = new Extension(responseExtDer[i]);
if (DEBUG != null) {
DEBUG.println("OCSP extension: " +
responseExtension[i]);
}
if ((responseExtension[i].getExtensionId()).equals(
OCSP_NONCE_EXTENSION_OID)) {
ocspNonce =
responseExtension[i].getExtensionValue();
} else if (responseExtension[i].isCritical()) {
throw new IOException(
"Unsupported OCSP critical extension: " +
responseExtension[i].getExtensionId());
}
}
}
}
// signatureAlgorithmId
sigAlgId = AlgorithmId.parse(seqTmp[1]);
// signature
byte[] signature = seqTmp[2].getBitString();
X509CertImpl[] x509Certs = null;
// if seq[3] is available , then it is a sequence of certificates
if (seqTmp.length > 3) {
// certs are available
DerValue seqCert = seqTmp[3];
if (! seqCert.isContextSpecific((byte)0)) {
throw new IOException("Bad encoding in certs element " +
"of OCSP response: expected ASN.1 context specific tag 0.");
}
DerValue[] certs = (seqCert.getData()).getSequence(3);
x509Certs = new X509CertImpl[certs.length];
for (int i = 0; i < certs.length; i++) {
x509Certs[i] = new X509CertImpl(certs[i].toByteArray());
}
}
// Check whether the cert returned by the responder is trusted
if (x509Certs != null && x509Certs[0] != null) {
X509CertImpl cert = x509Certs[0];
// First check if the cert matches the responder cert which
// was set locally.
if (cert.equals(responderCert)) {
// cert is trusted, now verify the signed response
// Next check if the cert was issued by the responder cert
// which was set locally.
} else if (cert.getIssuerX500Principal().equals(
responderCert.getSubjectX500Principal())) {
// Check for the OCSPSigning key purpose
List