Package org.ejbca.core.protocol.ocsp

Source Code of org.ejbca.core.protocol.ocsp.ProtocolOcspHttpStandaloneTest

/*************************************************************************
*                                                                       *
*  EJBCA: 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 java.io.ByteArrayInputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import junit.framework.TestSuite;

import org.apache.log4j.Logger;
import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
import org.bouncycastle.ocsp.BasicOCSPResp;
import org.bouncycastle.ocsp.CertificateID;
import org.bouncycastle.ocsp.OCSPReq;
import org.bouncycastle.ocsp.OCSPReqGenerator;
import org.bouncycastle.ocsp.OCSPResp;
import org.bouncycastle.ocsp.OCSPRespGenerator;
import org.bouncycastle.ocsp.RevokedStatus;
import org.bouncycastle.ocsp.SingleResp;
import org.bouncycastle.util.encoders.Hex;
import org.ejbca.core.ejb.JndiHelper;
import org.ejbca.core.ejb.ca.store.CertificateStatus;
import org.ejbca.core.ejb.ca.store.CertificateStoreSessionRemote;
import org.ejbca.core.model.SecConst;
import org.ejbca.util.Base64;
import org.ejbca.util.CertTools;

/**
* Tests HTTP pages of a stand-alone OCSP To run this test you must create a
* user named ocspTest that has at least two certificates and at least one of
* them must be revoked.
*
* Change the address 127.0.0.1 to where you stand-alone OCSP server is running.
* Change myCaId to the CA that ocspTest belongs to
**/
public class ProtocolOcspHttpStandaloneTest extends ProtocolOcspHttpTest {

    private static final Logger log = Logger.getLogger(ProtocolOcspHttpStandaloneTest.class);
    private static final String issuerDN = "CN=AdminCA1,O=EJBCA Sample,C=SE";

    private static final int myCaId = issuerDN.hashCode();
    //private static final String myOcspIp = "127.0.0.1";  TODO: Refactor back to where we could use this test remotely?

    private CertificateStoreSessionRemote certificateStoreOnlyDataSession = JndiHelper.getRemoteSession(CertificateStoreSessionRemote.class)// Stand alone OCSP version..

    public ProtocolOcspHttpStandaloneTest(String name) throws Exception {
        //super(name, "http://" + myOcspIp + ":8080/ejbca", "publicweb/status/ocsp");
      super(name, "8080", "publicweb/status/ocsp");
        caid = myCaId;
    }
   
    public static TestSuite suite() {
        // Only include "test*"-methods from this class, not from the parent
        // class
        TestSuite ret = new TestSuite();
        try {
            Method[] methods = ProtocolOcspHttpStandaloneTest.class.getDeclaredMethods();
            for (int i = 0; i < methods.length; i++) {
                String name = methods[i].getName();
                if (name.startsWith("test")) {
                    ret.addTest(new ProtocolOcspHttpStandaloneTest(name));
                }
            }
        } catch (Exception e) {
            log.error("", e);
        }
        return ret;
    }
   
    // Required to override check in baseclass
    public void setUp() throws Exception {
    }

    // Required to override check in baseclass
    public void tearDown() throws Exception {
    }

    protected void loadUserCert(int caid) throws Exception {
        ocspTestCert = getTestCert(false);
        cacert = getCaCert(ocspTestCert);
    }

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

    /**
     * Tests ocsp message
     *
     * @throws Exception
     *             error
     */
    public void test02OcspGood() throws Exception {
        log.trace(">test02OcspGood()");

        // And an OCSP request
        OCSPReqGenerator gen = new OCSPReqGenerator();
        final X509Certificate ocspTestCert = getTestCert(false);
        gen.addRequest(new CertificateID(CertificateID.HASH_SHA1, getCaCert(ocspTestCert), ocspTestCert.getSerialNumber()));
        OCSPReq req = gen.generate();

        // 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);
        log.trace("<test02OcspGood()");
    }

    /**
     * Tests ocsp message
     *
     * @throws Exception
     *             error
     */
    public void test03OcspRevoked() throws Exception {
        log.trace(">test03OcspRevoked()");
        final X509Certificate ocspTestCert = getTestCert(true);
        // And an OCSP request
        OCSPReqGenerator gen = new OCSPReqGenerator();
        gen.addRequest(new CertificateID(CertificateID.HASH_SHA1, getCaCert(ocspTestCert), ocspTestCert.getSerialNumber()));
        OCSPReq req = gen.generate();

        // 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 is not RevokedStatus", status instanceof RevokedStatus);
        RevokedStatus rev = (RevokedStatus) status;
        assertTrue("Status does not have reason", rev.hasRevocationReason());
        log.trace("<test03OcspRevoked()");
    }

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

