| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
package sun.security.provider.certpath;  | 
 | 
 | 
 | 
import java.io.*;  | 
 | 
import java.security.*;  | 
 | 
import java.security.cert.CertificateException;  | 
 | 
import java.security.cert.CertificateParsingException;  | 
 | 
import java.security.cert.CertPathValidatorException;  | 
 | 
import java.security.cert.CertPathValidatorException.BasicReason;  | 
 | 
import java.security.cert.CRLReason;  | 
 | 
import java.security.cert.TrustAnchor;  | 
 | 
import java.security.cert.X509Certificate;  | 
 | 
import java.util.ArrayList;  | 
 | 
import java.util.Arrays;  | 
 | 
import java.util.Collections;  | 
 | 
import java.util.Date;  | 
 | 
import java.util.HashMap;  | 
 | 
import java.util.List;  | 
 | 
import java.util.Map;  | 
 | 
import java.util.Set;  | 
 | 
import javax.security.auth.x500.X500Principal;  | 
 | 
 | 
 | 
import sun.misc.HexDumpEncoder;  | 
 | 
import sun.security.action.GetIntegerAction;  | 
 | 
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,              | 
 | 
        MALFORMED_REQUEST,       | 
 | 
        INTERNAL_ERROR,          | 
 | 
        TRY_LATER,               | 
 | 
        UNUSED,                  | 
 | 
        SIG_REQUIRED,            | 
 | 
        UNAUTHORIZED             | 
 | 
    };  | 
 | 
    private static final ResponseStatus[] rsvalues = ResponseStatus.values();  | 
 | 
 | 
 | 
    private static final Debug debug = Debug.getInstance("certpath"); | 
 | 
    private static final boolean dump = debug != null && Debug.isOn("ocsp"); | 
 | 
    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 int CERT_STATUS_GOOD = 0;  | 
 | 
    private static final int CERT_STATUS_REVOKED = 1;  | 
 | 
    private static final int CERT_STATUS_UNKNOWN = 2;  | 
 | 
 | 
 | 
      | 
 | 
    private static final int NAME_TAG = 1;  | 
 | 
    private static final int KEY_TAG = 2;  | 
 | 
 | 
 | 
      | 
 | 
    private static final String KP_OCSP_SIGNING_OID = "1.3.6.1.5.5.7.3.9";  | 
 | 
 | 
 | 
    // Default maximum clock skew in milliseconds (15 minutes)  | 
 | 
      | 
 | 
    private static final int DEFAULT_MAX_CLOCK_SKEW = 900000;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static final int MAX_CLOCK_SKEW = initializeClockSkew();  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static int initializeClockSkew() { | 
 | 
        Integer tmp = java.security.AccessController.doPrivileged(  | 
 | 
                new GetIntegerAction("com.sun.security.ocsp.clockSkew")); | 
 | 
        if (tmp == null || tmp < 0) { | 
 | 
            return DEFAULT_MAX_CLOCK_SKEW;  | 
 | 
        }  | 
 | 
        // Convert to milliseconds, as the system property will be  | 
 | 
          | 
 | 
        return tmp * 1000;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    private static final CRLReason[] values = CRLReason.values();  | 
 | 
 | 
 | 
    private final ResponseStatus responseStatus;  | 
 | 
    private final Map<CertId, SingleResponse> singleResponseMap;  | 
 | 
    private final AlgorithmId sigAlgId;  | 
 | 
    private final byte[] signature;  | 
 | 
    private final byte[] tbsResponseData;  | 
 | 
    private final byte[] responseNonce;  | 
 | 
    private List<X509CertImpl> certs;  | 
 | 
    private X509CertImpl signerCert = null;  | 
 | 
    private final ResponderId respId;  | 
 | 
    private Date producedAtDate = null;  | 
 | 
    private final Map<String, java.security.cert.Extension> responseExtensions;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public OCSPResponse(byte[] bytes) throws IOException { | 
 | 
        if (dump) { | 
 | 
            HexDumpEncoder hexEnc = new HexDumpEncoder();  | 
 | 
            debug.println("OCSPResponse bytes...\n\n" + | 
 | 
                hexEnc.encode(bytes) + "\n");  | 
 | 
        }  | 
 | 
        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();  | 
 | 
 | 
 | 
          | 
 | 
        int status = derIn.getEnumerated();  | 
 | 
        if (status >= 0 && status < rsvalues.length) { | 
 | 
            responseStatus = rsvalues[status];  | 
 | 
        } else { | 
 | 
              | 
 | 
            throw new IOException("Unknown OCSPResponse status: " + status); | 
 | 
        }  | 
 | 
        if (debug != null) { | 
 | 
            debug.println("OCSP response status: " + responseStatus); | 
 | 
        }  | 
 | 
        if (responseStatus != ResponseStatus.SUCCESSFUL) { | 
 | 
              | 
 | 
            singleResponseMap = Collections.emptyMap();  | 
 | 
            certs = new ArrayList<X509CertImpl>();  | 
 | 
            sigAlgId = null;  | 
 | 
            signature = null;  | 
 | 
            tbsResponseData = null;  | 
 | 
            responseNonce = null;  | 
 | 
            responseExtensions = Collections.emptyMap();  | 
 | 
            respId = null;  | 
 | 
            return;  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        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.");  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        derIn = tmp.data;  | 
 | 
        ObjectIdentifier responseType = derIn.getOID();  | 
 | 
        if (responseType.equals((Object)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);  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        DerInputStream basicOCSPResponse =  | 
 | 
            new DerInputStream(derIn.getOctetString());  | 
 | 
 | 
 | 
        DerValue[] seqTmp = basicOCSPResponse.getSequence(3);  | 
 | 
        if (seqTmp.length < 3) { | 
 | 
            throw new IOException("Unexpected BasicOCSPResponse value"); | 
 | 
        }  | 
 | 
 | 
 | 
        DerValue responseData = seqTmp[0];  | 
 | 
 | 
 | 
          | 
 | 
        tbsResponseData = seqTmp[0].toByteArray();  | 
 | 
 | 
 | 
          | 
 | 
        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();  | 
 | 
 | 
 | 
          | 
 | 
        if (seq.isContextSpecific((byte)0)) { | 
 | 
              | 
 | 
            if (seq.isConstructed() && seq.isContextSpecific()) { | 
 | 
                  | 
 | 
                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();  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        respId = new ResponderId(seq.toByteArray());  | 
 | 
        if (debug != null) { | 
 | 
            debug.println("Responder ID: " + respId); | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        seq = seqDerIn.getDerValue();  | 
 | 
        producedAtDate = seq.getGeneralizedTime();  | 
 | 
        if (debug != null) { | 
 | 
            debug.println("OCSP response produced at: " + producedAtDate); | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        DerValue[] singleResponseDer = seqDerIn.getSequence(1);  | 
 | 
        singleResponseMap = new HashMap<>(singleResponseDer.length);  | 
 | 
        if (debug != null) { | 
 | 
            debug.println("OCSP number of SingleResponses: " | 
 | 
                          + singleResponseDer.length);  | 
 | 
        }  | 
 | 
        for (DerValue srDer : singleResponseDer) { | 
 | 
            SingleResponse singleResponse = new SingleResponse(srDer);  | 
 | 
            singleResponseMap.put(singleResponse.getCertId(), singleResponse);  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        Map<String, java.security.cert.Extension> tmpExtMap = new HashMap<>();  | 
 | 
        if (seqDerIn.available() > 0) { | 
 | 
            seq = seqDerIn.getDerValue();  | 
 | 
            if (seq.isContextSpecific((byte)1)) { | 
 | 
                tmpExtMap = parseExtensions(seq);  | 
 | 
            }  | 
 | 
        }  | 
 | 
        responseExtensions = tmpExtMap;  | 
 | 
 | 
 | 
          | 
 | 
        Extension nonceExt = (Extension)tmpExtMap.get(  | 
 | 
                PKIXExtensions.OCSPNonce_Id.toString());  | 
 | 
        responseNonce = (nonceExt != null) ?  | 
 | 
                nonceExt.getExtensionValue() : null;  | 
 | 
        if (debug != null && responseNonce != null) { | 
 | 
            debug.println("Response nonce: " + Arrays.toString(responseNonce)); | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        sigAlgId = AlgorithmId.parse(seqTmp[1]);  | 
 | 
 | 
 | 
          | 
 | 
        signature = seqTmp[2].getBitString();  | 
 | 
 | 
 | 
          | 
 | 
        if (seqTmp.length > 3) { | 
 | 
              | 
 | 
            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[] derCerts = seqCert.getData().getSequence(3);  | 
 | 
            certs = new ArrayList<X509CertImpl>(derCerts.length);  | 
 | 
            try { | 
 | 
                for (int i = 0; i < derCerts.length; i++) { | 
 | 
                    X509CertImpl cert =  | 
 | 
                        new X509CertImpl(derCerts[i].toByteArray());  | 
 | 
                    certs.add(cert);  | 
 | 
 | 
 | 
                    if (debug != null) { | 
 | 
                        debug.println("OCSP response cert #" + (i + 1) + ": " + | 
 | 
                            cert.getSubjectX500Principal());  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            } catch (CertificateException ce) { | 
 | 
                throw new IOException("Bad encoding in X509 Certificate", ce); | 
 | 
            }  | 
 | 
        } else { | 
 | 
            certs = new ArrayList<X509CertImpl>();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    void verify(List<CertId> certIds, IssuerInfo issuerInfo,  | 
 | 
            X509Certificate responderCert, Date date, byte[] nonce,  | 
 | 
            String variant)  | 
 | 
        throws CertPathValidatorException  | 
 | 
    { | 
 | 
        switch (responseStatus) { | 
 | 
            case SUCCESSFUL:  | 
 | 
                break;  | 
 | 
            case TRY_LATER:  | 
 | 
            case INTERNAL_ERROR:  | 
 | 
                throw new CertPathValidatorException(  | 
 | 
                    "OCSP response error: " + responseStatus, null, null, -1,  | 
 | 
                    BasicReason.UNDETERMINED_REVOCATION_STATUS);  | 
 | 
            case UNAUTHORIZED:  | 
 | 
            default:  | 
 | 
                throw new CertPathValidatorException("OCSP response error: " + | 
 | 
                                                     responseStatus);  | 
 | 
        }  | 
 | 
 | 
 | 
        // Check that the response includes a response for all of the  | 
 | 
          | 
 | 
        for (CertId certId : certIds) { | 
 | 
            SingleResponse sr = getSingleResponse(certId);  | 
 | 
            if (sr == null) { | 
 | 
                if (debug != null) { | 
 | 
                    debug.println("No response found for CertId: " + certId); | 
 | 
                }  | 
 | 
                throw new CertPathValidatorException(  | 
 | 
                    "OCSP response does not include a response for a " +  | 
 | 
                    "certificate supplied in the OCSP request");  | 
 | 
            }  | 
 | 
            if (debug != null) { | 
 | 
                debug.println("Status of certificate (with serial number " + | 
 | 
                    certId.getSerialNumber() + ") is: " + sr.getCertStatus());  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        if (signerCert == null) { | 
 | 
            // Add the Issuing CA cert and/or Trusted Responder cert to the list  | 
 | 
              | 
 | 
            try { | 
 | 
                if (issuerInfo.getCertificate() != null) { | 
 | 
                    certs.add(X509CertImpl.toImpl(issuerInfo.getCertificate()));  | 
 | 
                }  | 
 | 
                if (responderCert != null) { | 
 | 
                    certs.add(X509CertImpl.toImpl(responderCert));  | 
 | 
                }  | 
 | 
            } catch (CertificateException ce) { | 
 | 
                throw new CertPathValidatorException(  | 
 | 
                    "Invalid issuer or trusted responder certificate", ce);  | 
 | 
            }  | 
 | 
 | 
 | 
            if (respId.getType() == ResponderId.Type.BY_NAME) { | 
 | 
                X500Principal rName = respId.getResponderName();  | 
 | 
                for (X509CertImpl cert : certs) { | 
 | 
                    if (cert.getSubjectX500Principal().equals(rName)) { | 
 | 
                        signerCert = cert;  | 
 | 
                        break;  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            } else if (respId.getType() == ResponderId.Type.BY_KEY) { | 
 | 
                KeyIdentifier ridKeyId = respId.getKeyIdentifier();  | 
 | 
                for (X509CertImpl cert : certs) { | 
 | 
                    // Match responder's key identifier against the cert's SKID  | 
 | 
                    // This will match if the SKID is encoded using the 160-bit  | 
 | 
                      | 
 | 
                    KeyIdentifier certKeyId = cert.getSubjectKeyId();  | 
 | 
                    if (certKeyId != null && ridKeyId.equals(certKeyId)) { | 
 | 
                        signerCert = cert;  | 
 | 
                        break;  | 
 | 
                    } else { | 
 | 
                        // The certificate does not have a SKID or may have  | 
 | 
                        // been using a different algorithm (ex: see RFC 7093).  | 
 | 
                        // Check if the responder's key identifier matches  | 
 | 
                        // against a newly generated key identifier of the  | 
 | 
                          | 
 | 
                        try { | 
 | 
                            certKeyId = new KeyIdentifier(cert.getPublicKey());  | 
 | 
                        } catch (IOException e) { | 
 | 
                            // ignore  | 
 | 
                        }  | 
 | 
                        if (ridKeyId.equals(certKeyId)) { | 
 | 
                            signerCert = cert;  | 
 | 
                            break;  | 
 | 
                        }  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        boolean signedByTrustedResponder = false;  | 
 | 
        if (signerCert != null) { | 
 | 
              | 
 | 
            if (signerCert.getSubjectX500Principal().equals(  | 
 | 
                    issuerInfo.getName()) &&  | 
 | 
                    signerCert.getPublicKey().equals(  | 
 | 
                            issuerInfo.getPublicKey())) { | 
 | 
                if (debug != null) { | 
 | 
                    debug.println("OCSP response is signed by the target's " + | 
 | 
                        "Issuing CA");  | 
 | 
                }  | 
 | 
                // cert is trusted, now verify the signed response  | 
 | 
 | 
 | 
            // Check if the response is signed by a trusted responder  | 
 | 
            } else if (signerCert.equals(responderCert)) { | 
 | 
                signedByTrustedResponder = true;  | 
 | 
                if (debug != null) { | 
 | 
                    debug.println("OCSP response is signed by a Trusted " + | 
 | 
                        "Responder");  | 
 | 
                }  | 
 | 
                // cert is trusted, now verify the signed response  | 
 | 
 | 
 | 
            // Check if the response is signed by an authorized responder  | 
 | 
            } else if (signerCert.getIssuerX500Principal().equals(  | 
 | 
                    issuerInfo.getName())) { | 
 | 
 | 
 | 
                  | 
 | 
                try { | 
 | 
                    List<String> keyPurposes = signerCert.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) { | 
 | 
                      | 
 | 
                    throw new CertPathValidatorException(  | 
 | 
                        "Responder's certificate not valid for signing " +  | 
 | 
                        "OCSP responses", cpe);  | 
 | 
                }  | 
 | 
 | 
 | 
                // Check algorithm constraints specified in security property  | 
 | 
                  | 
 | 
                AlgorithmChecker algChecker =  | 
 | 
                        new AlgorithmChecker(issuerInfo.getAnchor(), date,  | 
 | 
                                variant);  | 
 | 
                algChecker.init(false);  | 
 | 
                algChecker.check(signerCert, Collections.<String>emptySet());  | 
 | 
 | 
 | 
                  | 
 | 
                try { | 
 | 
                    if (date == null) { | 
 | 
                        signerCert.checkValidity();  | 
 | 
                    } else { | 
 | 
                        signerCert.checkValidity(date);  | 
 | 
                    }  | 
 | 
                } catch (CertificateException 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 =  | 
 | 
                    signerCert.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.  | 
 | 
                }  | 
 | 
 | 
 | 
                  | 
 | 
                try { | 
 | 
                    signerCert.verify(issuerInfo.getPublicKey());  | 
 | 
                    if (debug != null) { | 
 | 
                        debug.println("OCSP response is signed by an " + | 
 | 
                            "Authorized Responder");  | 
 | 
                    }  | 
 | 
                    // cert is trusted, now verify the signed response  | 
 | 
 | 
 | 
                } catch (GeneralSecurityException e) { | 
 | 
                    signerCert = 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  | 
 | 
          | 
 | 
        if (signerCert != null) { | 
 | 
            // Check algorithm constraints specified in security property  | 
 | 
              | 
 | 
            AlgorithmChecker.check(signerCert.getPublicKey(), sigAlgId, variant,  | 
 | 
                    signedByTrustedResponder  | 
 | 
                        ? new TrustAnchor(responderCert, null)  | 
 | 
                        : issuerInfo.getAnchor());  | 
 | 
 | 
 | 
            if (!verifySignature(signerCert)) { | 
 | 
                throw new CertPathValidatorException(  | 
 | 
                    "Error verifying OCSP Response's signature");  | 
 | 
            }  | 
 | 
        } else { | 
 | 
              | 
 | 
            throw new CertPathValidatorException(  | 
 | 
                "Unable to verify OCSP Response's signature");  | 
 | 
        }  | 
 | 
 | 
 | 
        if (nonce != null) { | 
 | 
            if (responseNonce != null && !Arrays.equals(nonce, responseNonce)) { | 
 | 
                throw new CertPathValidatorException("Nonces don't match"); | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        long now = (date == null) ? System.currentTimeMillis() : date.getTime();  | 
 | 
        Date nowPlusSkew = new Date(now + MAX_CLOCK_SKEW);  | 
 | 
        Date nowMinusSkew = new Date(now - MAX_CLOCK_SKEW);  | 
 | 
        for (SingleResponse sr : singleResponseMap.values()) { | 
 | 
            if (debug != null) { | 
 | 
                String until = "";  | 
 | 
                if (sr.nextUpdate != null) { | 
 | 
                    until = " until " + sr.nextUpdate;  | 
 | 
                }  | 
 | 
                debug.println("OCSP response validity interval is from " + | 
 | 
                        sr.thisUpdate + until);  | 
 | 
                debug.println("Checking validity of OCSP response on: " + | 
 | 
                        new Date(now));  | 
 | 
            }  | 
 | 
 | 
 | 
            // Check that the test date is within the validity interval:  | 
 | 
            //   [ thisUpdate - MAX_CLOCK_SKEW,  | 
 | 
              | 
 | 
            if (nowPlusSkew.before(sr.thisUpdate) ||  | 
 | 
                    nowMinusSkew.after(  | 
 | 
                    sr.nextUpdate != null ? sr.nextUpdate : sr.thisUpdate))  | 
 | 
            { | 
 | 
                throw new CertPathValidatorException(  | 
 | 
                                      "Response is unreliable: its validity " +  | 
 | 
                                      "interval is out-of-date");  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public ResponseStatus getResponseStatus() { | 
 | 
        return responseStatus;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private boolean verifySignature(X509Certificate cert)  | 
 | 
        throws CertPathValidatorException { | 
 | 
 | 
 | 
        try { | 
 | 
            Signature respSignature = Signature.getInstance(sigAlgId.getName());  | 
 | 
            respSignature.initVerify(cert.getPublicKey());  | 
 | 
            respSignature.update(tbsResponseData);  | 
 | 
 | 
 | 
            if (respSignature.verify(signature)) { | 
 | 
                if (debug != null) { | 
 | 
                    debug.println("Verified signature of OCSP Response"); | 
 | 
                }  | 
 | 
                return true;  | 
 | 
 | 
 | 
            } else { | 
 | 
                if (debug != null) { | 
 | 
                    debug.println(  | 
 | 
                        "Error verifying signature of OCSP Response");  | 
 | 
                }  | 
 | 
                return false;  | 
 | 
            }  | 
 | 
        } catch (InvalidKeyException | NoSuchAlgorithmException |  | 
 | 
                 SignatureException e)  | 
 | 
        { | 
 | 
            throw new CertPathValidatorException(e);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public SingleResponse getSingleResponse(CertId certId) { | 
 | 
        return singleResponseMap.get(certId);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public Set<CertId> getCertIds() { | 
 | 
        return Collections.unmodifiableSet(singleResponseMap.keySet());  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    X509Certificate getSignerCertificate() { | 
 | 
        return signerCert;   | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public ResponderId getResponderId() { | 
 | 
        return respId;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @Override  | 
 | 
    public String toString() { | 
 | 
        StringBuilder sb = new StringBuilder();  | 
 | 
        sb.append("OCSP Response:\n"); | 
 | 
        sb.append("Response Status: ").append(responseStatus).append("\n"); | 
 | 
        sb.append("Responder ID: ").append(respId).append("\n"); | 
 | 
        sb.append("Produced at: ").append(producedAtDate).append("\n"); | 
 | 
        int count = singleResponseMap.size();  | 
 | 
        sb.append(count).append(count == 1 ?  | 
 | 
                " response:\n" : " responses:\n");  | 
 | 
        for (SingleResponse sr : singleResponseMap.values()) { | 
 | 
            sb.append(sr).append("\n"); | 
 | 
        }  | 
 | 
        if (responseExtensions != null && responseExtensions.size() > 0) { | 
 | 
            count = responseExtensions.size();  | 
 | 
            sb.append(count).append(count == 1 ?  | 
 | 
                    " extension:\n" : " extensions:\n");  | 
 | 
            for (String extId : responseExtensions.keySet()) { | 
 | 
                sb.append(responseExtensions.get(extId)).append("\n"); | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        return sb.toString();  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static Map<String, java.security.cert.Extension>  | 
 | 
        parseExtensions(DerValue derVal) throws IOException { | 
 | 
        DerValue[] extDer = derVal.data.getSequence(3);  | 
 | 
        Map<String, java.security.cert.Extension> extMap =  | 
 | 
                new HashMap<>(extDer.length);  | 
 | 
 | 
 | 
        for (DerValue extDerVal : extDer) { | 
 | 
            Extension ext = new Extension(extDerVal);  | 
 | 
            if (debug != null) { | 
 | 
                debug.println("Extension: " + ext); | 
 | 
            }  | 
 | 
            // We don't support any extensions yet. Therefore, if it  | 
 | 
            // is critical we must throw an exception because we  | 
 | 
              | 
 | 
            if (ext.isCritical()) { | 
 | 
                throw new IOException("Unsupported OCSP critical extension: " + | 
 | 
                        ext.getExtensionId());  | 
 | 
            }  | 
 | 
            extMap.put(ext.getId(), ext);  | 
 | 
        }  | 
 | 
 | 
 | 
        return extMap;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public static final class SingleResponse implements OCSP.RevocationStatus { | 
 | 
        private final CertId certId;  | 
 | 
        private final CertStatus certStatus;  | 
 | 
        private final Date thisUpdate;  | 
 | 
        private final Date nextUpdate;  | 
 | 
        private final Date revocationTime;  | 
 | 
        private final CRLReason revocationReason;  | 
 | 
        private final Map<String, java.security.cert.Extension> singleExtensions;  | 
 | 
 | 
 | 
        private SingleResponse(DerValue der) throws IOException { | 
 | 
            if (der.tag != DerValue.tag_Sequence) { | 
 | 
                throw new IOException("Bad ASN.1 encoding in SingleResponse"); | 
 | 
            }  | 
 | 
            DerInputStream tmp = der.data;  | 
 | 
 | 
 | 
            certId = new CertId(tmp.getDerValue().data);  | 
 | 
            DerValue derVal = tmp.getDerValue();  | 
 | 
            short tag = (byte)(derVal.tag & 0x1f);  | 
 | 
            if (tag ==  CERT_STATUS_REVOKED) { | 
 | 
                certStatus = CertStatus.REVOKED;  | 
 | 
                revocationTime = derVal.data.getGeneralizedTime();  | 
 | 
                if (derVal.data.available() != 0) { | 
 | 
                    DerValue dv = derVal.data.getDerValue();  | 
 | 
                    tag = (byte)(dv.tag & 0x1f);  | 
 | 
                    if (tag == 0) { | 
 | 
                        int reason = dv.data.getEnumerated();  | 
 | 
                          | 
 | 
                        if (reason >= 0 && reason < values.length) { | 
 | 
                            revocationReason = values[reason];  | 
 | 
                        } else { | 
 | 
                            revocationReason = CRLReason.UNSPECIFIED;  | 
 | 
                        }  | 
 | 
                    } else { | 
 | 
                        revocationReason = CRLReason.UNSPECIFIED;  | 
 | 
                    }  | 
 | 
                } else { | 
 | 
                    revocationReason = CRLReason.UNSPECIFIED;  | 
 | 
                }  | 
 | 
                  | 
 | 
                if (debug != null) { | 
 | 
                    debug.println("Revocation time: " + revocationTime); | 
 | 
                    debug.println("Revocation reason: " + revocationReason); | 
 | 
                }  | 
 | 
            } else { | 
 | 
                revocationTime = null;  | 
 | 
                revocationReason = null;  | 
 | 
                if (tag == CERT_STATUS_GOOD) { | 
 | 
                    certStatus = CertStatus.GOOD;  | 
 | 
                } else if (tag == CERT_STATUS_UNKNOWN) { | 
 | 
                    certStatus = CertStatus.UNKNOWN;  | 
 | 
                } else { | 
 | 
                    throw new IOException("Invalid certificate status"); | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            thisUpdate = tmp.getGeneralizedTime();  | 
 | 
            if (debug != null) { | 
 | 
                debug.println("thisUpdate: " + thisUpdate); | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            Date tmpNextUpdate = null;  | 
 | 
            Map<String, java.security.cert.Extension> tmpMap = null;  | 
 | 
 | 
 | 
            // Check for the first optional item, it could be nextUpdate  | 
 | 
              | 
 | 
            if (tmp.available() > 0) { | 
 | 
                derVal = tmp.getDerValue();  | 
 | 
 | 
 | 
                  | 
 | 
                if (derVal.isContextSpecific((byte)0)) { | 
 | 
                    tmpNextUpdate = derVal.data.getGeneralizedTime();  | 
 | 
                    if (debug != null) { | 
 | 
                        debug.println("nextUpdate: " + tmpNextUpdate); | 
 | 
                    }  | 
 | 
 | 
 | 
                    // If more data exists in the singleResponse, it  | 
 | 
                    // can only be singleExtensions.  Get this DER value  | 
 | 
                      | 
 | 
                    derVal = tmp.available() > 0 ? tmp.getDerValue() : null;  | 
 | 
                }  | 
 | 
 | 
 | 
                  | 
 | 
                if (derVal != null) { | 
 | 
                    if (derVal.isContextSpecific((byte)1)) { | 
 | 
                        tmpMap = parseExtensions(derVal);  | 
 | 
 | 
 | 
                        // There should not be any other items in the  | 
 | 
                          | 
 | 
                        if (tmp.available() > 0) { | 
 | 
                            throw new IOException(tmp.available() +  | 
 | 
                                " bytes of additional data in singleResponse");  | 
 | 
                        }  | 
 | 
                    } else { | 
 | 
                          | 
 | 
                        throw new IOException("Unsupported singleResponse " + | 
 | 
                            "item, tag = " + String.format("%02X", derVal.tag)); | 
 | 
                    }  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            nextUpdate = tmpNextUpdate;  | 
 | 
            singleExtensions = (tmpMap != null) ? tmpMap :  | 
 | 
                    Collections.emptyMap();  | 
 | 
            if (debug != null) { | 
 | 
                for (java.security.cert.Extension ext :  | 
 | 
                        singleExtensions.values()) { | 
 | 
                   debug.println("singleExtension: " + ext); | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
         */  | 
 | 
        @Override  | 
 | 
        public CertStatus getCertStatus() { | 
 | 
            return certStatus;  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        public CertId getCertId() { | 
 | 
            return certId;  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        public Date getThisUpdate() { | 
 | 
            return (thisUpdate != null ? (Date) thisUpdate.clone() : null);  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        public Date getNextUpdate() { | 
 | 
            return (nextUpdate != null ? (Date) nextUpdate.clone() : null);  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        @Override  | 
 | 
        public Date getRevocationTime() { | 
 | 
            return (revocationTime != null ? (Date) revocationTime.clone() :  | 
 | 
                    null);  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        @Override  | 
 | 
        public CRLReason getRevocationReason() { | 
 | 
            return revocationReason;  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        @Override  | 
 | 
        public Map<String, java.security.cert.Extension> getSingleExtensions() { | 
 | 
            return Collections.unmodifiableMap(singleExtensions);  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
         */  | 
 | 
        @Override public String toString() { | 
 | 
            StringBuilder sb = new StringBuilder();  | 
 | 
            sb.append("SingleResponse:\n"); | 
 | 
            sb.append(certId);  | 
 | 
            sb.append("\nCertStatus: ").append(certStatus).append("\n"); | 
 | 
            if (certStatus == CertStatus.REVOKED) { | 
 | 
                sb.append("revocationTime is "); | 
 | 
                sb.append(revocationTime).append("\n"); | 
 | 
                sb.append("revocationReason is "); | 
 | 
                sb.append(revocationReason).append("\n"); | 
 | 
            }  | 
 | 
            sb.append("thisUpdate is ").append(thisUpdate).append("\n"); | 
 | 
            if (nextUpdate != null) { | 
 | 
                sb.append("nextUpdate is ").append(nextUpdate).append("\n"); | 
 | 
            }  | 
 | 
            for (java.security.cert.Extension ext : singleExtensions.values()) { | 
 | 
                sb.append("singleExtension: "); | 
 | 
                sb.append(ext.toString()).append("\n"); | 
 | 
            }  | 
 | 
            return sb.toString();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    static final class IssuerInfo { | 
 | 
        private final TrustAnchor anchor;  | 
 | 
        private final X509Certificate certificate;  | 
 | 
        private final X500Principal name;  | 
 | 
        private final PublicKey pubKey;  | 
 | 
 | 
 | 
        IssuerInfo(TrustAnchor anchor) { | 
 | 
            this(anchor, (anchor != null) ? anchor.getTrustedCert() : null);  | 
 | 
        }  | 
 | 
 | 
 | 
        IssuerInfo(X509Certificate issuerCert) { | 
 | 
            this(null, issuerCert);  | 
 | 
        }  | 
 | 
 | 
 | 
        IssuerInfo(TrustAnchor anchor, X509Certificate issuerCert) { | 
 | 
            if (anchor == null && issuerCert == null) { | 
 | 
                throw new NullPointerException("TrustAnchor and issuerCert " + | 
 | 
                        "cannot be null");  | 
 | 
            }  | 
 | 
            this.anchor = anchor;  | 
 | 
            if (issuerCert != null) { | 
 | 
                name = issuerCert.getSubjectX500Principal();  | 
 | 
                pubKey = issuerCert.getPublicKey();  | 
 | 
                certificate = issuerCert;  | 
 | 
            } else { | 
 | 
                name = anchor.getCA();  | 
 | 
                pubKey = anchor.getCAPublicKey();  | 
 | 
                certificate = anchor.getTrustedCert();  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        X509Certificate getCertificate() { | 
 | 
            return certificate;  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        X500Principal getName() { | 
 | 
            return name;  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        PublicKey getPublicKey() { | 
 | 
            return pubKey;  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        TrustAnchor getAnchor() { | 
 | 
            return anchor;  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        @Override  | 
 | 
        public String toString() { | 
 | 
            StringBuilder sb = new StringBuilder();  | 
 | 
            sb.append("Issuer Info:\n"); | 
 | 
            sb.append("Name: ").append(name.toString()).append("\n"); | 
 | 
            sb.append("Public Key:\n").append(pubKey.toString()).append("\n"); | 
 | 
            return sb.toString();  | 
 | 
        }  | 
 | 
    }  | 
 | 
}  |