org.ejbca.core.protocol.ocsp.ProtocolOcspHttpStandaloneTest.java Source code

Java tutorial

Introduction

Here is the source code for org.ejbca.core.protocol.ocsp.ProtocolOcspHttpStandaloneTest.java

Source

/*************************************************************************
 *                                                                       *
 *  EJBCA Community: The OpenSource Certificate Authority                *
 *                                                                       *
 *  This software is free software; you can redistribute it and/or       *
 *  modify it under the terms of the GNU Lesser General Public           *
 *  License as published by the Free Software Foundation; either         *
 *  version 2.1 of the License, or any later version.                    *
 *                                                                       *
 *  See terms of license at gnu.org.                                     *
 *                                                                       *
 *************************************************************************/

package org.ejbca.core.protocol.ocsp;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.OutputStream;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.SignatureException;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.ejb.CreateException;

import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.ocsp.BasicOCSPResp;
import org.bouncycastle.cert.ocsp.CertificateID;
import org.bouncycastle.cert.ocsp.OCSPReq;
import org.bouncycastle.cert.ocsp.OCSPReqBuilder;
import org.bouncycastle.cert.ocsp.OCSPResp;
import org.bouncycastle.cert.ocsp.OCSPRespBuilder;
import org.bouncycastle.cert.ocsp.RevokedStatus;
import org.bouncycastle.cert.ocsp.SingleResp;
import org.bouncycastle.cert.ocsp.jcajce.JcaCertificateID;
import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
import org.bouncycastle.util.encoders.Hex;
import org.cesecore.authentication.tokens.AuthenticationToken;
import org.cesecore.authorization.AuthorizationDeniedException;
import org.cesecore.certificates.ca.X509CA;
import org.cesecore.certificates.certificate.CertificateConstants;
import org.cesecore.certificates.certificate.CertificateStoreSessionRemote;
import org.cesecore.certificates.certificate.InternalCertificateStoreSessionRemote;
import org.cesecore.certificates.certificateprofile.CertificateProfileConstants;
import org.cesecore.certificates.ocsp.OcspResponseGeneratorTestSessionRemote;
import org.cesecore.certificates.ocsp.OcspTestUtils;
import org.cesecore.certificates.ocsp.SHA1DigestCalculator;
import org.cesecore.certificates.util.AlgorithmConstants;
import org.cesecore.config.GlobalOcspConfiguration;
import org.cesecore.config.OcspConfiguration;
import org.cesecore.configuration.CesecoreConfigurationProxySessionRemote;
import org.cesecore.configuration.GlobalConfigurationSessionRemote;
import org.cesecore.keybind.InternalKeyBindingMgmtSessionRemote;
import org.cesecore.keybind.InternalKeyBindingStatus;
import org.cesecore.keybind.impl.OcspKeyBinding;
import org.cesecore.keys.token.CryptoTokenManagementSessionRemote;
import org.cesecore.keys.token.CryptoTokenTestUtils;
import org.cesecore.mock.authentication.tokens.TestAlwaysAllowLocalAuthenticationToken;
import org.cesecore.util.Base64;
import org.cesecore.util.CertTools;
import org.cesecore.util.EjbRemoteHelper;
import org.cesecore.util.TraceLogMethodsRule;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;

/**
 * @version $Id: ProtocolOcspHttpStandaloneTest.java 20003 2014-10-16 17:14:03Z mikekushner $
 */
public class ProtocolOcspHttpStandaloneTest extends ProtocolOcspTestBase {

    private static final Logger log = Logger.getLogger(ProtocolOcspHttpStandaloneTest.class);

    private static final String TESTCLASSNAME = ProtocolOcspHttpStandaloneTest.class.getSimpleName();
    private static final String CA_DN = "CN=OcspDefaultTestCA,O=Foo,C=SE";

    private CesecoreConfigurationProxySessionRemote configurationSession = EjbRemoteHelper.INSTANCE
            .getRemoteSession(CesecoreConfigurationProxySessionRemote.class, EjbRemoteHelper.MODULE_TEST);
    private GlobalConfigurationSessionRemote globalConfigurationSession = EjbRemoteHelper.INSTANCE
            .getRemoteSession(GlobalConfigurationSessionRemote.class);
    private OcspResponseGeneratorTestSessionRemote ocspResponseGeneratorTestSession = EjbRemoteHelper.INSTANCE
            .getRemoteSession(OcspResponseGeneratorTestSessionRemote.class, EjbRemoteHelper.MODULE_TEST);