    public void test05OcspUnknownCA() throws Exception {
        super.test05OcspUnknownCA();
    }

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

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

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

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

    /**
     * Just verify that a both escaped and non-encoded GET requests work.
     */
    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(new ByteArrayInputStream(OcspJunitHelper.inputStreamToBytes(con.getInputStream())));
        assertNotNull("Response should not be null.", response);
        assertTrue("Should not be concidered malformed.", OCSPRespGenerator.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(new ByteArrayInputStream(OcspJunitHelper.inputStreamToBytes(con.getInputStream())));
        assertNotNull("Response should not be null.", response);
        assertTrue("Should not be concidered malformed.", OCSPRespGenerator.MALFORMED_REQUEST != response.getStatus());
    }

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

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

    /**
     * Verify the headers of a successful GET request. ocsp.untilNextUpdate has
     * to be configured for this test.
     */
    public void test17VerifyHttpGetHeaders() throws Exception {
        final X509Certificate ocspTestCert = getTestCert(false);
        // An OCSP request, ocspTestCert is already created in earlier tests
        OCSPReqGenerator gen = new OCSPReqGenerator();
        gen.addRequest(new CertificateID(CertificateID.HASH_SHA1, cacert, ocspTestCert.getSerialNumber()));
        OCSPReq req = gen.generate();
        String reqString = new String(Base64.encode(req.getEncoded(), false));
        URL url = new URL(httpReqPath + '/' + resourceOcsp + '/' + URLEncoder.encode(reqString, "UTF-8"));
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        assertEquals("Response code did not match. ", 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(new ByteArrayInputStream(OcspJunitHelper.inputStreamToBytes(con.getInputStream())));
        assertEquals("Response status not the expected.", OCSPRespGenerator.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 is 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));
        assertTrue("RFC 5019 6.2: 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
        X509Certificate[] chain = brep.getCerts("BC");
        boolean verify = brep.verify(chain[0].getPublicKey(), "BC");
        assertTrue("Response failed to verify.", verify);
        assertNull("No nonce should be present.", brep.getExtensionValue(OCSPObjectIdentifiers.id_pkix_ocsp_nonce.getId()));
        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(), ocspTestCert.getSerialNumber());
        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());
    }

    /**
     * Tests nextUpdate and thisUpdate ocsp.untilNextUpdate has to be configured
     * for this test.
     */
    public void test18NextUpdateThisUpdate() throws Exception {
        final X509Certificate ocspTestCert = getTestCert(false);
        // And an OCSP request
        OCSPReqGenerator gen = new OCSPReqGenerator();
        gen.addRequest(new CertificateID(CertificateID.HASH_SHA1, cacert, ocspTestCert.getSerialNumber()));
        OCSPReq req = gen.generate();
        // 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(new ByteArrayInputStream(OcspJunitHelper.inputStreamToBytes(con.getInputStream())));
        assertEquals("Response status not the expected.", 0, response.getStatus());
        BasicOCSPResp brep = (BasicOCSPResp) response.getResponseObject();
        X509Certificate[] chain = brep.getCerts("BC");
        boolean verify = brep.verify(chain[0].getPublicKey(), "BC");
        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(), ocspTestCert.getSerialNumber());
        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));
    }

    private X509Certificate getTestCert(boolean isRevoked) throws Exception {
        try {
            Collection<Certificate> certs = certificateStoreOnlyDataSession.findCertificatesByUsername(admin, "ocspTest");
            Iterator<Certificate> i = certs.iterator();
            while (i.hasNext()) {
                X509Certificate cert = (X509Certificate) i.next();
                CertificateStatus cs = certificateStoreOnlyDataSession.getStatus(issuerDN, CertTools.getSerialNumber(cert));
                if (isRevoked == cs.equals(CertificateStatus.REVOKED)) {
                    return cert;
                }
            }
        } catch (Throwable e) {
            log.debug("", e);
        }
        assertNotNull("To run this test you must have at least one active and one revoked end user cert in the database. (Could not fetch certificate.)", null);
        return null;
    }

    private X509Certificate getCaCert(X509Certificate cert) throws Exception {
        Collection<Certificate> certs = certificateStoreOnlyDataSession.findCertificatesByType(admin, SecConst.CERTTYPE_ROOTCA, CertTools.getIssuerDN(cert));
        assertTrue("Could not determine or find the CA cert.", certs != null && !certs.isEmpty());
        return (X509Certificate) certs.iterator().next();
    }

}
TOP

Related Classes of org.ejbca.core.protocol.ocsp.ProtocolOcspHttpStandaloneTest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.