Example usage for org.bouncycastle.cert.ocsp BasicOCSPResp getResponses

List of usage examples for org.bouncycastle.cert.ocsp BasicOCSPResp getResponses

Introduction

In this page you can find the example usage for org.bouncycastle.cert.ocsp BasicOCSPResp getResponses.

Prototype

public SingleResp[] getResponses() 

Source Link

Usage

From source file:org.jruby.ext.openssl.OCSPBasicResponse.java

License:Common Public License

private boolean checkIssuer(BasicOCSPResp basicOCSPResp, IRubyObject chain) throws IOException {
    boolean ret = false;
    if (((RubyArray) chain).size() <= 0)
        return false;
    List<SingleResp> singleResponses = Arrays.asList(basicOCSPResp.getResponses());
    CertificateID certId = checkCertIds(singleResponses);

    X509Cert signer = (X509Cert) ((RubyArray) chain).first();
    if (((RubyArray) chain).size() > 1) {
        X509Cert signerCA = (X509Cert) ((RubyArray) chain).entry(1);
        if (matchIssuerId(signerCA, certId, singleResponses)) {
            return checkDelegated(signerCA);
        }//from w  w w.j a  v  a 2s.c o m
    } else {
        ret = matchIssuerId(signer, certId, singleResponses);
    }

    return ret;
}

From source file:org.keycloak.common.util.OCSPUtils.java

License:Apache License

private static OCSPRevocationStatus processBasicOCSPResponse(X509Certificate issuerCertificate,
        X509Certificate responderCertificate, Date date, JcaCertificateID certificateID, BigInteger nounce,
        BasicOCSPResp basicOcspResponse)
        throws OCSPException, NoSuchProviderException, NoSuchAlgorithmException,
        CertificateNotYetValidException, CertificateExpiredException, CertPathValidatorException {
    SingleResp expectedResponse = null;/*from ww  w  .j  a v  a2  s .c o m*/
    for (SingleResp singleResponse : basicOcspResponse.getResponses()) {
        if (compareCertIDs(certificateID, singleResponse.getCertID())) {
            expectedResponse = singleResponse;
            break;
        }
    }

    if (expectedResponse != null) {
        verifyResponse(basicOcspResponse, issuerCertificate, responderCertificate, nounce.toByteArray(), date);
        return singleResponseToRevocationStatus(expectedResponse);
    } else {
        throw new CertPathValidatorException(
                "OCSP response does not include a response for a certificate supplied in the OCSP request");
    }
}

From source file:org.keycloak.common.util.OCSPUtils.java

License:Apache License