    private static final AuthenticationToken authenticationToken = new TestAlwaysAllowLocalAuthenticationToken(
            TESTCLASSNAME);
    private static X509CA x509ca;
    private static int cryptoTokenId;
    private static int internalKeyBindingId;
    private static X509Certificate ocspSigningCertificate;
    private static X509Certificate caCertificate;

    @Rule
    public TestRule traceLogMethodsRule = new TraceLogMethodsRule();

    public ProtocolOcspHttpStandaloneTest() throws MalformedURLException, URISyntaxException {
        super("http", "ejbca", "publicweb/status/ocsp");
    }

    @BeforeClass
    public static void beforeClass() throws Exception {
        x509ca = CryptoTokenTestUtils.createTestCAWithSoftCryptoToken(authenticationToken, CA_DN);
        cryptoTokenId = CryptoTokenTestUtils.createSoftCryptoToken(authenticationToken, TESTCLASSNAME);
        internalKeyBindingId = OcspTestUtils.createInternalKeyBinding(authenticationToken, cryptoTokenId,
                OcspKeyBinding.IMPLEMENTATION_ALIAS, TESTCLASSNAME, "RSA2048",
                AlgorithmConstants.SIGALG_SHA1_WITH_RSA);
        String signerDN = "CN=ocspTestSigner,O=Foo,C=SE";
        ocspSigningCertificate = OcspTestUtils.createOcspSigningCertificate(authenticationToken,
                OcspTestUtils.OCSP_END_USER_NAME, signerDN, internalKeyBindingId, x509ca.getCAId());
        OcspTestUtils.updateInternalKeyBindingCertificate(authenticationToken, internalKeyBindingId);
        OcspTestUtils.setInternalKeyBindingStatus(authenticationToken, internalKeyBindingId,
                InternalKeyBindingStatus.ACTIVE);
        caCertificate = createCaCertificate(authenticationToken, x509ca.getCACertificate());
        setupTestCertificates(x509ca.getCAId());
    }

    @AfterClass
    public static void afterClass() throws Exception {
        removeTestCertifices();
        InternalCertificateStoreSessionRemote internalCertificateStoreSession = EjbRemoteHelper.INSTANCE
                .getRemoteSession(InternalCertificateStoreSessionRemote.class, EjbRemoteHelper.MODULE_TEST);
        try {
            internalCertificateStoreSession.removeCertificate(ocspSigningCertificate);
        } catch (Exception e) {
            //Ignore any failures.
        }
        try {
            internalCertificateStoreSession.removeCertificate(caCertificate);
        } catch (Exception e) {
            //Ignore any failures.
        }

        InternalKeyBindingMgmtSessionRemote internalKeyBindingMgmtSession = EjbRemoteHelper.INSTANCE
                .getRemoteSession(InternalKeyBindingMgmtSessionRemote.class);
        CryptoTokenManagementSessionRemote cryptoTokenManagementSession = EjbRemoteHelper.INSTANCE
                .getRemoteSession(CryptoTokenManagementSessionRemote.class);
        AuthenticationToken authenticationToken = new TestAlwaysAllowLocalAuthenticationToken(TESTCLASSNAME);
        internalKeyBindingMgmtSession.deleteInternalKeyBinding(authenticationToken, internalKeyBindingId);
        cryptoTokenManagementSession.deleteCryptoToken(authenticationToken, cryptoTokenId);
        OcspTestUtils.deleteCa(authenticationToken, x509ca);
    }

    @Before
    public void setUp() throws Exception {
        caid = ISSUER_DN.hashCode();
        unknowncacert = (X509Certificate) CertTools.getCertfromByteArray(unknowncacertBytes);
    }

    @Test
    public void test01Access() throws Exception {
        super.test01Access();
    }

    /**
     * Tests ocsp message
     * 
     * @throws Exception
     *             error
     */
    @Test
    public void test02OcspGood() throws Exception {
        @SuppressWarnings("unused")
        String subjectDnCA = CertTools.getSubjectDN(unknowncacert);
        // And an OCSP request
        OCSPReqBuilder gen = new OCSPReqBuilder();
        final X509Certificate ocspTestCert = getActiveTestCert();
        gen.addRequest(new JcaCertificateID(SHA1DigestCalculator.buildSha1Instance(), getCaCert(ocspTestCert),
                ocspTestCert.getSerialNumber()));
        OCSPReq req = gen.build();
        helper.reloadKeys();
        // Send the request and receive a singleResponse
        SingleResp[] singleResps = helper.sendOCSPPost(req.getEncoded(), null, 0, 200);
        assertEquals("No of SingResps should be 1.", 1, singleResps.length);
        SingleResp singleResp = singleResps[0];

        CertificateID certId = singleResp.getCertID();
        assertEquals("Serno in response does not match serno in request.", certId.getSerialNumber(),
                ocspTestCert.getSerialNumber());
        Object status = singleResp.getCertStatus();
        assertEquals("Status is not null (good)", null, status);
    }

