diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/utils/sslcert/crl/CRLCache.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/utils/sslcert/crl/CRLCache.java index 0a8274a35..b7c591429 100644 --- a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/utils/sslcert/crl/CRLCache.java +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/utils/sslcert/crl/CRLCache.java @@ -47,12 +47,10 @@ public class CRLCache implements ManageableCache { private final Map hashMap; private Iterator> iterator; private volatile CacheManager cacheManager; - private final CRLVerifier crlVerifier; private CRLCache() { hashMap = new ConcurrentHashMap(); iterator = hashMap.entrySet().iterator(); - crlVerifier = new CRLVerifier(null); } public static CRLCache getCache() { @@ -116,15 +114,7 @@ private synchronized void replaceNewCacheValue(CRLCacheValue cacheValue) { return; } - try { - String crlUrl = cacheValue.crlUrl; - X509CRL x509CRL = crlVerifier.downloadCRLFromWeb(crlUrl); - this.setCacheValue(crlUrl, x509CRL); - } catch (Exception e) { - log.debug("Cant replace old CacheValue with new CacheValue. So remove", e); - //If cant be replaced remove. - cacheValue.removeThisCacheValue(); - } + cacheValue.removeThisCacheValue(); } public synchronized X509CRL getCacheValue(String crlUrl) { diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/utils/sslcert/crl/CRLVerifier.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/utils/sslcert/crl/CRLVerifier.java index dd8ea788e..082bcc10d 100644 --- a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/utils/sslcert/crl/CRLVerifier.java +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/utils/sslcert/crl/CRLVerifier.java @@ -32,8 +32,10 @@ import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; +import java.security.GeneralSecurityException; import java.security.cert.*; import java.util.ArrayList; +import java.util.Date; import java.util.List; /** @@ -46,6 +48,18 @@ public class CRLVerifier implements RevocationVerifier { private CRLCache cache; + /** + * Tolerance applied to both ends of a CRL's validity window, to allow for clock skew between + * this host and the CA that published it. + */ + private static final long CLOCK_SKEW_MILLIS = 5 * 60 * 1000L; + + /** + * How old a CRL may be when it carries no nextUpdate, which RFC 5280 section 5.1.2.5 + * leaves optional. + */ + private static final long MAX_CRL_AGE_MILLIS = 24 * 60 * 60 * 1000L; + public CRLVerifier(CRLCache cache) { this.cache = cache; } @@ -54,7 +68,7 @@ public CRLVerifier(CRLCache cache) { * Checks revocation status (Good, Revoked) of the peer certificate. * * @param peerCert peer certificate - * @param issuerCert issuer certificate of the peer. not used currently. + * @param issuerCert issuer of the peer certificate; every CRL used here is verified against it. * @return revocation status of the peer certificate. * @throws CertificateVerificationException * @@ -74,9 +88,18 @@ public RevocationStatus checkRevocationStatus(X509Certificate peerCert, X509Cert X509CRL x509CRL = cache.getCacheValue(crlUrl); if (x509CRL != null) { //If cant be casted, we have used the wrong cache. - RevocationStatus status = getRevocationStatus(x509CRL, peerCert); - log.debug("CRL taken from cache...."); - return status; + try { + verifyCRL(x509CRL, issuerCert); + RevocationStatus status = getRevocationStatus(x509CRL, peerCert); + log.debug("CRL taken from cache...."); + return status; + } catch (CertificateVerificationException e) { + if (log.isDebugEnabled()) { + log.debug("Discarding the cached CRL for " + crlUrl + + " and downloading it again.", e); + } + cache.removeCacheValue(crlUrl); + } } } @@ -84,6 +107,7 @@ public RevocationStatus checkRevocationStatus(X509Certificate peerCert, X509Cert try { X509CRL x509CRL = downloadCRLFromWeb(crlUrl); if (x509CRL != null) { + verifyCRL(x509CRL, issuerCert); if (cache != null) { cache.setCacheValue(crlUrl, x509CRL); } @@ -108,6 +132,55 @@ private RevocationStatus getRevocationStatus(X509CRL x509CRL, X509Certificate pe } } + /** + * Verifies a CRL was published by the issuer of the certificate being checked and is still + * current, rejecting forged or stale CRLs and indirect CRLs signed by a separate CRL issuer. + * + * @param x509CRL the CRL that was fetched or taken from the cache. + * @param issuerCert the issuer of the certificate being checked. + * @throws CertificateVerificationException if the CRL is not validly signed by that issuer or + * is no longer current. + */ + private void verifyCRL(X509CRL x509CRL, X509Certificate issuerCert) + throws CertificateVerificationException { + + if (issuerCert == null) { + throw new CertificateVerificationException("Cannot verify a CRL without the issuer " + + "certificate"); + } + + if (!x509CRL.getIssuerX500Principal().equals(issuerCert.getSubjectX500Principal())) { + throw new CertificateVerificationException("CRL issuer " + + x509CRL.getIssuerX500Principal() + " is not the issuer of the certificate " + + "being checked"); + } + + try { + x509CRL.verify(issuerCert.getPublicKey()); + } catch (GeneralSecurityException e) { + throw new CertificateVerificationException("CRL is not signed by the issuer of the " + + "certificate being checked", e); + } + + long now = System.currentTimeMillis(); + Date thisUpdate = x509CRL.getThisUpdate(); + Date nextUpdate = x509CRL.getNextUpdate(); + + if (thisUpdate == null) { + throw new CertificateVerificationException("CRL does not say when it was issued"); + } + if (thisUpdate.getTime() > now + CLOCK_SKEW_MILLIS) { + throw new CertificateVerificationException("CRL is not valid yet. It is dated " + + thisUpdate); + } + if (nextUpdate != null && nextUpdate.getTime() < now - CLOCK_SKEW_MILLIS) { + throw new CertificateVerificationException("CRL expired on " + nextUpdate); + } else if (nextUpdate == null && thisUpdate.getTime() < now - MAX_CRL_AGE_MILLIS) { + throw new CertificateVerificationException("CRL carries no nextUpdate and is older " + + "than the accepted maximum. It is dated " + thisUpdate); + } + } + /** * Downloads CRL from the crlUrl. Does not support HTTPS */ diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/utils/sslcert/ocsp/OCSPCache.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/utils/sslcert/ocsp/OCSPCache.java index 937265f20..f78fd4266 100644 --- a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/utils/sslcert/ocsp/OCSPCache.java +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/utils/sslcert/ocsp/OCSPCache.java @@ -22,14 +22,11 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.synapse.commons.jmx.MBeanRegistrar; -import org.apache.synapse.transport.utils.sslcert.CertificateVerificationException; import org.apache.synapse.transport.utils.sslcert.cache.CacheController; import org.apache.synapse.transport.utils.sslcert.cache.CacheManager; import org.apache.synapse.transport.utils.sslcert.cache.ManageableCache; import org.apache.synapse.transport.utils.sslcert.cache.ManageableCacheValue; -import org.bouncycastle.cert.ocsp.BasicOCSPResp; import org.bouncycastle.cert.ocsp.OCSPReq; -import org.bouncycastle.cert.ocsp.OCSPResp; import org.bouncycastle.cert.ocsp.SingleResp; import java.math.BigInteger; @@ -52,12 +49,10 @@ public class OCSPCache implements ManageableCache { private final Map hashMap; private Iterator> iterator; private volatile CacheManager cacheManager; - private final OCSPVerifier ocspVerifier; private OCSPCache() { this.hashMap = new ConcurrentHashMap(); this.iterator = hashMap.entrySet().iterator(); - this.ocspVerifier = new OCSPVerifier(null); } public static OCSPCache getCache() { @@ -120,28 +115,7 @@ private synchronized void replaceNewCacheValue(OCSPCacheValue cacheValue){ return; } - try { - String serviceUrl = cacheValue.serviceUrl; - OCSPReq request = cacheValue.request; - OCSPResp response= ocspVerifier.getOCSPResponse(serviceUrl, request); - - if (OCSPResp.SUCCESSFUL != response.getStatus()) - throw new CertificateVerificationException("OCSP response status not SUCCESSFUL"); - - BasicOCSPResp basicResponse = (BasicOCSPResp) response.getResponseObject(); - SingleResp[] responses = (basicResponse == null) ? null : basicResponse.getResponses(); - - if (responses == null) - throw new CertificateVerificationException("Cant get OCSP response"); - - SingleResp resp = responses[0]; - this.setCacheValue(cacheValue.serialNumber, resp, request, serviceUrl); - - } catch (Exception e){ - log.debug("Cant replace old CacheValue with new CacheValue. So remove", e); - //If cant be replaced remove. - cacheValue.removeThisCacheValue(); - } + cacheValue.removeThisCacheValue(); } public synchronized SingleResp getCacheValue(BigInteger serialNumber) { diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/utils/sslcert/ocsp/OCSPVerifier.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/utils/sslcert/ocsp/OCSPVerifier.java index dd2dea384..36f6390bd 100644 --- a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/utils/sslcert/ocsp/OCSPVerifier.java +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/utils/sslcert/ocsp/OCSPVerifier.java @@ -24,6 +24,7 @@ import org.bouncycastle.asn1.*; import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers; import org.bouncycastle.asn1.x509.*; +import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; import org.bouncycastle.cert.ocsp.BasicOCSPResp; import org.bouncycastle.cert.ocsp.CertificateID; @@ -35,8 +36,10 @@ import org.bouncycastle.cert.ocsp.RevokedStatus; import org.bouncycastle.cert.ocsp.SingleResp; import org.bouncycastle.cert.ocsp.UnknownStatus; +import org.bouncycastle.operator.ContentVerifierProvider; import org.bouncycastle.operator.DigestCalculator; import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder; import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; import org.apache.synapse.transport.utils.sslcert.*; @@ -48,6 +51,7 @@ import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.util.ArrayList; +import java.util.Date; import java.util.List; /** @@ -60,6 +64,18 @@ public class OCSPVerifier implements RevocationVerifier { private OCSPCache cache; + /** + * Tolerance applied to both ends of a response's validity window. RFC 6960 section 4.2.2.1 + * requires clients to allow for clock skew between themselves and the responder. + */ + private static final long CLOCK_SKEW_MILLIS = 5 * 60 * 1000L; + + /** + * How old a response may be when it carries no nextUpdate, which RFC 6960 section 4.2.2.1 + * leaves optional. + */ + private static final long MAX_RESPONSE_AGE_MILLIS = 24 * 60 * 60 * 1000L; + public OCSPVerifier(OCSPCache cache) { this.cache = cache; } @@ -68,7 +84,7 @@ public OCSPVerifier(OCSPCache cache) { * Gets the revocation status (Good, Revoked or Unknown) of the given peer certificate. * * @param peerCert The certificate we want to check if revoked. - * @param issuerCert Needed to create OCSP request. + * @param issuerCert its issuer, paired by PathChecker; sound because PKIX validation already confirmed chain order. * @return revocation status of the peer certificate. * @throws CertificateVerificationException * @@ -77,18 +93,35 @@ public OCSPVerifier(OCSPCache cache) { public RevocationStatus checkRevocationStatus(X509Certificate peerCert, X509Certificate issuerCert) throws CertificateVerificationException { + if (issuerCert == null) { + throw new CertificateVerificationException("Cannot check revocation status without " + + "the issuer certificate"); + } + + CertificateID certificateID = generateCertificateID(issuerCert, peerCert.getSerialNumber()); + //check cache if (cache != null) { SingleResp resp = cache.getCacheValue(peerCert.getSerialNumber()); if (resp != null) { - //If cant be casted, we have used the wrong cache. - RevocationStatus status = getRevocationStatus(resp); - log.debug("OCSP response taken from cache...."); - return status; + try { + verifyCertificateId(certificateID, resp); + verifyResponseFreshness(resp); + //If cant be casted, we have used the wrong cache. + RevocationStatus status = getRevocationStatus(resp); + log.debug("OCSP response taken from cache...."); + return status; + } catch (CertificateVerificationException e) { + if (log.isDebugEnabled()) { + log.debug("Discarding the cached OCSP response for serial number " + + peerCert.getSerialNumber() + " and asking the responder again.", e); + } + cache.removeCacheValue(peerCert.getSerialNumber()); + } } } - OCSPReq request = generateOCSPRequest(issuerCert, peerCert.getSerialNumber()); + OCSPReq request = generateOCSPRequest(certificateID); //This list will sometimes have non ocsp urls as well. List locations = getAIALocations(peerCert); @@ -102,14 +135,21 @@ public RevocationStatus checkRevocationStatus(X509Certificate peerCert, X509Cert } BasicOCSPResp basicResponse = (BasicOCSPResp) ocspResponse.getResponseObject(); + verifyResponseSignature(basicResponse, issuerCert); responses = (basicResponse == null) ? null : basicResponse.getResponses(); //todo use the super exception } catch (Exception e) { + if (log.isDebugEnabled()) { + log.debug("Could not obtain a usable OCSP response from " + serviceUrl + + ". Trying the next url in the list.", e); + } continue; } if (responses != null && responses.length == 1) { SingleResp resp = responses[0]; + verifyCertificateId(certificateID, resp); + verifyResponseFreshness(resp); RevocationStatus status = getRevocationStatus(resp); if (cache != null) cache.setCacheValue(peerCert.getSerialNumber(), resp, request, serviceUrl); @@ -119,6 +159,147 @@ public RevocationStatus checkRevocationStatus(X509Certificate peerCert, X509Cert throw new CertificateVerificationException("Cant get Revocation Status from OCSP."); } + /** + * Verifies the response is signed by the certificate issuer, or by a responder the issuer + * authorised via the id-kp-OCSPSigning extended key usage (RFC 6960 section 4.2.2.2). + * + * @param basicResponse the response to check. + * @param issuerCert the issuer of the certificate whose status was requested. + * @throws CertificateVerificationException if the response is not signed by a key the issuer + * authorised for this purpose. + */ + private void verifyResponseSignature(BasicOCSPResp basicResponse, X509Certificate issuerCert) + throws CertificateVerificationException { + + if (basicResponse == null) { + throw new CertificateVerificationException("OCSP response carries no basic response " + + "to verify"); + } + + X509CertificateHolder issuerHolder; + try { + issuerHolder = new JcaX509CertificateHolder(issuerCert); + } catch (CertificateEncodingException e) { + throw new CertificateVerificationException("Cannot read the issuer certificate while " + + "verifying the OCSP response signature", e); + } + + if (isSignedBy(basicResponse, issuerHolder)) { + return; + } + + X509CertificateHolder[] responderCerts = basicResponse.getCerts(); + if (responderCerts != null) { + for (X509CertificateHolder responderCert : responderCerts) { + if (hasOcspSigningExtendedKeyUsage(responderCert) + && responderCert.isValidOn(new Date()) + && isIssuedBy(responderCert, issuerHolder) + && isSignedBy(basicResponse, responderCert)) { + return; + } + } + } + + throw new CertificateVerificationException("OCSP response is not signed by the " + + "certificate issuer or by a responder the issuer delegated to"); + } + + /** + * @return true if the response signature verifies under the given certificate's public key. + */ + private boolean isSignedBy(BasicOCSPResp basicResponse, X509CertificateHolder signerCert) { + try { + ContentVerifierProvider verifierProvider = + new JcaContentVerifierProviderBuilder().build(signerCert); + return basicResponse.isSignatureValid(verifierProvider); + } catch (Exception e) { + if (log.isDebugEnabled()) { + log.debug("OCSP response signature did not verify under " + + signerCert.getSubject(), e); + } + return false; + } + } + + /** + * @return true if the certificate names the issuer as its issuer AND actually carries the + * issuer's signature; the name alone is not sufficient. + */ + private boolean isIssuedBy(X509CertificateHolder cert, X509CertificateHolder issuerCert) { + try { + if (!cert.getIssuer().equals(issuerCert.getSubject())) { + return false; + } + ContentVerifierProvider verifierProvider = + new JcaContentVerifierProviderBuilder().build(issuerCert); + return cert.isSignatureValid(verifierProvider); + } catch (Exception e) { + if (log.isDebugEnabled()) { + log.debug("Responder certificate " + cert.getSubject() + + " is not validly issued by " + issuerCert.getSubject(), e); + } + return false; + } + } + + /** + * @return true if the certificate carries the id-kp-OCSPSigning extended key usage, which is + * how a CA marks a certificate as authorised to answer OCSP on its behalf. + */ + private boolean hasOcspSigningExtendedKeyUsage(X509CertificateHolder cert) { + Extension extension = cert.getExtension(Extension.extendedKeyUsage); + if (extension == null) { + return false; + } + ExtendedKeyUsage extendedKeyUsage = ExtendedKeyUsage.getInstance(extension.getParsedValue()); + return extendedKeyUsage != null && extendedKeyUsage.hasKeyPurposeId(KeyPurposeId.id_kp_OCSPSigning); + } + + /** + * Verifies the response answers for the certificate actually asked about, not merely one + * sharing its cache key. + * + * @throws CertificateVerificationException if the response is about a different certificate. + */ + private void verifyCertificateId(CertificateID requestedCertificateId, SingleResp response) + throws CertificateVerificationException { + + if (!requestedCertificateId.equals(response.getCertID())) { + throw new CertificateVerificationException("OCSP response does not answer for the " + + "certificate it was requested for"); + } + } + + /** + * Verifies that the response is still within its validity window. + * + * @throws CertificateVerificationException if the response is stale or not yet valid. + */ + private void verifyResponseFreshness(SingleResp response) + throws CertificateVerificationException { + + long now = System.currentTimeMillis(); + Date thisUpdate = response.getThisUpdate(); + Date nextUpdate = response.getNextUpdate(); + + if (thisUpdate == null) { + throw new CertificateVerificationException("OCSP response does not say when it was " + + "issued"); + } + if (thisUpdate.getTime() > now + CLOCK_SKEW_MILLIS) { + throw new CertificateVerificationException("OCSP response is not valid yet. It is " + + "dated " + thisUpdate); + } + if (nextUpdate != null) { + if (nextUpdate.getTime() < now - CLOCK_SKEW_MILLIS) { + throw new CertificateVerificationException("OCSP response expired on " + nextUpdate); + } + } else if (thisUpdate.getTime() < now - MAX_RESPONSE_AGE_MILLIS) { + throw new CertificateVerificationException("OCSP response carries no nextUpdate and " + + "is older than the accepted maximum. It is dated " + thisUpdate); + } + } + private RevocationStatus getRevocationStatus(SingleResp resp) throws CertificateVerificationException { CertificateStatus status = resp.getCertStatus(); if (status == CertificateStatus.GOOD) { @@ -178,26 +359,19 @@ protected OCSPResp getOCSPResponse(String serviceUrl, } /** - * This method generates an OCSP Request to be sent to an OCSP endpoint. + * Generates an OCSP request for the given certificate identity. * - * @param issuerCert is the Certificate of the Issuer of the peer certificate we are interested in. - * @param serialNumber of the peer certificate. + * @param id the CertID of the certificate to ask about. * @return generated OCSP request. * @throws CertificateVerificationException */ - private OCSPReq generateOCSPRequest(X509Certificate issuerCert, BigInteger serialNumber) + private OCSPReq generateOCSPRequest(CertificateID id) throws CertificateVerificationException { //TODO: Have to check if this is OK with synapse implementation. //Add provider BC Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); try { - // CertID structure is used to uniquely identify certificates that are the subject of - // an OCSP request or response and has an ASN.1 definition. CertID structure is defined - // in RFC 2560 - DigestCalculator digestCalculator = new JcaDigestCalculatorProviderBuilder().build().get(CertificateID.HASH_SHA1); - CertificateID id = new CertificateID(digestCalculator, new JcaX509CertificateHolder(issuerCert), serialNumber); - // basic request generation with nonce OCSPReqBuilder builder = new OCSPReqBuilder(); builder.addRequest(id); @@ -211,12 +385,37 @@ private OCSPReq generateOCSPRequest(X509Certificate issuerCert, BigInteger seria builder.setRequestExtensions(new Extensions(new Extension[]{ext})); return builder.build(); - } catch (OCSPException | OperatorCreationException | CertificateEncodingException | IOException e) { + } catch (OCSPException | IOException e) { throw new CertificateVerificationException("Cannot generate OCSP Request with the " + "given certificate", e); } } + /** + * Builds the CertID (RFC 2560) identifying the certificate being asked about, usable for + * both a fresh request and a comparison against a cached response. + * + * @param issuerCert is the Certificate of the Issuer of the peer certificate we are interested in. + * @param serialNumber of the peer certificate. + * @return the CertID for that certificate. + * @throws CertificateVerificationException + */ + private CertificateID generateCertificateID(X509Certificate issuerCert, BigInteger serialNumber) + throws CertificateVerificationException { + + Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); + try { + // CertID structure is used to uniquely identify certificates that are the subject of + // an OCSP request or response and has an ASN.1 definition. CertID structure is defined + // in RFC 2560 + DigestCalculator digestCalculator = new JcaDigestCalculatorProviderBuilder().build().get(CertificateID.HASH_SHA1); + return new CertificateID(digestCalculator, new JcaX509CertificateHolder(issuerCert), serialNumber); + } catch (OCSPException | OperatorCreationException | CertificateEncodingException e) { + throw new CertificateVerificationException("Cannot identify the certificate to be " + + "checked with OCSP", e); + } + } + /** * Authority Information Access (AIA) is a non-critical extension in an X509 Certificate. This contains the * URL of the OCSP endpoint if one is available. diff --git a/modules/transports/core/nhttp/src/test/java/org/apache/synapse/transport/utils/sslcert/CRLVerifierTest.java b/modules/transports/core/nhttp/src/test/java/org/apache/synapse/transport/utils/sslcert/CRLVerifierTest.java index 12d5208ac..977363405 100644 --- a/modules/transports/core/nhttp/src/test/java/org/apache/synapse/transport/utils/sslcert/CRLVerifierTest.java +++ b/modules/transports/core/nhttp/src/test/java/org/apache/synapse/transport/utils/sslcert/CRLVerifierTest.java @@ -80,7 +80,8 @@ public void testRevokedCertificate() throws Exception { cache.setCacheValue(crlDistributionPointUrl, x509CRL); CRLVerifier crlVerifier = new CRLVerifier(cache); - RevocationStatus status = crlVerifier.checkRevocationStatus(fakeRevokedCertificate, null); + RevocationStatus status = crlVerifier.checkRevocationStatus(fakeRevokedCertificate, + fakeCACert); //the fake crl we created will be checked if the fake certificate is revoked. So the // status should be REVOKED. diff --git a/modules/transports/core/nhttp/src/test/java/org/apache/synapse/transport/utils/sslcert/OCSPVerifierTest.java b/modules/transports/core/nhttp/src/test/java/org/apache/synapse/transport/utils/sslcert/OCSPVerifierTest.java index 8eb88d169..e65cb1709 100644 --- a/modules/transports/core/nhttp/src/test/java/org/apache/synapse/transport/utils/sslcert/OCSPVerifierTest.java +++ b/modules/transports/core/nhttp/src/test/java/org/apache/synapse/transport/utils/sslcert/OCSPVerifierTest.java @@ -124,15 +124,32 @@ private OCSPReq getOCSPRequest(X509Certificate caCert, BigInteger revokedSerialN OCSPVerifier ocspVerifier = new OCSPVerifier(null); Class ocspVerifierClass = ocspVerifier.getClass(); Method generateOCSPRequest = ocspVerifierClass.getDeclaredMethod("generateOCSPRequest", - X509Certificate.class, - BigInteger.class); + CertificateID.class); generateOCSPRequest.setAccessible(true); - OCSPReq request = (OCSPReq) generateOCSPRequest.invoke(ocspVerifier, caCert, - revokedSerialNumber); + OCSPReq request = (OCSPReq) generateOCSPRequest.invoke(ocspVerifier, + getCertificateID(caCert, revokedSerialNumber)); return request; } + /** + * Builds the CertID for a certificate, by calling the private generateCertificateID(..) + * method in OCSPVerifier through reflection. + * + * @param caCert the fake CA certificate. + * @param serialNumber the serial number of the certificate being asked about. + * @return the CertID identifying that certificate. + * @throws Exception + */ + private CertificateID getCertificateID(X509Certificate caCert, BigInteger serialNumber) + throws Exception { + OCSPVerifier ocspVerifier = new OCSPVerifier(null); + Method generateCertificateID = ocspVerifier.getClass().getDeclaredMethod( + "generateCertificateID", X509Certificate.class, BigInteger.class); + generateCertificateID.setAccessible(true); + return (CertificateID) generateCertificateID.invoke(ocspVerifier, caCert, serialNumber); + } + /** * This makes the corresponding OCSP response to the OCSP request which is sent to the fake CA. * If the request has a certificateID which is marked as revoked by the CA, the OCSP response