private static void verifyResponse(BasicOCSPResp basicOcspResponse, X509Certificate issuerCertificate,
        X509Certificate responderCertificate, byte[] requestNonce, Date date)
        throws NoSuchProviderException, NoSuchAlgorithmException, CertificateNotYetValidException,
        CertificateExpiredException, CertPathValidatorException {

    List<X509CertificateHolder> certs = new ArrayList<>(Arrays.asList(basicOcspResponse.getCerts()));
    X509Certificate signingCert = null;

    try {/*w w w .  j a  v  a  2 s .  com*/
        certs.add(new JcaX509CertificateHolder(issuerCertificate));
        if (responderCertificate != null) {
            certs.add(new JcaX509CertificateHolder(responderCertificate));
        }
    } catch (CertificateEncodingException e) {
        e.printStackTrace();
    }
    if (certs.size() > 0) {

        X500Name responderName = basicOcspResponse.getResponderId().toASN1Primitive().getName();
        byte[] responderKey = basicOcspResponse.getResponderId().toASN1Primitive().getKeyHash();

        if (responderName != null) {
            logger.log(Level.INFO, "Responder Name: {0}", responderName.toString());
            for (X509CertificateHolder certHolder : certs) {
                try {
                    X509Certificate tempCert = new JcaX509CertificateConverter().setProvider("BC")
                            .getCertificate(certHolder);
                    X500Name respName = new X500Name(tempCert.getSubjectX500Principal().getName());
                    if (responderName.equals(respName)) {
                        signingCert = tempCert;
                        logger.log(Level.INFO,
                                "Found a certificate whose principal \"{0}\" matches the responder name \"{1}\"",
                                new Object[] { tempCert.getSubjectDN().getName(), responderName.toString() });
                        break;
                    }
                } catch (CertificateException e) {
                    logger.log(Level.FINE, e.getMessage());
                }
            }
        } else if (responderKey != null) {
            SubjectKeyIdentifier responderSubjectKey = new SubjectKeyIdentifier(responderKey);
            logger.log(Level.INFO, "Responder Key: {0}", Arrays.toString(responderKey));
            for (X509CertificateHolder certHolder : certs) {
                try {
                    X509Certificate tempCert = new JcaX509CertificateConverter().setProvider("BC")
                            .getCertificate(certHolder);

                    SubjectKeyIdentifier subjectKeyIdentifier = null;
                    if (certHolder.getExtensions() != null) {
                        subjectKeyIdentifier = SubjectKeyIdentifier.fromExtensions(certHolder.getExtensions());
                    }

                    if (subjectKeyIdentifier != null) {
                        logger.log(Level.INFO, "Certificate: {0}\nSubject Key Id: {1}",
                                new Object[] { tempCert.getSubjectDN().getName(),
                                        Arrays.toString(subjectKeyIdentifier.getKeyIdentifier()) });
                    }

                    if (subjectKeyIdentifier != null && responderSubjectKey.equals(subjectKeyIdentifier)) {
                        signingCert = tempCert;
                        logger.log(Level.INFO,
                                "Found a signer certificate \"{0}\" with the subject key extension value matching the responder key",
                                signingCert.getSubjectDN().getName());

                        break;
                    }

                    subjectKeyIdentifier = new JcaX509ExtensionUtils()
                            .createSubjectKeyIdentifier(tempCert.getPublicKey());
                    if (responderSubjectKey.equals(subjectKeyIdentifier)) {
                        signingCert = tempCert;
                        logger.log(Level.INFO,
                                "Found a certificate \"{0}\" with the subject key matching the OCSP responder key",
                                signingCert.getSubjectDN().getName());
                        break;
                    }

                } catch (CertificateException e) {
                    logger.log(Level.FINE, e.getMessage());
                }
            }
        }
    }
    if (signingCert != null) {
        if (signingCert.equals(issuerCertificate)) {
            logger.log(Level.INFO, "OCSP response is signed by the target''s Issuing CA");
        } else if (responderCertificate != null && signingCert.equals(responderCertificate)) {
            // https://www.ietf.org/rfc/rfc2560.txt
            // 2.6  OCSP Signature Authority Delegation
            // - The responder certificate is issued to the responder by CA
            logger.log(Level.INFO, "OCSP response is signed by an authorized responder certificate");
        } else {
            // 4.2.2.2  Authorized Responders
            // 3. Includes a value of id-ad-ocspSigning in an ExtendedKeyUsage
            // extension and is issued by the CA that issued the certificate in
            // question."
            if (!signingCert.getIssuerX500Principal().equals(issuerCertificate.getSubjectX500Principal())) {
                logger.log(Level.INFO, "Signer certificate''s Issuer: {0}\nIssuer certificate''s Subject: {1}",
                        new Object[] { signingCert.getIssuerX500Principal().getName(),
                                issuerCertificate.getSubjectX500Principal().getName() });
                throw new CertPathValidatorException(
                        "Responder\'s certificate is not authorized to sign OCSP responses");
            }
            try {
                List<String> purposes = signingCert.getExtendedKeyUsage();
                if (purposes != null && !purposes.contains(KeyPurposeId.id_kp_OCSPSigning.getId())) {
                    logger.log(Level.INFO, "OCSPSigning extended usage is not set");
                    throw new CertPathValidatorException(
                            "Responder\'s certificate not valid for signing OCSP responses");
                }
            } catch (CertificateParsingException e) {
                logger.log(Level.FINE, "Failed to get certificate''s extended key usage extension\n{0}",
                        e.getMessage());
            }
            if (date == null) {
                signingCert.checkValidity();
            } else {
                signingCert.checkValidity(date);
            }
            try {
                Extension noOCSPCheck = new JcaX509CertificateHolder(signingCert)
                        .getExtension(OCSPObjectIdentifiers.id_pkix_ocsp_nocheck);
                // TODO If the extension is present, the OCSP client can trust the
                // responder's certificate for the lifetime of the certificate.
                logger.log(Level.INFO, "OCSP no-check extension is {0} present",
                        noOCSPCheck == null ? "not" : "");
            } catch (CertificateEncodingException e) {
                logger.log(Level.FINE, "Certificate encoding exception: {0}", e.getMessage());
            }

            try {
                signingCert.verify(issuerCertificate.getPublicKey());
                logger.log(Level.INFO, "OCSP response is signed by an Authorized Responder");

            } catch (GeneralSecurityException ex) {
                signingCert = null;
            }
        }
    }
    if (signingCert == null) {
        throw new CertPathValidatorException("Unable to verify OCSP Response\'s signature");
    } else {
        if (!verifySignature(basicOcspResponse, signingCert)) {
            throw new CertPathValidatorException("Error verifying OCSP Response\'s signature");
        } else {
            Extension responseNonce = basicOcspResponse.getExtension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce);
            if (responseNonce != null && requestNonce != null
                    && !Arrays.equals(requestNonce, responseNonce.getExtnValue().getOctets())) {
                throw new CertPathValidatorException("Nonces do not match.");
            } else {
                // See Sun's OCSP implementation.
                // https://www.ietf.org/rfc/rfc2560.txt, if nextUpdate is not set,
                // the responder is indicating that newer update is avilable all the time
                long current = date == null ? System.currentTimeMillis() : date.getTime();
                Date stop = new Date(current + (long) TIME_SKEW);
                Date start = new Date(current - (long) TIME_SKEW);

                Iterator<SingleResp> iter = Arrays.asList(basicOcspResponse.getResponses()).iterator();
                SingleResp singleRes = null;
                do {
                    if (!iter.hasNext()) {
                        return;
                    }
                    singleRes = iter.next();
                } while (!stop.before(singleRes.getThisUpdate())
                        && !start.after(singleRes.getNextUpdate() != null ? singleRes.getNextUpdate()
                                : singleRes.getThisUpdate()));

                throw new CertPathValidatorException(
                        "Response is unreliable: its validity interval is out-of-date");
            }
        }
    }
}

From source file:org.poreid.verify.ocsp.OCSPClient.java

License:Open Source License

public CertStatus getCertificateStatus() throws OCSPValidationException {
    try {/*  ww w  .  java2  s  .c  o  m*/
        if (null == url) {
            throw new OCSPValidationException("Certificado no tem validao por OCSP");
        }

        byte[] encodedOcspRequest = generateOCSPRequest(issuer, certificate.getSerialNumber()).getEncoded();

        HttpURLConnection httpConnection;
        httpConnection = (HttpURLConnection) url.openConnection();
        httpConnection.setRequestProperty("Content-Type", "application/ocsp-request");
        httpConnection.setRequestProperty("Accept", "application/ocsp-response");
        httpConnection.setDoOutput(true);

        try (DataOutputStream dataOut = new DataOutputStream(
                new BufferedOutputStream(httpConnection.getOutputStream()))) {
            dataOut.write(encodedOcspRequest);
            dataOut.flush();
        }

        InputStream in = (InputStream) httpConnection.getContent();

        if (httpConnection.getResponseCode() != HttpURLConnection.HTTP_OK) {
            throw new OCSPValidationException(
                    "Cdigo HTTP recebido != 200 [" + httpConnection.getResponseCode() + "]");
        }

        OCSPResp ocspResponse = new OCSPResp(in);
        BasicOCSPResp basicResponse = (BasicOCSPResp) ocspResponse.getResponseObject();

        byte[] receivedNonce = basicResponse.getExtension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce).getExtnId()
                .getEncoded();
        if (!Arrays.equals(receivedNonce, sentNonce)) {
            throw new OCSPValidationException("Nonce na resposta ocsp no coincide com nonce do pedido ocsp");
        }

        X509CertificateHolder certHolder = basicResponse.getCerts()[0];
        if (!basicResponse
                .isSignatureValid(new JcaContentVerifierProviderBuilder().setProvider("BC").build(issuer))) {
            if (!certHolder.isValidOn(Date.from(Instant.now()))) {
                throw new OCSPValidationException("Certificado no  vlido na data atual");
            }
            // Certificado tem de ter uma Key Purpose ID for authorized responders
            if (!ExtendedKeyUsage.fromExtensions(certHolder.getExtensions())
                    .hasKeyPurposeId(KeyPurposeId.id_kp_OCSPSigning)) {
                throw new OCSPValidationException(
                        "Certificado no contm extenso necessria (id_kp_OCSPSigning)");
            }
            // Certificado tem de ser emitido pela mesma CA do certificado que estamos a verificar
            if (!certHolder.isSignatureValid(
                    new JcaContentVerifierProviderBuilder().setProvider("BC").build(issuer))) {
                throw new OCSPValidationException("Certificado no  assinado pelo mesmo issuer");
            }
            // Validar assinatura na resposta ocsp
            if (!basicResponse.isSignatureValid(
                    new JcaContentVerifierProviderBuilder().setProvider("BC").build(certHolder))) {
                throw new OCSPValidationException("No foi possivel validar resposta ocsp");
            }
        } else {
            if (!certHolder.isValidOn(Date.from(Instant.now()))) {
                throw new OCSPValidationException("Certificado no  vlido na data atual");
            }
        }

        // Politica de Certificados do SCEE
        if (null == certHolder.getExtension(OCSPObjectIdentifiers.id_pkix_ocsp_nocheck).getExtnId()) {
            throw new OCSPValidationException(
                    "Extenso id_pkix_ocsp_nocheck no encontrada no certificado (Politica de Certificados do SCEE)");
        }

        SingleResp[] responses = basicResponse.getResponses();
        if (responses[0].getCertID().getSerialNumber().equals(certificate.getSerialNumber())) {
            CertificateStatus status = responses[0].getCertStatus();
            if (status == CertificateStatus.GOOD) {
                return CertStatus.GOOD;
            } else {

                if (status instanceof RevokedStatus) {
                    revokedStatus = (RevokedStatus) status;
                    return CertStatus.REVOKED;
                } else {
                    return CertStatus.UNKNOWN;
                }
            }
        } else {
            throw new OCSPValidationException(
                    "Nmero de srie do certificado na resposta ocsp no coincide com nmero de srie do certificado");
        }
    } catch (CertificateEncodingException | OperatorCreationException | OCSPException | IOException ex) {
        throw new OCSPValidationException("No foi possivel efetuar a validao atravs de OCSP ("
                + certificate.getSubjectX500Principal().getName() + ")", ex);
    } catch (CertException | CertificateException ex) {
        throw new OCSPValidationException("No foi possivel efetuar a validao atravs de OCSP ("
                + certificate.getSubjectX500Principal().getName() + ")", ex);
    }
}