    /**
     * Tests ocsp message
     * 
     * @throws Exception
     *             error
     */
    @Test
    public void test03OcspRevoked() throws Exception {
        ocspResponseGeneratorTestSession.reloadOcspSigningCache();
        final X509Certificate ocspTestCert = getRevokedTestCert();
        // And an OCSP request
        OCSPReqBuilder gen = new OCSPReqBuilder();
        gen.addRequest(new JcaCertificateID(SHA1DigestCalculator.buildSha1Instance(), getCaCert(ocspTestCert),
                ocspTestCert.getSerialNumber()));
        OCSPReq req = gen.build();

        // Send the request and receive a singleResponse
        SingleResp[] singleResps = helper.sendOCSPPost(req.getEncoded(), null, 0, 200);
        assertEquals("No of SingResps should be 1.", 1, singleResps.length);
        SingleResp singleResp = singleResps[0];

        CertificateID certId = singleResp.getCertID();
        assertEquals("Serno in response does not match serno in request.", certId.getSerialNumber(),
                ocspTestCert.getSerialNumber());
        Object status = singleResp.getCertStatus();
        assertTrue("Status (" + status + ") is not RevokedStatus", status instanceof RevokedStatus);
        RevokedStatus rev = (RevokedStatus) status;
        assertTrue("Status does not have reason", rev.hasRevocationReason());
    }

    @Test
    public void test04OcspUnknown() throws Exception {
        loadUserCert(caid);
        super.test04OcspUnknown();
    }

    @Test
    public void test05OcspUnknownCA() throws Exception {
        final String issuerDN = CertTools.getIssuerDN(ocspSigningCertificate);
        GlobalOcspConfiguration ocspConfiguration = (GlobalOcspConfiguration) globalConfigurationSession
                .getCachedConfiguration(GlobalOcspConfiguration.OCSP_CONFIGURATION_ID);
        ocspConfiguration.setOcspDefaultResponderReference(issuerDN);
        globalConfigurationSession.saveConfiguration(authenticationToken, ocspConfiguration);
        ocspResponseGeneratorTestSession.reloadOcspSigningCache();
        super.test05OcspUnknownCA();
        // Reverted issuer DN should work as well, we are independent of the order here
        final String revertedIssuerDN = CertTools.reverseDN(issuerDN);
        assertNotEquals("Reverting DN should produce a different result.", issuerDN, revertedIssuerDN);
        ocspConfiguration.setOcspDefaultResponderReference(revertedIssuerDN);
        globalConfigurationSession.saveConfiguration(authenticationToken, ocspConfiguration);
        ocspResponseGeneratorTestSession.reloadOcspSigningCache();
        super.test05OcspUnknownCA();
        ocspConfiguration.setOcspDefaultResponderReference("CN=error");
        globalConfigurationSession.saveConfiguration(authenticationToken, ocspConfiguration);
        ocspResponseGeneratorTestSession.reloadOcspSigningCache();
        testOcspUnauthorized();
    }

    private void testOcspUnauthorized() throws Exception { // NOPMD, this is not a test class itself
        log.trace(">testocspInternalError()");
        loadUserCert(this.caid);
        // An OCSP request for a certificate from an unknwon CA
        this.helper.verifyResponseUnauthorized(this.caid, this.unknowncacert, new BigInteger("1"));
        log.trace("<testocspInternalError()");
    }

    @Test
    public void test06OcspSendWrongContentType() throws Exception {
        super.test06OcspSendWrongContentType();
    }

    @Test
    public void test10MultipleRequests() throws Exception {
        super.test10MultipleRequests();
    }

    @Test
    public void test11MalformedRequest() throws Exception {
        super.test11MalformedRequest();
    }

    @Test
    public void test12CorruptRequests() throws Exception {
        super.test12CorruptRequests();
    }

