676 lines · 26.4 KB · Java
RawDownload
/* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. 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.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.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.CertificateException;import java.security.cert.CertificateParsingException;import java.security.cert.CertPathValidatorException;import java.security.cert.CRLReason;import java.security.cert.TrustAnchor;import java.security.cert.X509Certificate;import java.util.Collections;import java.util.Date;import java.util.HashMap;import java.util.List;import java.util.Map;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: * <pre> * *  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 * * </pre> * * @author      Ram Marti */ public final class OCSPResponse {     public enum ResponseStatus {        SUCCESSFUL,            // Response has valid confirmations        MALFORMED_REQUEST,     // Illegal confirmation request        INTERNAL_ERROR,        // Internal error in issuer        TRY_LATER,             // Try again later        UNUSED,                // is not used        SIG_REQUIRED,          // Must sign the request        UNAUTHORIZED           // Request unauthorized    };    private static ResponseStatus[] rsvalues = ResponseStatus.values();     private static final Debug DEBUG = Debug.getInstance("certpath");    private static final boolean dump = false;    private static final ObjectIdentifier OCSP_BASIC_RESPONSE_OID =        ObjectIdentifier.newInternal(new int[] { 1, 3, 6, 1, 5, 5, 7, 48, 1, 1});    private static final ObjectIdentifier OCSP_NONCE_EXTENSION_OID =        ObjectIdentifier.newInternal(new int[] { 1, 3, 6, 1, 5, 5, 7, 48, 1, 2});     private static final int CERT_STATUS_GOOD = 0;    private static final int CERT_STATUS_REVOKED = 1;    private static final int CERT_STATUS_UNKNOWN = 2;     // 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 final ResponseStatus responseStatus;    private final Map<CertId, SingleResponse> singleResponseMap;     // Maximum clock skew in milliseconds (15 minutes) allowed when checking    // validity of OCSP responses    private static final long MAX_CLOCK_SKEW = 900000;     // 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.     */    OCSPResponse(byte[] bytes, Date dateCheckedAgainst,        X509Certificate responderCert)        throws IOException, CertPathValidatorException {         // 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        int status = derIn.getEnumerated();        if (status >= 0 && status < rsvalues.length) {            responseStatus = rsvalues[status];        } else {            // unspecified responseStatus            throw new IOException("Unknown OCSPResponse status: " + status);        }        if (DEBUG != null) {            DEBUG.println("OCSP response status: " + responseStatus);        }        if (responseStatus != ResponseStatus.SUCCESSFUL) {            // no need to continue, responseBytes are not set.            singleResponseMap = Collections.emptyMap();            return;        }         // 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;        ObjectIdentifier 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);        if (seqTmp.length < 3) {            throw new IOException("Unexpected BasicOCSPResponse value");        }         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();                int 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) {            if (DEBUG != null) {                X500Name responderName = new X500Name(seq.getData());                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();        if (DEBUG != null) {            Date producedAtDate = seq.getGeneralizedTime();            DEBUG.println("OCSP response produced at: " + producedAtDate);        }         // responses        DerValue[] singleResponseDer = seqDerIn.getSequence(1);        singleResponseMap            = new HashMap<CertId, SingleResponse>(singleResponseDer.length);        if (DEBUG != null) {            DEBUG.println("OCSP number of SingleResponses: "                + singleResponseDer.length);        }        for (int i = 0; i < singleResponseDer.length; i++) {            SingleResponse singleResponse                = new SingleResponse(singleResponseDer[i]);            singleResponseMap.put(singleResponse.getCertId(), singleResponse);        }         // responseExtensions        if (seqDerIn.available() > 0) {            seq = seqDerIn.getDerValue();            if (seq.isContextSpecific((byte)1)) {                DerValue[] responseExtDer = seq.data.getSequence(3);                for (int i = 0; i < responseExtDer.length; i++) {                    Extension responseExtension                        = new Extension(responseExtDer[i]);                    if (DEBUG != null) {                        DEBUG.println("OCSP extension: " + responseExtension);                    }                    if (responseExtension.getExtensionId().equals(                        OCSP_NONCE_EXTENSION_OID)) {                        /*                        ocspNonce =                            responseExtension[i].getExtensionValue();                         */                    } else if (responseExtension.isCritical())  {                        throw new IOException(                            "Unsupported OCSP critical extension: " +                            responseExtension.getExtensionId());                    }                }            }        }         // signatureAlgorithmId        AlgorithmId 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];            try {                for (int i = 0; i < certs.length; i++) {                    x509Certs[i] = new X509CertImpl(certs[i].toByteArray());                }            } catch (CertificateException ce) {                throw new IOException("Bad encoding in X509 Certificate", ce);            }        }         // 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                try {                    List<String> keyPurposes = cert.getExtendedKeyUsage();                    if (keyPurposes == null ||                        !keyPurposes.contains(KP_OCSP_SIGNING_OID)) {                        throw new CertPathValidatorException(                            "Responder's certificate not valid for signing " +                            "OCSP responses");                    }                } catch (CertificateParsingException cpe) {                    // assume cert is not valid for signing                    throw new CertPathValidatorException(                        "Responder's certificate not valid for signing " +                        "OCSP responses", cpe);                }                 // Check algorithm constraints specified in security property                // "jdk.certpath.disabledAlgorithms".                AlgorithmChecker algChecker = new AlgorithmChecker(                                    new TrustAnchor(responderCert, null));                algChecker.init(false);                algChecker.check(cert, Collections.<String>emptySet());                 // check the validity                try {                    if (dateCheckedAgainst == null) {                        cert.checkValidity();                    } else {                        cert.checkValidity(dateCheckedAgainst);                    }                } catch (GeneralSecurityException e) {                    throw new CertPathValidatorException(                        "Responder's certificate not within the " +                        "validity period", e);                }                 // check for revocation                //                // A CA may specify that an OCSP client can trust a                // responder for the lifetime of the responder's                // certificate. The CA does so by including the                // extension id-pkix-ocsp-nocheck.                //                Extension noCheck =                    cert.getExtension(PKIXExtensions.OCSPNoCheck_Id);                if (noCheck != null) {                    if (DEBUG != null) {                        DEBUG.println("Responder's certificate includes " +                            "the extension id-pkix-ocsp-nocheck.");                    }                } else {                    // we should do the revocation checking of the                    // authorized responder in a future update.                }                 // verify the signature                try {                    cert.verify(responderCert.getPublicKey());                    responderCert = cert;                    // cert is trusted, now verify the signed response                 } catch (GeneralSecurityException e) {                    responderCert = null;                }            } else {                throw new CertPathValidatorException(                    "Responder's certificate is not authorized to sign " +                    "OCSP responses");            }        }         // Confirm that the signed response was generated using the public        // key from the trusted responder cert        if (responderCert != null) {            // Check algorithm constraints specified in security property            // "jdk.certpath.disabledAlgorithms".            AlgorithmChecker.check(responderCert.getPublicKey(), sigAlgId);             if (!verifyResponse(responseDataDer, responderCert,                sigAlgId, signature)) {                throw new CertPathValidatorException(                    "Error verifying OCSP Responder's signature");            }        } else {            // Need responder's cert in order to verify the signature            throw new CertPathValidatorException(                "Unable to verify OCSP Responder's signature");        }    }     /**     * Returns the OCSP ResponseStatus.     */    ResponseStatus getResponseStatus() {        return responseStatus;    }     /*     * Verify the signature of the OCSP response.     * The responder's cert is implicitly trusted.     */    private boolean verifyResponse(byte[] responseData, X509Certificate cert,        AlgorithmId sigAlgId, byte[] signBytes)        throws CertPathValidatorException {         try {            Signature respSignature = Signature.getInstance(sigAlgId.getName());            respSignature.initVerify(cert);            respSignature.update(responseData);             if (respSignature.verify(signBytes)) {                if (DEBUG != null) {                    DEBUG.println("Verified signature of OCSP Responder");                }                return true;             } else {                if (DEBUG != null) {                    DEBUG.println(                        "Error verifying signature of OCSP Responder");                }                return false;            }        } catch (InvalidKeyException ike) {            throw new CertPathValidatorException(ike);        } catch (NoSuchAlgorithmException nsae) {            throw new CertPathValidatorException(nsae);        } catch (SignatureException se) {            throw new CertPathValidatorException(se);        }    }     /**     * Returns the SingleResponse of the specified CertId, or null if     * there is no response for that CertId.     */    SingleResponse getSingleResponse(CertId certId) {        return singleResponseMap.get(certId);    }     /*     * A class representing a single OCSP response.

Lines 1–500 of 676.