From source file:org.signserver.module.xades.validator.AbstractCustomCertPathChecker.java

License:Open Source License

/**
 * Parses received response bytes to form basic ocsp response object and verifies ocsp response  
 * If returns , ocsp response is successfully verified, otherwise throws exception detailing problem
 * /*from  w  ww.j a  v a  2 s.  c o  m*/
 * @param x509Cert - certificate originally passed to validator for validation
 * @param ocspresp - ocsp response received from ocsp responder
 * @throws OCSPException 
 * @throws NoSuchProviderException 
 * @throws IOException 
 * @throws CertStoreException 
 * @throws NoSuchAlgorithmException 
 * @throws SignServerException 
 * @throws CertificateParsingException 
 * @throws CryptoTokenOfflineException 
 * @throws IllegalRequestException 
 */
protected void parseAndVerifyOCSPResponse(X509Certificate x509Cert, OCSPResp ocspresp, X509Certificate cACert)
        throws NoSuchProviderException, OCSPException, NoSuchAlgorithmException, CertStoreException,
        IOException, SignServerException, CertificateParsingException, IllegalRequestException,
        CryptoTokenOfflineException, OperatorCreationException, CertificateEncodingException {

    if (ocspresp.getStatus() != OCSPRespStatus.SUCCESSFUL) {
        throw new SignServerException(
                "Unexpected ocsp response status. Response Status Received : " + ocspresp.getStatus());
    }

    // we currently support only basic ocsp response 
    BasicOCSPResp basicOCSPResponse = (BasicOCSPResp) ocspresp.getResponseObject();

    if (basicOCSPResponse == null) {
        throw new SignServerException(
                "Could not construct BasicOCSPResp object from response. Only BasicOCSPResponse as defined in RFC 2560 is supported.");
    }

    //OCSP response might be signed by CA issuing the certificate or  
    //the Authorized OCSP responder certificate containing the id-kp-OCSPSigning extended key usage extension

    X509Certificate ocspRespSignerCertificate = null;

    //first check if CA issuing certificate signed the response
    //since it is expected to be the most common case
    if (basicOCSPResponse.isSignatureValid(
            new JcaContentVerifierProviderBuilder().setProvider("BC").build(cACert.getPublicKey()))) {
        ocspRespSignerCertificate = cACert;
    }
    //if CA did not sign the ocsp response, look for authorized ocsp responses from properties or from certificate chain received with response

    if (ocspRespSignerCertificate == null) {
        //look for existence of Authorized OCSP responder inside the cert chain in ocsp response
        ocspRespSignerCertificate = getAuthorizedOCSPRespondersCertificateFromOCSPResponse(basicOCSPResponse);

        //could not find the certificate signing the OCSP response in the ocsp response
        if (ocspRespSignerCertificate == null) {
            throw new SignServerException(
                    "Certificate signing the ocsp response is not found in ocsp response's certificate chain received and is not signed by CA issuing certificate");
        }
    }

    LOG.debug("OCSP response signed by :  " + CertTools.getSubjectDN(ocspRespSignerCertificate));
    // validating ocsp signers certificate
    // Check if responders certificate has id-pkix-ocsp-nocheck extension, in which case we do not validateUsingCRL (perform revocation check on ) ocsp certs for lifetime of certificate
    // using CRL RFC 2560 sect 4.2.2.2.1
    // TODO : RFC States the extension value should be NULL, so maybe bare existence of the extension is not sufficient ??
    if (ocspRespSignerCertificate
            .getExtensionValue(OCSPObjectIdentifiers.id_pkix_ocsp_nocheck.getId()) != null) {
        //check if lifetime of certificate is ok
        try {
            ocspRespSignerCertificate.checkValidity();
        } catch (CertificateExpiredException e) {
            throw new SignServerException(
                    "Certificate signing the ocsp response has expired. OCSP Responder Certificate Subject DN : "
                            + CertTools.getSubjectDN(ocspRespSignerCertificate));
        } catch (CertificateNotYetValidException e) {
            throw new SignServerException(
                    "Certificate signing the ocsp response is not yet valid. OCSP Responder Certificate Subject DN : "
                            + CertTools.getSubjectDN(ocspRespSignerCertificate));
        }
    } else if (ocspRespSignerCertificate.equals(cACert)) {
        LOG.debug("Not performing revocation check on issuer certificate");
    } else {
        // TODO: Could try to use CRL if available
        throw new SignServerException("Revokation check of OCSP certificate not yet supported");
    }

    //get the response we requested for 
    for (SingleResp singleResponse : basicOCSPResponse.getResponses()) {
        if (singleResponse.getCertID().getSerialNumber().equals(x509Cert.getSerialNumber())) {
            //found our response
            //check if response is OK, and if not throw OCSPStatusNotGoodException
            if (singleResponse.getCertStatus() != null) {
                throw new OCSPStatusNotGoodException(
                        "Responce for queried certificate is not good. Certificate status returned : "
                                + singleResponse.getCertStatus(),
                        singleResponse.getCertStatus());
            }
            //check the dates ThisUpdate and NextUpdate RFC 2560 sect : 4.2.2.1
            if (singleResponse.getNextUpdate() != null
                    && (new Date()).compareTo(singleResponse.getNextUpdate()) >= 0) {
                throw new SignServerException(
                        "Unreliable response received. Response reported a nextupdate as : "
                                + singleResponse.getNextUpdate().toString()
                                + " which is earlier than current date.");
            }
            if (singleResponse.getThisUpdate() != null
                    && (new Date()).compareTo(singleResponse.getThisUpdate()) <= 0) {
                throw new SignServerException(
                        "Unreliable response received. Response reported a thisupdate as : "
                                + singleResponse.getThisUpdate().toString()
                                + " which is earlier than current date.");
            }

            break;
        }
    }

}