    /**
     * Just verify that a both escaped and non-encoded GET requests work.
     */
    @Test
    public void test13GetRequests() throws Exception {
        super.test13GetRequests();
        // See if the OCSP Servlet can also read escaped requests
        final String urlEncReq = httpReqPath + '/' + resourceOcsp + '/'
                + "MGwwajBFMEMwQTAJBgUrDgMCGgUABBRBRfilzPB%2BAevx0i1AoeKTkrHgLgQUFJw5gwk9BaEgsX3pzsRF9iso29ICCCzdx5N0v9XwoiEwHzAdBgkrBgEFBQcwAQIEECrZswo%2Fa7YW%2Bhyi5Sn85fs%3D";
        URL url = new URL(urlEncReq);
        log.info(url.toString()); // Dump the exact string we use for access
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        assertEquals(
                "Response code did not match. (Make sure you allow encoded slashes in your appserver.. add -Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true in Tomcat)",
                200, con.getResponseCode());
        assertNotNull(con.getContentType());
        assertTrue(con.getContentType().startsWith("application/ocsp-response"));
        OCSPResp response = new OCSPResp(IOUtils.toByteArray(con.getInputStream()));
        assertNotNull("Response should not be null.", response);
        assertTrue("Should not be concidered malformed.",
                OCSPRespBuilder.MALFORMED_REQUEST != response.getStatus());
        final String dubbleSlashEncReq = httpReqPath + '/' + resourceOcsp + '/'
                + "MGwwajBFMEMwQTAJBgUrDgMCGgUABBRBRfilzPB%2BAevx0i1AoeKTkrHgLgQUFJw5gwk9BaEgsX3pzsRF9iso29ICCAvB%2F%2FHJyKqpoiEwHzAdBgkrBgEFBQcwAQIEEOTzT2gv3JpVva22Vj8cuKo%3D";
        url = new URL(dubbleSlashEncReq);
        log.info(url.toString()); // Dump the exact string we use for access
        con = (HttpURLConnection) url.openConnection();
        assertEquals("Response code did not match. ", 200, con.getResponseCode());
        assertNotNull(con.getContentType());
        assertTrue(con.getContentType().startsWith("application/ocsp-response"));
        response = new OCSPResp(IOUtils.toByteArray(con.getInputStream()));
        assertNotNull("Response should not be null.", response);
        assertTrue("Should not be concidered malformed.",
                OCSPRespBuilder.MALFORMED_REQUEST != response.getStatus());
    }

    @Test
    public void test14CorruptGetRequests() throws Exception {
        super.test14CorruptGetRequests();
    }

    @Test
    public void test15MultipleGetRequests() throws Exception {
        super.test15MultipleGetRequests();
    }

    /** Verify the headers of a successful GET request with untilNextUpdate and maxAge. */
    @Test
    public void test17VerifyHttpGetHeaders() throws Exception {
        final String oldConfigurationValue1 = configurationSession
                .getConfigurationValue(OcspConfiguration.UNTIL_NEXT_UPDATE);
        final String oldConfigurationValue2 = configurationSession.getConfigurationValue(OcspConfiguration.MAX_AGE);
        configurationSession.setConfigurationValue(OcspConfiguration.UNTIL_NEXT_UPDATE, "5");
        configurationSession.setConfigurationValue(OcspConfiguration.MAX_AGE, "30");
        // Make sure that we run the test with a CA where this is no OcspKeyBinding
        OcspTestUtils.setInternalKeyBindingStatus(authenticationToken, internalKeyBindingId,
                InternalKeyBindingStatus.DISABLED);
        ocspResponseGeneratorTestSession.reloadOcspSigningCache();
        try {
            testVerifyHttpGetHeaders(caCertificate, ocspSigningCertificate.getSerialNumber());
        } finally {
            configurationSession.setConfigurationValue(OcspConfiguration.UNTIL_NEXT_UPDATE, oldConfigurationValue1);
            configurationSession.setConfigurationValue(OcspConfiguration.MAX_AGE, oldConfigurationValue2);
            OcspTestUtils.setInternalKeyBindingStatus(authenticationToken, internalKeyBindingId,
                    InternalKeyBindingStatus.ACTIVE);
        }
    }

    @Test
    public void test17VerifyHttpGetHeadersOcspKeyBinding() throws Exception {
        final long oldValue1 = OcspTestUtils.setOcspKeyBindingUntilNextUpdate(authenticationToken,
                internalKeyBindingId, 5L);
        final long oldValue2 = OcspTestUtils.setOcspKeyBindingMaxAge(authenticationToken, internalKeyBindingId,
                30L);
        try {
            ocspResponseGeneratorTestSession.reloadOcspSigningCache();
            testVerifyHttpGetHeaders(caCertificate, ocspSigningCertificate.getSerialNumber());
        } finally {
            OcspTestUtils.setOcspKeyBindingUntilNextUpdate(authenticationToken, internalKeyBindingId, oldValue1);
            OcspTestUtils.setOcspKeyBindingMaxAge(authenticationToken, internalKeyBindingId, oldValue2);
        }
    }