From source file:org.signserver.validationservice.server.OCSPPathChecker.java

License:Open Source License

/**
 * Parses received response bytes to form basic ocsp response object and verifies ocsp response  
 * If returns , ocsp response is successfully verified, otherwise throws exception detailing problem
 * /*from   ww w  . j av  a 2 s.c o  m*/
 * @param x509Cert - certificate originally passed to validator for validation
 * @param derocspresponse - der formatted ocsp response received from ocsp responder
 * @throws OCSPException 
 * @throws NoSuchProviderException 
 * @throws IOException 
 * @throws CertStoreException 
 * @throws NoSuchAlgorithmException 
 * @throws NoSuchAlgorithmException 
 * @throws SignServerException 
 * @throws CertificateParsingException 
 * @throws CryptoTokenOfflineException 
 * @throws IllegalRequestException 
 */
protected void parseAndVerifyOCSPResponse(X509Certificate x509Cert, byte[] derocspresponse)
        throws NoSuchProviderException, OCSPException, NoSuchAlgorithmException, CertStoreException,
        IOException, SignServerException, CertificateParsingException, IllegalRequestException,
        CryptoTokenOfflineException, OperatorCreationException, CertificateEncodingException {
    //parse received ocsp response
    OCSPResp ocspresp = new OCSPResp(derocspresponse);
    if (ocspresp.getStatus() != OCSPRespStatus.SUCCESSFUL) {
        throw new SignServerException(
                "Unexpected ocsp response status. Response Status Received : " + ocspresp.getStatus());
    }

    // we currently support only basic ocsp response 
    BasicOCSPResp basicOCSPResponse = (BasicOCSPResp) ocspresp.getResponseObject();

    if (basicOCSPResponse == null) {
        throw new SignServerException(
                "Could not construct BasicOCSPResp object from response. Only BasicOCSPResponse as defined in RFC 2560 is supported.");
    }

    //OCSP response might be signed by CA issuing the certificate or  
    //the Authorized OCSP responder certificate containing the id-kp-OCSPSigning extended key usage extension

    X509Certificate ocspRespSignerCertificate = null;

    //first check if CA issuing certificate signed the response
    //since it is expected to be the most common case
    if (basicOCSPResponse.isSignatureValid(
            new JcaContentVerifierProviderBuilder().setProvider("BC").build(cACert.getPublicKey()))) {
        ocspRespSignerCertificate = cACert;
    }
    //if CA did not sign the ocsp response, look for authorized ocsp responses from properties or from certificate chain received with response
    if (ocspRespSignerCertificate == null) {
        log.debug("OCSP Response is not signed by issuing CA. Looking for authorized responders");
        if (basicOCSPResponse.getCerts() == null) {
            log.debug(
                    "OCSP Response does not contain certificate chain, trying to verify response using one of configured authorized ocsp responders");

            //certificate chain is not present in response received 
            //try to verify using one of the configured AuthorizedOCSPResponderCerts
            ocspRespSignerCertificate = getAuthorizedOCSPRespondersCertificateFromProperties(basicOCSPResponse);

            if (ocspRespSignerCertificate == null) {
                throw new SignServerException(
                        "OCSP Response does not contain certificate chain, and response is not signed by any of the configured Authorized OCSP Responders or CA issuing certificate.");
            }
        } else {
            //look for existence of Authorized OCSP responder inside the cert chain in ocsp response
            ocspRespSignerCertificate = getAuthorizedOCSPRespondersCertificateFromOCSPResponse(
                    basicOCSPResponse);

            //could not find the certificate signing the OCSP response in the ocsp response
            if (ocspRespSignerCertificate == null) {
                throw new SignServerException(
                        "Certificate signing the ocsp response is not found in ocsp response's certificate chain received and is not signed by CA issuing certificate");
            }
        }
    }

    log.debug("OCSP response signed by :  " + CertTools.getSubjectDN(ocspRespSignerCertificate));
    // validating ocsp signers certificate
    // Check if responders certificate has id-pkix-ocsp-nocheck extension, in which case we do not validate (perform revocation check on ) ocsp certs for lifetime of certificate
    // using CRL RFC 2560 sect 4.2.2.2.1
    // TODO : RFC States the extension value should be NULL, so maybe bare existence of the extension is not sufficient ??
    if (ocspRespSignerCertificate
            .getExtensionValue(OCSPObjectIdentifiers.id_pkix_ocsp_nocheck.getId()) != null) {
        //check if lifetime of certificate is ok
        try {
            ocspRespSignerCertificate.checkValidity();
        } catch (CertificateExpiredException e) {
            throw new SignServerException(
                    "Certificate signing the ocsp response has expired. OCSP Responder Certificate Subject DN : "
                            + CertTools.getSubjectDN(ocspRespSignerCertificate));
        } catch (CertificateNotYetValidException e) {
            throw new SignServerException(
                    "Certificate signing the ocsp response is not yet valid. OCSP Responder Certificate Subject DN : "
                            + CertTools.getSubjectDN(ocspRespSignerCertificate));
        }
    } else {
        // check if CDP exists in ocsp signers certificate
        // TODO : ?? add property for issuer whether to accept the OCSP response if the CDPs are not available (or use preconfigured CRLs) on signing certificate CRL RFC 2560 sect 4.2.2.2.1
        if (CertTools.getCrlDistributionPoint(ocspRespSignerCertificate) == null) {
            throw new SignServerException(
                    "CRL Distribution Point extension missing in ocsp signer's certificate.");
        }

        //verify certificate using CRL Validator
        //TODO : refactor Validators to follow factory pattern (discuss)
        CRLValidator crlValidator = new CRLValidator();
        Validation valresult = crlValidator.validate(ocspRespSignerCertificate, this.props);
        if (valresult.getStatus() != Validation.Status.VALID) {
            throw new SignServerException(
                    "Validation of ocsp signer's certificate failed. Status message received : "
                            + valresult.getStatusMessage());
        }
    }

    //get the response we requested for 
    for (SingleResp singleResponse : basicOCSPResponse.getResponses()) {
        if (singleResponse.getCertID().getSerialNumber().equals(x509Cert.getSerialNumber())) {
            //found our response
            //check if response is OK, and if not throw OCSPStatusNotGoodException
            if (singleResponse.getCertStatus() != null) {
                throw new OCSPStatusNotGoodException(
                        "Responce for queried certificate is not good. Certificate status returned : "
                                + singleResponse.getCertStatus(),
                        singleResponse.getCertStatus());
            }
            //check the dates ThisUpdate and NextUpdate RFC 2560 sect : 4.2.2.1
            if (singleResponse.getNextUpdate() != null
                    && (new Date()).compareTo(singleResponse.getNextUpdate()) >= 0) {
                throw new SignServerException(
                        "Unreliable response received. Response reported a nextupdate as : "
                                + singleResponse.getNextUpdate().toString()
                                + " which is earlier than current date.");
            }
            if (singleResponse.getThisUpdate() != null
                    && (new Date()).compareTo(singleResponse.getThisUpdate()) <= 0) {
                throw new SignServerException(
                        "Unreliable response received. Response reported a thisupdate as : "
                                + singleResponse.getThisUpdate().toString()
                                + " which is earlier than current date.");
            }

            break;
        }
    }

}

From source file:org.xdi.oxauth.cert.validation.OCSPCertificateVerifier.java

License:MIT License

@Override
public ValidationStatus validate(X509Certificate certificate, List<X509Certificate> issuers,
        Date validationDate) {// w  ww.  j a v a 2  s.  c om
    X509Certificate issuer = issuers.get(0);
    ValidationStatus status = new ValidationStatus(certificate, issuer, validationDate,
            ValidatorSourceType.OCSP, CertificateValidity.UNKNOWN);

    try {
        Principal subjectX500Principal = certificate.getSubjectX500Principal();

        String ocspUrl = getOCSPUrl(certificate);
        if (ocspUrl == null) {
            log.error("OCSP URL for '" + subjectX500Principal + "' is empty");
            return status;
        }

        log.debug("OCSP URL for '" + subjectX500Principal + "' is '" + ocspUrl + "'");

        DigestCalculator digestCalculator = new JcaDigestCalculatorProviderBuilder().build()
                .get(CertificateID.HASH_SHA1);
        CertificateID certificateId = new CertificateID(digestCalculator,
                new JcaX509CertificateHolder(certificate), certificate.getSerialNumber());

        // Generate OCSP request
        OCSPReq ocspReq = generateOCSPRequest(certificateId);

        // Get OCSP response from server
        OCSPResp ocspResp = requestOCSPResponse(ocspUrl, ocspReq);
        if (ocspResp.getStatus() != OCSPRespBuilder.SUCCESSFUL) {
            log.error("OCSP response is invalid!");
            status.setValidity(CertificateValidity.INVALID);
            return status;
        }

        boolean foundResponse = false;
        BasicOCSPResp basicOCSPResp = (BasicOCSPResp) ocspResp.getResponseObject();
        SingleResp[] singleResps = basicOCSPResp.getResponses();
        for (SingleResp singleResp : singleResps) {
            CertificateID responseCertificateId = singleResp.getCertID();
            if (!certificateId.equals(responseCertificateId)) {
                continue;
            }

            foundResponse = true;

            log.debug("OCSP validationDate: " + validationDate);
            log.debug("OCSP thisUpdate: " + singleResp.getThisUpdate());
            log.debug("OCSP nextUpdate: " + singleResp.getNextUpdate());

            status.setRevocationObjectIssuingTime(basicOCSPResp.getProducedAt());

            Object certStatus = singleResp.getCertStatus();
            if (certStatus == CertificateStatus.GOOD) {
                log.debug("OCSP status is valid for '" + certificate.getSubjectX500Principal() + "'");
                status.setValidity(CertificateValidity.VALID);
            } else {
                if (singleResp.getCertStatus() instanceof RevokedStatus) {
                    log.warn("OCSP status is revoked for: " + subjectX500Principal);
                    if (validationDate
                            .before(((RevokedStatus) singleResp.getCertStatus()).getRevocationTime())) {
                        log.warn("OCSP revocation time after the validation date, the certificate '"
                                + subjectX500Principal + "' was valid at " + validationDate);
                        status.setValidity(CertificateValidity.VALID);
                    } else {
                        Date revocationDate = ((RevokedStatus) singleResp.getCertStatus()).getRevocationTime();
                        log.info("OCSP for certificate '" + subjectX500Principal + "' is revoked since "
                                + revocationDate);
                        status.setRevocationDate(revocationDate);
                        status.setRevocationObjectIssuingTime(singleResp.getThisUpdate());
                        status.setValidity(CertificateValidity.REVOKED);
                    }
                }
            }
        }

        if (!foundResponse) {
            log.error("There is no matching OCSP response entries");
        }
    } catch (Exception ex) {
        log.error("OCSP exception: ", ex);
    }

    return status;
}

From source file:org.xipki.ocsp.client.impl.AbstractOCSPRequestor.java

License:Open Source License