    private void testVerifyHttpGetHeaders(X509Certificate caCertificate, BigInteger serialNumber) throws Exception {
        // An OCSP request, ocspTestCert is already created in earlier tests
        OCSPReqBuilder gen = new OCSPReqBuilder();
        gen.addRequest(new JcaCertificateID(SHA1DigestCalculator.buildSha1Instance(), caCertificate, serialNumber));
        OCSPReq req = gen.build();
        String reqString = new String(Base64.encode(req.getEncoded(), false));
        URL url = new URL(httpReqPath + '/' + resourceOcsp + '/' + URLEncoder.encode(reqString, "UTF-8"));
        log.debug("OCSP Request: " + url.toExternalForm());
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        assertEquals(
                "Response code did not match. (Make sure you allow encoded slashes in your appserver.. add -Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true in Tomcat)",
                200, con.getResponseCode());
        // Some appserver (Weblogic) responds with
        // "application/ocsp-response; charset=UTF-8"
        assertNotNull(con.getContentType());
        assertTrue(con.getContentType().startsWith("application/ocsp-response"));
        OCSPResp response = new OCSPResp(IOUtils.toByteArray(con.getInputStream()));
        assertEquals("Response status not the expected.", OCSPRespBuilder.SUCCESSFUL, response.getStatus());
        BasicOCSPResp brep = (BasicOCSPResp) response.getResponseObject();
        // Just output the headers to stdout so we can visually inspect them if
        // something goes wrong
        Set<String> keys = con.getHeaderFields().keySet();
        for (String field : keys) {
            List<String> values = con.getHeaderFields().get(field);
            for (String value : values) {
                log.info(field + ": " + value);
            }
        }
        String eTag = con.getHeaderField("ETag");
        assertNotNull(
                "RFC 5019 6.2: No 'ETag' HTTP header present as it SHOULD. (Make sure ocsp.untilNextUpdate and ocsp.maxAge are configured for this test)",
                eTag);
        assertTrue("ETag is messed up.",
                ("\"" + new String(
                        Hex.encode(MessageDigest.getInstance("SHA-1", "BC").digest(response.getEncoded()))) + "\"")
                                .equals(eTag));
        long date = con.getHeaderFieldDate("Date", -1);
        assertTrue("RFC 5019 6.2: No 'Date' HTTP header present as it SHOULD.", date != -1);
        long lastModified = con.getHeaderFieldDate("Last-Modified", -1);
        assertTrue("RFC 5019 6.2: No 'Last-Modified' HTTP header present as it SHOULD.", lastModified != -1);
        // assertTrue("Last-Modified is after response was sent",
        // lastModified<=date); This will not hold on JBoss AS due to the
        // caching of the Date-header
        long expires = con.getExpiration();
        assertTrue("Expires is before response was sent", expires >= date);
        assertTrue("RFC 5019 6.2: No 'Expires' HTTP header present as it SHOULD.", expires != 0);
        String cacheControl = con.getHeaderField("Cache-Control");
        assertNotNull("RFC 5019 6.2: No 'Cache-Control' HTTP header present as it SHOULD.", cacheControl);
        assertTrue("RFC 5019 6.2: No 'public' HTTP header Cache-Control present as it SHOULD.",
                cacheControl.contains("public"));
        assertTrue("RFC 5019 6.2: No 'no-transform' HTTP header Cache-Control present as it SHOULD.",
                cacheControl.contains("no-transform"));
        assertTrue("RFC 5019 6.2: No 'must-revalidate' HTTP header Cache-Control present as it SHOULD.",
                cacheControl.contains("must-revalidate"));
        Matcher matcher = Pattern.compile(".*max-age\\s*=\\s*(\\d+).*").matcher(cacheControl);
        assertTrue("RFC 5019 6.2: No 'max-age' HTTP header Cache-Control present as it SHOULD.", matcher.matches());
        int maxAge = Integer.parseInt(matcher.group(1));
        log.debug("maxAge=" + maxAge + " (expires-lastModified)/1000=" + ((expires - lastModified) / 1000));
        assertTrue(
                "thisUpdate and nextUpdate should not be the same (Make sure ocsp.untilNextUpdate and ocsp.maxAge are configured for this test)",
                expires != lastModified);
        assertTrue("RFC 5019 6.2: [maxAge] SHOULD be 'later than thisUpdate but earlier than nextUpdate'.",
                maxAge < (expires - lastModified) / 1000);
        // assertTrue("Response cannot be produced after it was sent.",
        // brep.getProducedAt().getTime() <= date); This might not hold on JBoss
        // AS due to the caching of the Date-header
        X509CertificateHolder[] chain = brep.getCerts();
        boolean verify = brep.isSignatureValid(new JcaContentVerifierProviderBuilder().build(chain[0]));
        assertTrue("Response failed to verify.", verify);
        assertNull("No nonce should be present.", brep.getExtension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce));
        SingleResp[] singleResps = brep.getResponses();
        assertNotNull("SingleResps should not be null.", singleResps);
        assertTrue("Expected a single SingleResp in the repsonse.", singleResps.length == 1);
        assertEquals("Serno in response does not match serno in request.",
                singleResps[0].getCertID().getSerialNumber(), serialNumber);
        assertEquals("Status is not null (null is 'good')", singleResps[0].getCertStatus(), null);
        assertTrue(
                "RFC 5019 6.2: Last-Modified SHOULD 'be the same as the thisUpdate timestamp in the request itself'",
                singleResps[0].getThisUpdate().getTime() == lastModified);
        assertTrue("RFC 5019 6.2: Expires SHOULD 'be the same as the nextUpdate timestamp in the request itself'",
                singleResps[0].getNextUpdate().getTime() == expires);
        assertTrue("Response cannot be produced before it was last modified..",
                brep.getProducedAt().getTime() >= singleResps[0].getThisUpdate().getTime());
    }

    /** Verify the headers of a successful GET request with untilNextUpdate and maxAge. */
    @Test
    public void test18NextUpdateThisUpdate() throws Exception {
        final String oldConfigurationValue1 = configurationSession
                .getConfigurationValue(OcspConfiguration.UNTIL_NEXT_UPDATE);
        final String oldConfigurationValue2 = configurationSession.getConfigurationValue(OcspConfiguration.MAX_AGE);
        configurationSession.setConfigurationValue(OcspConfiguration.UNTIL_NEXT_UPDATE, "5");
        configurationSession.setConfigurationValue(OcspConfiguration.MAX_AGE, "30");
        // Make sure that we run the test with a CA where this is no OcspKeyBinding
        OcspTestUtils.setInternalKeyBindingStatus(authenticationToken, internalKeyBindingId,
                InternalKeyBindingStatus.DISABLED);
        ocspResponseGeneratorTestSession.reloadOcspSigningCache();
        try {
            testNextUpdateThisUpdate(caCertificate, ocspSigningCertificate.getSerialNumber());
        } finally {
            configurationSession.setConfigurationValue(OcspConfiguration.UNTIL_NEXT_UPDATE, oldConfigurationValue1);
            configurationSession.setConfigurationValue(OcspConfiguration.MAX_AGE, oldConfigurationValue2);
            OcspTestUtils.setInternalKeyBindingStatus(authenticationToken, internalKeyBindingId,
                    InternalKeyBindingStatus.ACTIVE);
        }
    }

    @Test
    public void test18NextUpdateThisUpdateOcspKeyBinding() throws Exception {
        final long oldValue = OcspTestUtils.setOcspKeyBindingUntilNextUpdate(authenticationToken,
                internalKeyBindingId, 5L);
        try {
            ocspResponseGeneratorTestSession.reloadOcspSigningCache();
            testNextUpdateThisUpdate(caCertificate, ocspSigningCertificate.getSerialNumber());
        } finally {
            OcspTestUtils.setOcspKeyBindingUntilNextUpdate(authenticationToken, internalKeyBindingId, oldValue);
        }
    }

    private void testNextUpdateThisUpdate(X509Certificate caCertificate, BigInteger serialNumber) throws Exception {
        // And an OCSP request
        OCSPReqBuilder gen = new OCSPReqBuilder();
        gen.addRequest(new JcaCertificateID(SHA1DigestCalculator.buildSha1Instance(), caCertificate, serialNumber));
        OCSPReq req = gen.build();
        // POST the request and receive a singleResponse
        URL url = new URL(httpReqPath + '/' + resourceOcsp);
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        con.setDoOutput(true);
        con.setRequestMethod("POST");
        con.setRequestProperty("Content-Type", "application/ocsp-request");
        OutputStream os = con.getOutputStream();
        os.write(req.getEncoded());
        os.close();
        assertEquals("Response code", 200, con.getResponseCode());
        // Some appserver (Weblogic) responds with
        // "application/ocsp-response; charset=UTF-8"
        assertNotNull(con.getContentType());
        assertTrue(con.getContentType().startsWith("application/ocsp-response"));
        OCSPResp response = new OCSPResp(IOUtils.toByteArray(con.getInputStream()));
        assertEquals("Response status not the expected.", 0, response.getStatus());
        BasicOCSPResp brep = (BasicOCSPResp) response.getResponseObject();
        X509CertificateHolder[] chain = brep.getCerts();
        boolean verify = brep.isSignatureValid(new JcaContentVerifierProviderBuilder().build(chain[0]));
        assertTrue("Response failed to verify.", verify);
        SingleResp[] singleResps = brep.getResponses();
        assertEquals("No of SingResps should be 1.", 1, singleResps.length);
        CertificateID certId = singleResps[0].getCertID();
        assertEquals("Serno in response does not match serno in request.", certId.getSerialNumber(), serialNumber);
        assertNull("Status is not null.", singleResps[0].getCertStatus());
        Date thisUpdate = singleResps[0].getThisUpdate();
        Date nextUpdate = singleResps[0].getNextUpdate();
        Date producedAt = brep.getProducedAt();
        assertNotNull("thisUpdate was not set.", thisUpdate);
        assertNotNull("nextUpdate was not set. (This test requires ocsp.untilNextUpdate to be configured.)",
                nextUpdate);
        assertNotNull("producedAt was not set.", producedAt);
        assertTrue("nextUpdate cannot be before thisUpdate.", !nextUpdate.before(thisUpdate));
        assertTrue("producedAt cannot be before thisUpdate.", !producedAt.before(thisUpdate));
    }

    /**
     * Tests the ocsp.revoked.untilNextUpdate configuration.
     * 
     * The test sets ocsp.untilNextUpdate and ocsp.revoked.untilNextUpdate to different values and then verified that the response's next update value matches 
     * the setting of ocsp.revoked.untilNextUpdate and not ocsp.untilNextUpdate
     * 
     * @throws Exception
     *             error
     */
    @Test
    public void testRevokedNextUpdate() throws Exception {
        ocspResponseGeneratorTestSession.reloadOcspSigningCache();
        final X509Certificate ocspTestCert = getRevokedTestCert();

        final String oldConfigurationValue1 = configurationSession.getConfigurationValue(
                "ocsp." + CertificateProfileConstants.CERTPROFILE_FIXED_OCSPSIGNER + ".untilNextUpdate");
        final String oldConfigurationValue2 = configurationSession.getConfigurationValue(
                "ocsp." + CertificateProfileConstants.CERTPROFILE_FIXED_OCSPSIGNER + ".revoked.untilNextUpdate");
        configurationSession.setConfigurationValue(
                "ocsp." + CertificateProfileConstants.CERTPROFILE_FIXED_OCSPSIGNER + ".untilNextUpdate", "7");
        configurationSession.setConfigurationValue(
                "ocsp." + CertificateProfileConstants.CERTPROFILE_FIXED_OCSPSIGNER + ".revoked.untilNextUpdate",
                "5");
        // Make sure that we run the test with a CA where this is no OcspKeyBinding
        OcspTestUtils.setInternalKeyBindingStatus(authenticationToken, internalKeyBindingId,
                InternalKeyBindingStatus.DISABLED);
        ocspResponseGeneratorTestSession.reloadOcspSigningCache();
        try {

            // And an OCSP request
            OCSPReqBuilder gen = new OCSPReqBuilder();
            gen.addRequest(new JcaCertificateID(SHA1DigestCalculator.buildSha1Instance(), getCaCert(ocspTestCert),
                    ocspTestCert.getSerialNumber()));
            OCSPReq req = gen.build();

            // Send the request and receive a singleResponse
            SingleResp[] singleResps = helper.sendOCSPPost(req.getEncoded(), null, 0, 200);
            assertEquals("No of SingResps should be 1.", 1, singleResps.length);
            SingleResp singleResp = singleResps[0];

            CertificateID certId = singleResp.getCertID();
            assertEquals("Serno in response does not match serno in request.", certId.getSerialNumber(),
                    ocspTestCert.getSerialNumber());
            Object status = singleResp.getCertStatus();
            assertTrue("Status (" + status + ") is not RevokedStatus", status instanceof RevokedStatus);
            RevokedStatus rev = (RevokedStatus) status;
            assertTrue("Status does not have reason", rev.hasRevocationReason());

            Date thisUpdate = singleResp.getThisUpdate();
            Date nextUpdate = singleResp.getNextUpdate();
            assertNotNull("thisUpdate was not set.", thisUpdate);
            assertNotNull(
                    "nextUpdate was not set. (This test requires ocsp.revoked.untilNextUpdate to be configured.)",
                    nextUpdate);

            long diff = nextUpdate.getTime() - thisUpdate.getTime();
            assertEquals("The nextUpdate value was not taken from ocsp.revoked.untilNextUpdate", 5000L, diff);

        } finally {
            configurationSession.setConfigurationValue(
                    "ocsp." + CertificateProfileConstants.CERTPROFILE_FIXED_OCSPSIGNER + ".untilNextUpdate",
                    oldConfigurationValue1);
            configurationSession.setConfigurationValue(
                    "ocsp." + CertificateProfileConstants.CERTPROFILE_FIXED_OCSPSIGNER + ".revoked.untilNextUpdate",
                    oldConfigurationValue2);
            OcspTestUtils.setInternalKeyBindingStatus(authenticationToken, internalKeyBindingId,
                    InternalKeyBindingStatus.ACTIVE);
        }

    }

    @Test
    public void testKeyRenewal() throws Exception {
        //Add localhost to list of rekeying triggering hosts.
        Set<String> originalHosts = OcspConfiguration.getRekeyingTriggingHosts();
        String originalRekeyingPassword = OcspConfiguration.getRekeyingTriggingPassword();
        configurationSession.setConfigurationValue(OcspConfiguration.REKEYING_TRIGGERING_HOSTS, "127.0.0.1");
        configurationSession.setConfigurationValue(OcspConfiguration.REKEYING_TRIGGERING_PASSWORD, "foo123");
        ocspResponseGeneratorTestSession.reloadOcspSigningCache();
        List<X509Certificate> oldValues = ocspResponseGeneratorTestSession.getCacheOcspCertificates();
        try {
            X509Certificate cert = getActiveTestCert();
            X509Certificate caCertificate = getCaCert(cert);
            helper.renewAllKeys();
            ocspResponseGeneratorTestSession.reloadOcspSigningCache();
            List<X509Certificate> newValues = ocspResponseGeneratorTestSession.getCacheOcspCertificates();
            //Make sure that cache contains one and only one value
            assertEquals(
                    "Cache contains a different amount of values after rekeying than before. This indicates a test failure",
                    oldValues.size(), newValues.size());
            //Make check that the certificate has changed (sanity check)
            X509Certificate newSigningCertificate = null;
            for (X509Certificate signingCertificate : newValues) {
                if (CertTools.getIssuerDN(signingCertificate).equals(CertTools.getSubjectDN(caCertificate))) {
                    newSigningCertificate = signingCertificate;
                    break;
                }
            }
            assertNotEquals("The same certificate was returned after the renewal process. Key renewal failed",
                    cert.getSerialNumber(), newSigningCertificate.getSerialNumber());
            //Make sure that the new certificate is signed by the CA certificate
            try {
                newSigningCertificate.verify(caCertificate.getPublicKey());
            } catch (SignatureException e) {
                log.error("Exception caught", e);
                fail("The new signing certificate was not signed correctly.");
            }

        } finally {
            StringBuilder originalHostsString = new StringBuilder();
            for (String host : originalHosts.toArray(new String[originalHosts.size()])) {
                originalHostsString.append(host + ";");
            }
            configurationSession.setConfigurationValue(OcspConfiguration.REKEYING_TRIGGERING_HOSTS,
                    originalHostsString.toString());
            configurationSession.setConfigurationValue(OcspConfiguration.REKEYING_TRIGGERING_PASSWORD,
                    originalRekeyingPassword);
        }
    }

    public static X509Certificate createCaCertificate(AuthenticationToken authenticationToken,
            Certificate certificate) throws CreateException, AuthorizationDeniedException {
        final CertificateStoreSessionRemote certificateStoreSession = EjbRemoteHelper.INSTANCE
                .getRemoteSession(CertificateStoreSessionRemote.class);
        X509Certificate caCertificate = (X509Certificate) certificate;
        //Store the CA Certificate.
        certificateStoreSession.storeCertificate(authenticationToken, caCertificate, "foo", "1234",
                CertificateConstants.CERT_ACTIVE, CertificateConstants.CERTTYPE_ROOTCA,
                CertificateProfileConstants.CERTPROFILE_FIXED_ROOTCA, "footag", new Date().getTime());
        return caCertificate;
    }

}