@Override
public OCSPResp ask(final X509Certificate issuerCert, final BigInteger[] serialNumbers, final URL responderUrl,
        final RequestOptions requestOptions, final RequestResponseDebug debug)
        throws OCSPResponseException, OCSPRequestorException {
    if (requestOptions == null) {
        throw new IllegalArgumentException("requestOptions could not be null");
    }//from  ww w. ja  v  a  2  s. c  o m

    byte[] nonce = null;
    if (requestOptions.isUseNonce()) {
        nonce = nextNonce(requestOptions.getNonceLen());
    }

    OCSPReq ocspReq = buildRequest(issuerCert, serialNumbers, nonce, requestOptions);
    byte[] encodedReq;
    try {
        encodedReq = ocspReq.getEncoded();
    } catch (IOException e) {
        throw new OCSPRequestorException("could not encode OCSP request: " + e.getMessage(), e);
    }

    RequestResponsePair msgPair = null;
    if (debug != null) {
        msgPair = new RequestResponsePair();
        debug.add(msgPair);
        msgPair.setRequest(encodedReq);
    }

    byte[] encodedResp;
    try {
        encodedResp = send(encodedReq, responderUrl, requestOptions);
    } catch (IOException e) {
        throw new ResponderUnreachableException("IOException: " + e.getMessage(), e);
    }

    if (debug != null) {
        msgPair.setRequest(encodedResp);
    }

    OCSPResp ocspResp;
    try {
        ocspResp = new OCSPResp(encodedResp);
    } catch (IOException e) {
        throw new InvalidOCSPResponseException("IOException: " + e.getMessage(), e);
    }

    Object respObject;
    try {
        respObject = ocspResp.getResponseObject();
    } catch (OCSPException e) {
        throw new InvalidOCSPResponseException("responseObject is invalid");
    }

    if (ocspResp.getStatus() != 0) {
        return ocspResp;
    }

    if (respObject instanceof BasicOCSPResp == false) {
        return ocspResp;
    }

    BasicOCSPResp basicOCSPResp = (BasicOCSPResp) respObject;

    if (nonce != null) {
        Extension nonceExtn = basicOCSPResp.getExtension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce);
        if (nonceExtn == null) {
            throw new OCSPNonceUnmatchedException(nonce, null);
        }
        byte[] receivedNonce = nonceExtn.getExtnValue().getOctets();
        if (Arrays.equals(nonce, receivedNonce) == false) {
            throw new OCSPNonceUnmatchedException(nonce, receivedNonce);
        }
    }

    SingleResp[] singleResponses = basicOCSPResp.getResponses();
    final int countSingleResponses = singleResponses == null ? 0 : singleResponses.length;
    if (countSingleResponses != serialNumbers.length) {
        StringBuilder sb = new StringBuilder(100);
        sb.append("response with ").append(countSingleResponses).append(" singleRessponse");
        if (countSingleResponses > 1) {
            sb.append("s");
        }
        sb.append(" is returned, expected is ").append(serialNumbers.length);
        throw new OCSPTargetUnmatchedException(sb.toString());
    }

    CertificateID certID = ocspReq.getRequestList()[0].getCertID();
    ASN1ObjectIdentifier issuerHashAlg = certID.getHashAlgOID();
    byte[] issuerKeyHash = certID.getIssuerKeyHash();
    byte[] issuerNameHash = certID.getIssuerNameHash();

    if (serialNumbers.length == 1) {
        SingleResp m = singleResponses[0];
        CertificateID cid = m.getCertID();
        boolean issuerMatch = issuerHashAlg.equals(cid.getHashAlgOID())
                && Arrays.equals(issuerKeyHash, cid.getIssuerKeyHash())
                && Arrays.equals(issuerNameHash, cid.getIssuerNameHash());

        if (issuerMatch == false) {
            throw new OCSPTargetUnmatchedException("the issuer is not requested");
        }

        BigInteger serialNumber = cid.getSerialNumber();
        if (serialNumbers[0].equals(serialNumber) == false) {
            throw new OCSPTargetUnmatchedException("the serialNumber is not requested");
        }
    } else {
        List<BigInteger> tmpSerials1 = Arrays.asList(serialNumbers);
        List<BigInteger> tmpSerials2 = new ArrayList<>(tmpSerials1);

        for (int i = 0; i < singleResponses.length; i++) {
            SingleResp m = singleResponses[i];
            CertificateID cid = m.getCertID();
            boolean issuerMatch = issuerHashAlg.equals(cid.getHashAlgOID())
                    && Arrays.equals(issuerKeyHash, cid.getIssuerKeyHash())
                    && Arrays.equals(issuerNameHash, cid.getIssuerNameHash());

            if (issuerMatch == false) {
                throw new OCSPTargetUnmatchedException(
                        "the issuer specified in singleResponse[" + i + "] is not requested");
            }

            BigInteger serialNumber = cid.getSerialNumber();
            if (tmpSerials2.remove(serialNumber) == false) {
                if (tmpSerials1.contains(serialNumber)) {
                    throw new OCSPTargetUnmatchedException(
                            "serialNumber " + serialNumber + "is contained in at least two singleResponses");
                } else {
                    throw new OCSPTargetUnmatchedException(
                            "the serialNumber specified in singleResponse[" + i + "] is not requested");
                }
            }
        }
    }

    return ocspResp;
}

From source file:org.xipki.ocsp.client.shell.OCSPStatusCommand.java

License:Open Source License

@Override
protected Object processResponse(final OCSPResp response, final X509Certificate respIssuer,
        final X509Certificate issuer, final List<BigInteger> serialNumbers,
        final Map<BigInteger, byte[]> encodedCerts) throws Exception {
    BasicOCSPResp basicResp = OCSPUtils.extractBasicOCSPResp(response);

    boolean extendedRevoke = basicResp.getExtension(OCSPRequestor.id_pkix_ocsp_extendedRevoke) != null;

    SingleResp[] singleResponses = basicResp.getResponses();

    int n = singleResponses == null ? 0 : singleResponses.length;
    if (n == 0) {
        throw new CmdFailure("received no status from server");
    }/*  ww w . j av a  2 s .co  m*/

    if (n != serialNumbers.size()) {
        throw new CmdFailure("received status with " + n + " single responses from server, but "
                + serialNumbers.size() + " were requested");
    }

    Date[] thisUpdates = new Date[n];
    for (int i = 0; i < n; i++) {
        thisUpdates[i] = singleResponses[i].getThisUpdate();
    }

    // check the signature if available
    if (null == basicResp.getSignature()) {
        out("response is not signed");
    } else {
        X509CertificateHolder[] responderCerts = basicResp.getCerts();
        if (responderCerts == null || responderCerts.length < 1) {
            throw new CmdFailure("no responder certificate is contained in the response");
        }

        X509CertificateHolder respSigner = responderCerts[0];
        boolean validOn = true;
        for (Date thisUpdate : thisUpdates) {
            validOn = respSigner.isValidOn(thisUpdate);
            if (validOn == false) {
                throw new CmdFailure("responder certificate is not valid on " + thisUpdate);
            }
        }

        if (validOn) {
            PublicKey responderPubKey = KeyUtil.generatePublicKey(respSigner.getSubjectPublicKeyInfo());
            ContentVerifierProvider cvp = KeyUtil.getContentVerifierProvider(responderPubKey);
            boolean sigValid = basicResp.isSignatureValid(cvp);

            if (sigValid == false) {
                throw new CmdFailure("response is equipped with invalid signature");
            }

            // verify the OCSPResponse signer
            if (respIssuer != null) {
                boolean certValid = true;
                X509Certificate jceRespSigner = new X509CertificateObject(respSigner.toASN1Structure());
                if (X509Util.issues(respIssuer, jceRespSigner)) {
                    try {
                        jceRespSigner.verify(respIssuer.getPublicKey());
                    } catch (SignatureException e) {
                        certValid = false;
                    }
                }

                if (certValid == false) {
                    throw new CmdFailure(
                            "response is equipped with valid signature but the OCSP signer is not trusted");
                }
            } else {
                out("response is equipped with valid signature");
            }
        }

        if (verbose.booleanValue()) {
            out("responder is " + X509Util.getRFC4519Name(responderCerts[0].getSubject()));
        }
    }

    for (int i = 0; i < n; i++) {
        if (n > 1) {
            out("---------------------------- " + i + " ----------------------------");
        }
        SingleResp singleResp = singleResponses[i];
        BigInteger serialNumber = singleResp.getCertID().getSerialNumber();

        CertificateStatus singleCertStatus = singleResp.getCertStatus();

        String status;
        if (singleCertStatus == null) {
            status = "good";
        } else if (singleCertStatus instanceof RevokedStatus) {
            RevokedStatus revStatus = (RevokedStatus) singleCertStatus;
            Date revTime = revStatus.getRevocationTime();
            Date invTime = null;
            Extension ext = singleResp.getExtension(Extension.invalidityDate);
            if (ext != null) {
                invTime = ASN1GeneralizedTime.getInstance(ext.getParsedValue()).getDate();
            }

            if (revStatus.hasRevocationReason()) {
                int reason = revStatus.getRevocationReason();
                if (extendedRevoke && reason == CRLReason.CERTIFICATE_HOLD.getCode()
                        && revTime.getTime() == 0) {
                    status = "unknown (RFC6960)";
                } else {
                    StringBuilder sb = new StringBuilder("revoked, reason = ");
                    sb.append(CRLReason.forReasonCode(reason).getDescription());
                    sb.append(", revocationTime = ");
                    sb.append(revTime);
                    if (invTime != null) {
                        sb.append(", invalidityTime = ");
                        sb.append(invTime);
                    }
                    status = sb.toString();
                }
            } else {
                status = "revoked, no reason, revocationTime = " + revTime;
            }
        } else if (singleCertStatus instanceof UnknownStatus) {
            status = "unknown (RFC2560)";
        } else {
            status = "ERROR";
        }

        StringBuilder msg = new StringBuilder();
        msg.append("serialNumber: ").append(serialNumber);
        msg.append("\nCertificate status: ").append(status);

        if (verbose.booleanValue()) {
            msg.append("\nthisUpdate: " + singleResp.getThisUpdate());
            msg.append("\nnextUpdate: " + singleResp.getNextUpdate());

            Extension extension = singleResp.getExtension(ISISMTTObjectIdentifiers.id_isismtt_at_certHash);
            if (extension != null) {
                msg.append("\nCertHash is provided:\n");
                ASN1Encodable extensionValue = extension.getParsedValue();
                CertHash certHash = CertHash.getInstance(extensionValue);
                ASN1ObjectIdentifier hashAlgOid = certHash.getHashAlgorithm().getAlgorithm();
                byte[] hashValue = certHash.getCertificateHash();

                msg.append("\tHash algo : ").append(hashAlgOid.getId()).append("\n");
                msg.append("\tHash value: ").append(Hex.toHexString(hashValue)).append("\n");

                if (encodedCerts != null) {
                    byte[] encodedCert = encodedCerts.get(serialNumber);
                    MessageDigest md = MessageDigest.getInstance(hashAlgOid.getId());
                    byte[] expectedHashValue = md.digest(encodedCert);
                    if (Arrays.equals(expectedHashValue, hashValue)) {
                        msg.append("\tThis matches the requested certificate");
                    } else {
                        msg.append("\tThis differs from the requested certificate");
                    }
                }
            }

            extension = singleResp.getExtension(OCSPObjectIdentifiers.id_pkix_ocsp_archive_cutoff);
            if (extension != null) {
                ASN1Encodable extensionValue = extension.getParsedValue();
                ASN1GeneralizedTime time = ASN1GeneralizedTime.getInstance(extensionValue);
                msg.append("\nArchive-CutOff: ");
                msg.append(time.getTimeString());
            }

            AlgorithmIdentifier sigAlg = basicResp.getSignatureAlgorithmID();
            if (sigAlg == null) {
                msg.append(("\nresponse is not signed"));
            } else {
                String sigAlgName = AlgorithmUtil.getSignatureAlgoName(sigAlg);
                if (sigAlgName == null) {
                    sigAlgName = "unknown";
                }
                msg.append("\nresponse is signed with ").append(sigAlgName);
            }

            // extensions
            msg.append("\nExtensions: ");

            List<?> extensionOIDs = basicResp.getExtensionOIDs();
            if (extensionOIDs == null || extensionOIDs.size() == 0) {
                msg.append("-");
            } else {
                int size = extensionOIDs.size();
                for (int j = 0; j < size; j++) {
                    ASN1ObjectIdentifier extensionOID = (ASN1ObjectIdentifier) extensionOIDs.get(j);
                    String name = extensionOidNameMap.get(extensionOID);
                    msg.append(name == null ? extensionOID.getId() : name);
                    if (j != size - 1) {
                        msg.append(", ");
                    }
                }
            }
        }

        out(msg.toString());
    }
    out("");

    return null;
}

From source file:org.xipki.ocsp.qa.impl.OcspQAImpl.java

License:Open Source License

@Override
public ValidationResult checkOCSP(final OCSPResp response, final X509Certificate issuer,
        final List<BigInteger> serialNumbers, final Map<BigInteger, byte[]> encodedCerts,
        final OcspError expectedOcspError, final Map<BigInteger, OcspCertStatus> expectedOcspStatuses,
        final OcspResponseOption responseOption) {
    List<ValidationIssue> resultIssues = new LinkedList<ValidationIssue>();

    int status = response.getStatus();

    // Response status
    {/*from   www.j  av a 2  s . com*/
        ValidationIssue issue = new ValidationIssue("OCSP.STATUS", "response.status");
        resultIssues.add(issue);
        if (expectedOcspError != null) {
            if (status != expectedOcspError.getStatus()) {
                issue.setFailureMessage(
                        "is '" + status + "', but expected '" + expectedOcspError.getStatus() + "'");
            }
        } else {
            if (status != 0) {
                issue.setFailureMessage("is '" + status + "', but expected '0'");
            }
        }
    }

    if (status != 0) {
        return new ValidationResult(resultIssues);
    }

    ValidationIssue encodingIssue = new ValidationIssue("OCSP.ENCODING", "response encoding");
    resultIssues.add(encodingIssue);

    BasicOCSPResp basicResp;
    {
        try {
            basicResp = (BasicOCSPResp) response.getResponseObject();
        } catch (OCSPException e) {
            encodingIssue.setFailureMessage(e.getMessage());
            return new ValidationResult(resultIssues);
        }
    }

    SingleResp[] singleResponses = basicResp.getResponses();

    {
        ValidationIssue issue = new ValidationIssue("OCSP.RESPONSES.NUM", "number of single responses");
        resultIssues.add(issue);

        int n = singleResponses == null ? 0 : singleResponses.length;
        if (n == 0) {
            issue.setFailureMessage("received no status from server");
        } else if (n != serialNumbers.size()) {
            issue.setFailureMessage("is '" + n + "', but expected '" + serialNumbers.size() + "'");
        }

        if (issue.isFailed()) {
            return new ValidationResult(resultIssues);
        }
    }

    {
        boolean hasSignature = basicResp.getSignature() != null;

        {
            // check the signature if available
            ValidationIssue issue = new ValidationIssue("OCSP.SIG", "signature presence");
            resultIssues.add(issue);
            if (hasSignature == false) {
                issue.setFailureMessage("response is not signed");
            }
        }

        if (hasSignature) {
            {
                // signature algorithm
                ValidationIssue issue = new ValidationIssue("OCSP.SIG.ALG", "signature algorithm");
                resultIssues.add(issue);

                String expectedSigalgo = responseOption.getSignatureAlgName();
                if (expectedSigalgo != null) {
                    AlgorithmIdentifier sigAlg = basicResp.getSignatureAlgorithmID();
                    try {
                        String sigAlgName = AlgorithmUtil.getSignatureAlgoName(sigAlg);
                        if (AlgorithmUtil.equalsAlgoName(sigAlgName, expectedSigalgo) == false) {
                            issue.setFailureMessage(
                                    "is '" + sigAlgName + "', but expected '" + expectedSigalgo + "'");
                        }
                    } catch (NoSuchAlgorithmException e) {
                        issue.setFailureMessage("could not extract the signature algorithm");
                    }
                }
            }

            // signer certificate
            ValidationIssue sigSignerCertIssue = new ValidationIssue("OCSP.SIGNERCERT", "signer certificate");
            resultIssues.add(sigSignerCertIssue);

            // signature validation
            ValidationIssue sigValIssue = new ValidationIssue("OCSP.SIG.VALIDATION", "signature validation");
            resultIssues.add(sigValIssue);

            X509CertificateHolder[] responderCerts = basicResp.getCerts();
            if (responderCerts == null || responderCerts.length < 1) {
                sigSignerCertIssue.setFailureMessage("No responder certificate is contained in the response");
                sigValIssue.setFailureMessage("could not find certificate to validate signature");
            } else {
                X509CertificateHolder respSigner = responderCerts[0];

                ValidationIssue issue = new ValidationIssue("OCSP.SIGNERCERT.TRUST",
                        "signer certificate validation");
                resultIssues.add(issue);

                for (int i = 0; i < singleResponses.length; i++) {
                    SingleResp singleResp = singleResponses[i];
                    if (respSigner.isValidOn(singleResp.getThisUpdate()) == false) {
                        issue.setFailureMessage("responder certificate is not valid on the thisUpdate[ " + i
                                + "]" + singleResp.getThisUpdate());
                    }
                }

                if (issue.isFailed() == false) {
                    X509Certificate respIssuer = responseOption.getRespIssuer();
                    if (respIssuer != null) {
                        X509Certificate jceRespSigner;
                        try {
                            jceRespSigner = new X509CertificateObject(respSigner.toASN1Structure());
                            if (X509Util.issues(respIssuer, jceRespSigner)) {
                                jceRespSigner.verify(respIssuer.getPublicKey());
                            } else {
                                issue.setFailureMessage("responder signer is not trusted");
                            }
                        } catch (Exception e) {
                            issue.setFailureMessage("responder signer is not trusted");
                        }
                    }
                }

                try {
                    PublicKey responderPubKey = KeyUtil.generatePublicKey(respSigner.getSubjectPublicKeyInfo());
                    ContentVerifierProvider cvp = KeyUtil.getContentVerifierProvider(responderPubKey);
                    boolean sigValid = basicResp.isSignatureValid(cvp);
                    if (sigValid == false) {
                        sigValIssue.setFailureMessage("signature is invalid");
                    }
                } catch (Exception e) {
                    sigValIssue.setFailureMessage("error while validating signature");
                }
            }
        }
    }

    {
        // nonce
        Extension nonceExtn = basicResp.getExtension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce);
        resultIssues.add(checkOccurrence("OCSP.NONCE", nonceExtn, responseOption.getNonceOccurrence()));
    }

    boolean extendedRevoke = basicResp.getExtension(id_pkix_ocsp_extendedRevoke) != null;

    for (int i = 0; i < singleResponses.length; i++) {
        SingleResp singleResp = singleResponses[i];
        BigInteger serialNumber = singleResp.getCertID().getSerialNumber();
        OcspCertStatus expectedStatus = expectedOcspStatuses.get(serialNumber);

        byte[] encodedCert = null;
        if (encodedCerts != null) {
            encodedCert = encodedCerts.get(serialNumber);
        }

        List<ValidationIssue> issues = checkSingleCert(i, singleResp, expectedStatus, encodedCert,
                extendedRevoke, responseOption.getNextUpdateOccurrence(),
                responseOption.getCerthashOccurrence(), responseOption.getCerthashAlgId());
        resultIssues.addAll(issues);
    }

    return new ValidationResult(resultIssues);
}