org.ejbca.ui.web.pub.CertRequestHttpTest.java Source code

Java tutorial

Introduction

Here is the source code for org.ejbca.ui.web.pub.CertRequestHttpTest.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.ui.web.pub;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.net.URLEncoder;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.apache.xml.security.utils.Base64;
import org.bouncycastle.asn1.DEROutputStream;
import org.bouncycastle.asn1.DERSet;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.cesecore.SystemTestsConfiguration;
import org.cesecore.authentication.tokens.AuthenticationToken;
import org.cesecore.authentication.tokens.UsernamePrincipal;
import org.cesecore.certificates.certificateprofile.CertificateProfileConstants;
import org.cesecore.certificates.endentity.EndEntityConstants;
import org.cesecore.certificates.endentity.EndEntityInformation;
import org.cesecore.certificates.endentity.EndEntityTypes;
import org.cesecore.keys.util.KeyTools;
import org.cesecore.mock.authentication.tokens.TestAlwaysAllowLocalAuthenticationToken;
import org.cesecore.util.CertTools;
import org.cesecore.util.CryptoProviderTools;
import org.cesecore.util.EjbRemoteHelper;
import org.ejbca.config.WebConfiguration;
import org.ejbca.core.ejb.ca.CaTestCase;
import org.ejbca.core.ejb.config.ConfigurationSessionRemote;
import org.ejbca.core.ejb.ra.EndEntityAccessSessionRemote;
import org.ejbca.core.ejb.ra.EndEntityExistsException;
import org.ejbca.core.ejb.ra.EndEntityManagementSessionRemote;
import org.ejbca.core.model.InternalEjbcaResources;
import org.ejbca.core.model.SecConst;
import org.ejbca.core.model.ra.NotFoundException;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

/**
 * Tests http servlet for certificate request
 * 
 * @version $Id: CertRequestHttpTest.java 19902 2014-09-30 14:32:24Z anatom $
 */
public class CertRequestHttpTest extends CaTestCase {
    private static Logger log = Logger.getLogger(CertRequestHttpTest.class);

    private static final String TEST_USERNAME = "reqtest";
    private String httpReqPath;
    private String resourceReq;

    private int caid = getTestCAId();
    private static final AuthenticationToken admin = new TestAlwaysAllowLocalAuthenticationToken(
            new UsernamePrincipal("CertRequestHttpTest"));

    private final ConfigurationSessionRemote configurationSession = EjbRemoteHelper.INSTANCE
            .getRemoteSession(ConfigurationSessionRemote.class, EjbRemoteHelper.MODULE_TEST);
    private final EndEntityManagementSessionRemote endEntityManagementSession = EjbRemoteHelper.INSTANCE
            .getRemoteSession(EndEntityManagementSessionRemote.class);
    private final EndEntityAccessSessionRemote endEntityAccessSession = EjbRemoteHelper.INSTANCE
            .getRemoteSession(EndEntityAccessSessionRemote.class);

    @BeforeClass
    public static void beforeClass() {
        // Install BouncyCastle provider
        CryptoProviderTools.installBCProvider();

    }

    @Before
    public void setUp() throws Exception {
        super.setUp();
        final String remoteHost = SystemTestsConfiguration.getRemoteHost("127.0.0.1");
        final String remotePort = SystemTestsConfiguration
                .getRemotePortHttp(configurationSession.getProperty(WebConfiguration.CONFIG_HTTPSERVERPUBHTTP));
        httpReqPath = "http://" + remoteHost + ":" + remotePort + "/ejbca";
        resourceReq = "certreq";
    }

    @After
    public void tearDown() throws Exception {
        super.tearDown();
        try {
            endEntityManagementSession.deleteUser(admin, TEST_USERNAME);
        } catch (NotFoundException e) {
            // NOPMD:ignore if the user was not created
        }
    }

    /**
     * Tests request for a pkcs12
     * 
     * @throws Exception error
     */
    @Test
    public void test01RequestPKCS12() throws Exception {
        log.trace(">test01RequestPKCS12()");

        // find a CA (TestCA?) create a user
        // Send certificate request for a server generated PKCS12
        setupUser(SecConst.TOKEN_SOFT_P12);
        setupUserStatus(EndEntityConstants.STATUS_NEW);

        // POST the OCSP request
        URL url = new URL(httpReqPath + '/' + resourceReq);
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        // we are going to do a POST
        con.setDoOutput(true);
        con.setRequestMethod("POST");

        // POST it
        con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        OutputStream os = con.getOutputStream();
        os.write(("user=" + TEST_USERNAME + "&password=foo123&keylength=2048").getBytes("UTF-8"));
        os.close();
        assertEquals("Response code", 200, con.getResponseCode());
        // Some appserver (Weblogic) responds with
        // "application/x-pkcs12; charset=UTF-8"
        String contentType = con.getContentType();
        boolean contentTypeIsPkcs12 = contentType.startsWith("application/x-pkcs12");
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        // This works for small requests, and PKCS12 requests are small
        InputStream in = con.getInputStream();
        int b = in.read();
        while (b != -1) {
            baos.write(b);
            b = in.read();
        }
        baos.flush();
        in.close();
        byte[] respBytes = baos.toByteArray();
        assertTrue(respBytes.length > 0);
        if (!contentTypeIsPkcs12 && log.isDebugEnabled()) {
            // If the content-type isn't application/x-pkcs12 we like to know what we got back..
            log.debug(new String(respBytes));
        }
        assertTrue("contentType was " + contentType, contentTypeIsPkcs12);

        KeyStore store = KeyStore.getInstance("PKCS12", "BC");
        ByteArrayInputStream is = new ByteArrayInputStream(respBytes);
        store.load(is, "foo123".toCharArray());
        assertTrue(store.containsAlias("ReqTest"));
        X509Certificate cert = (X509Certificate) store.getCertificate("ReqTest");
        PublicKey pk = cert.getPublicKey();
        if (pk instanceof RSAPublicKey) {
            RSAPublicKey rsapk = (RSAPublicKey) pk;
            assertEquals(rsapk.getAlgorithm(), "RSA");
            assertEquals(2048, rsapk.getModulus().bitLength());
        } else {
            assertTrue("Public key is not RSA", false);
        }

        log.trace("<test01RequestPKCS12()");
    }

    /**
     * Tests request for a unknown user
     * 
     * @throws Exception error
     */
    @Test
    public void test02RequestUnknownUser() throws Exception {
        log.trace(">test02RequestUnknownUser()");

        // POST the OCSP request
        URL url = new URL(httpReqPath + '/' + resourceReq);
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        // we are going to do a POST
        con.setDoOutput(true);
        con.setRequestMethod("POST");
        con.setInstanceFollowRedirects(false);
        con.setAllowUserInteraction(false);

        // POST it
        con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        OutputStream os = con.getOutputStream();
        os.write("user=reqtestunknown&password=foo123&keylength=2048".getBytes("UTF-8"));
        os.close();
        final int responseCode = con.getResponseCode();
        if (responseCode != HttpURLConnection.HTTP_OK) {
            log.info("ResponseMessage: " + con.getResponseMessage());
            assertEquals("Response code", HttpURLConnection.HTTP_OK, responseCode);
        }
        log.info("Content-Type: " + con.getContentType());
        boolean ok = false;
        // Some containers return the content type with a space and some
        // without...
        if ("text/html;charset=UTF-8".equals(con.getContentType())) {
            ok = true;
        }
        if ("text/html; charset=UTF-8".equals(con.getContentType())) {
            ok = true;
        }
        assertTrue(con.getContentType(), ok);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        // This works for small requests, and PKCS12 requests are small
        InputStream in = con.getInputStream();
        int b = in.read();
        while (b != -1) {
            baos.write(b);
            b = in.read();
        }
        baos.flush();
        in.close();
        byte[] respBytes = baos.toByteArray();
        String error = new String(respBytes);
        int index = error.indexOf("<pre>");
        int index2 = error.indexOf("</pre>");
        String errormsg = error.substring(index + 5, index2);
        log.info(errormsg);
        String expectedErrormsg = "Username: reqtestunknown\nNon existent username. To generate a certificate a valid username and password must be supplied.\n";
        assertEquals(expectedErrormsg.replaceAll("\\s", ""), errormsg.replaceAll("\\s", ""));
        log.trace("<test02RequestUnknownUser()");
    }

    /**
     * Tests request for a wrong password
     * 
     * @throws Exception error
     */
    @Test
    public void test03RequestWrongPwd() throws Exception {
        log.trace(">test03RequestWrongPwd()");

        setupUser(SecConst.TOKEN_SOFT_P12);

        // POST the OCSP request
        URL url = new URL(httpReqPath + '/' + resourceReq);
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        // we are going to do a POST
        con.setDoOutput(true);
        con.setRequestMethod("POST");
        con.setInstanceFollowRedirects(false);
        con.setAllowUserInteraction(false);

        // POST it
        con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        OutputStream os = con.getOutputStream();
        os.write(("user=" + TEST_USERNAME + "&password=foo456&keylength=2048").getBytes("UTF-8"));
        os.close();
        final int responseCode = con.getResponseCode();
        if (responseCode != HttpURLConnection.HTTP_OK) {
            log.info("ResponseMessage: " + con.getResponseMessage());
            assertEquals("Response code", HttpURLConnection.HTTP_OK, responseCode);
        }
        boolean ok = false;
        // Some containers return the content type with a space and some
        // without...
        if ("text/html;charset=UTF-8".equals(con.getContentType())) {
            ok = true;
        }
        if ("text/html; charset=UTF-8".equals(con.getContentType())) {
            ok = true;
        }
        assertTrue(con.getContentType(), ok);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        // This works for small requests, and PKCS12 requests are small
        InputStream in = con.getInputStream();
        int b = in.read();
        while (b != -1) {
            baos.write(b);
            b = in.read();
        }
        baos.flush();
        in.close();
        byte[] respBytes = baos.toByteArray();
        String error = new String(respBytes);
        int index = error.indexOf("<pre>");
        int index2 = error.indexOf("</pre>");
        String errormsg = error.substring(index + 5, index2);
        String expectedErrormsg = "Username: " + TEST_USERNAME + "\nWrong username or password";
        assertEquals(expectedErrormsg.replaceAll("\\s", ""), errormsg.replaceAll("\\s", ""));
        log.info(errormsg);
        log.trace("<test03RequestWrongPwd()");
    }

    /**
     * Tests request with wrong status
     * 
     * @throws Exception error
     */
    @Test
    public void test04RequestWrongStatus() throws Exception {
        log.trace(">test04RequestWrongStatus()");

        setupUser(SecConst.TOKEN_SOFT_P12);
        setupUserStatus(EndEntityConstants.STATUS_GENERATED);

        // POST the OCSP request
        URL url = new URL(httpReqPath + '/' + resourceReq);
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        // we are going to do a POST
        con.setDoOutput(true);
        con.setRequestMethod("POST");
        con.setInstanceFollowRedirects(false);
        con.setAllowUserInteraction(false);

        // POST it
        con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        OutputStream os = con.getOutputStream();
        os.write(("user=" + TEST_USERNAME + "&password=foo456&keylength=2048").getBytes("UTF-8"));
        os.close();
        final int responseCode = con.getResponseCode();
        if (responseCode != HttpURLConnection.HTTP_OK) {
            log.info("ResponseMessage: " + con.getResponseMessage());
            assertEquals("Response code", HttpURLConnection.HTTP_OK, responseCode);
        }
        boolean ok = false;
        // Some containers return the content type with a space and some
        // without...
        if ("text/html;charset=UTF-8".equals(con.getContentType())) {
            ok = true;
        }
        if ("text/html; charset=UTF-8".equals(con.getContentType())) {
            ok = true;
        }
        assertTrue(con.getContentType(), ok);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        // This works for small requests, and PKCS12 requests are small
        InputStream in = con.getInputStream();
        int b = in.read();
        while (b != -1) {
            baos.write(b);
            b = in.read();
        }
        baos.flush();
        in.close();
        byte[] respBytes = baos.toByteArray();
        String error = new String(respBytes);
        int index = error.indexOf("<pre>");
        int index2 = error.indexOf("</pre>");
        String errormsg = error.substring(index + 5, index2);
        String expectedErrormsg = "Username: " + TEST_USERNAME
                + "\nWrong user status! To generate a certificate for a user the user must have status New, Failed or In process.\n";

        assertEquals(expectedErrormsg.replaceAll("\\s", ""), errormsg.replaceAll("\\s", ""));
        log.info(errormsg);
        log.trace("<test04RequestWrongStatus()");
    }

    @Test
    public void test05RequestIE() throws Exception {
        // find a CA (TestCA?) create a user
        // Send certificate request for a server generated PKCS12
        setupUser(SecConst.TOKEN_SOFT_BROWSERGEN);

        String resp = sendCsrRequest(1);
        // This is a string with VB script and all
        // System.out.println(resp);
        assertTrue("Response does not contain 'cert ='", resp.contains("cert ="));
    }

    /** Tries to send a browser certificate request when the user on the server side has been 
     * added using a P12 token type. This means server generated token, which should be wrong for a browser request.
     * We should receive an error back.
     */
    @Test
    public void test06RequestIEWrongTokenType() throws Exception {
        // find a CA (TestCA?) create a user
        // Send certificate request for a server generated PKCS12
        setupUser(SecConst.TOKEN_SOFT_PEM);

        String resp = sendCsrRequest(1);
        // This is a string with VB script and all
        // System.out.println(resp);
        assertTrue(
                "Response does not contain 'User was configured for server generated token but a CSR was sent in the request'",
                resp.contains("User was configured for server generated token but a CSR was sent in the request"));
    }

    /** Tries to send a CSR and gets a certificate back 
     * added using a P12 token type. This means server generated token, which should be wrong for a CSR request.
     * We should receive an error back.
     */
    @Test
    public void test07RequestCsr() throws Exception {
        // find a CA (TestCA?) create a user
        // Send certificate request for a server generated PKCS12
        setupUser(SecConst.TOKEN_SOFT_BROWSERGEN);

        String resp = sendCsrRequest(2);
        // This is a string with VB script and all
        // System.out.println(resp);
        assertTrue("Response does not start with '-----BEGIN CERTIFICATE-----'",
                resp.startsWith("-----BEGIN CERTIFICATE-----"));
    }

    /** Tries to send a CSR when the user on the server side has been 
     * added using a P12 token type. This means server generated token, which should be wrong for a CSR request.
     * We should receive an error back.
     */
    @Test
    public void test08RequestCsrWrongTokenType() throws Exception {
        // find a CA (TestCA?) create a user
        // Send certificate request for a server generated PKCS12
        setupUser(SecConst.TOKEN_SOFT_PEM);

        String resp = sendCsrRequest(2);
        // This is a string with VB script and all
        // System.out.println(resp);
        assertTrue(
                "Response does not contain 'User was configured for server generated token but a CSR was sent in the request'",
                resp.contains("User was configured for server generated token but a CSR was sent in the request"));
    }

    /**
     * Tests request for a pkcs12 with a clear-text password
     */
    @Test
    public void test09RequestPKCS12ClearPassword() throws Exception {
        log.trace(">test01RequestPKCS12()");

        // Create a user with a clear-text password
        setupUser(SecConst.TOKEN_SOFT_P12, true);
        assertEquals("end entity password wasn't set", "foo123", findPassword(TEST_USERNAME));

        // POST the OCSP request
        URL url = new URL(httpReqPath + '/' + resourceReq);
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        // we are going to do a POST
        con.setDoOutput(true);
        con.setRequestMethod("POST");

        // POST it
        con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        OutputStream os = con.getOutputStream();
        os.write(("user=" + TEST_USERNAME + "&password=foo123&keylength=2048").getBytes("UTF-8"));
        os.close();
        assertEquals("Response code", 200, con.getResponseCode());
        // Some appserver (Weblogic) responds with
        // "application/x-pkcs12; charset=UTF-8"
        String contentType = con.getContentType();
        assertTrue("contentType was " + contentType, contentType.startsWith("application/x-pkcs12"));

        // First read the response and then close the connection
        try {
            con.getInputStream().skip(99999);
        } catch (EOFException e) {
            /* Ignore */ }
        con.disconnect();

        assertTrue("password wasn't cleared", StringUtils.isEmpty(findPassword(TEST_USERNAME)));
        log.trace("<test01RequestPKCS12()");
    }

    /** type 1 = ie (pkcs10)
     *  type 2 = csr (pkcs10req)
     */
    private String sendCsrRequest(int type) throws NoSuchAlgorithmException, NoSuchProviderException,
            InvalidAlgorithmParameterException, IOException, InvalidKeyException, SignatureException,
            OperatorCreationException, MalformedURLException, ProtocolException, UnsupportedEncodingException {
        // Create a PKCS10 request
        KeyPair rsakeys = KeyTools.genKeys("512", "RSA");
        PKCS10CertificationRequest req = CertTools.genPKCS10CertificationRequest("SHA1WithRSA",
                CertTools.stringToBcX500Name("C=SE, O=AnaTom, CN=foo"), rsakeys.getPublic(), new DERSet(),
                rsakeys.getPrivate(), null);
        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
        DEROutputStream dOut = new DEROutputStream(bOut);
        dOut.writeObject(req.toASN1Structure());
        dOut.close();
        final StringBuilder request = new StringBuilder();
        if (type == 2) {
            request.append("-----BEGIN CERTIFICATE REQUEST-----\n");
        }
        request.append(new String(Base64.encode(bOut.toByteArray())));
        if (type == 2) {
            request.append("\n-----END CERTIFICATE REQUEST-----\n");
        }
        String p10 = request.toString();
        // System.out.println(p10);

        // POST the OCSP request
        URL url = new URL(httpReqPath + '/' + resourceReq);
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        // we are going to do a POST
        con.setDoOutput(true);
        con.setRequestMethod("POST");

        // POST it
        con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        OutputStream os = con.getOutputStream();
        final StringBuilder buf = new StringBuilder("user=" + TEST_USERNAME + "&password=foo123&");
        switch (type) {
        case 1:
            buf.append("pkcs10=");
            break;
        case 2:
            buf.append("resulttype=1&pkcs10req=");
            break;
        default:
            break;
        }
        buf.append(URLEncoder.encode(p10, "UTF-8"));
        os.write(buf.toString().getBytes("UTF-8"));
        os.close();
        assertEquals("Response code", 200, con.getResponseCode());

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        // This works for small requests, and PKCS7 responses are small
        InputStream in = con.getInputStream();
        int b = in.read();
        while (b != -1) {
            baos.write(b);
            b = in.read();
        }
        baos.flush();
        in.close();
        byte[] respBytes = baos.toByteArray();
        assertTrue(respBytes.length > 0);

        String resp = new String(respBytes);
        return resp;
    }

    //
    // Private helper methods
    //

    private void setupUser(int tokentype) throws Exception {
        setupUser(tokentype, false); // without clear text password
    }

    private void setupUser(int tokentype, boolean clearpwd) throws Exception {
        // Make user that we know...
        boolean userExists = false;
        try {
            endEntityManagementSession.addUser(admin, TEST_USERNAME, "foo123", "C=SE,O=PrimeKey,CN=ReqTest", null,
                    "reqtest@primekey.se", clearpwd, SecConst.EMPTY_ENDENTITYPROFILE,
                    CertificateProfileConstants.CERTPROFILE_FIXED_ENDUSER, EndEntityTypes.ENDUSER.toEndEntityType(),
                    tokentype, 0, caid);
            log.debug("created user: " + TEST_USERNAME + ", foo123, C=SE, O=PrimeKey, CN=ReqTest");
        } catch (EndEntityExistsException e) {
            userExists = true; // This is what we want
        }
        if (userExists) {
            log.debug("User " + TEST_USERNAME + " already exists.");
            EndEntityInformation endEntityInformation = new EndEntityInformation(TEST_USERNAME,
                    "C=SE,O=PrimeKey,CN=ReqTest", caid, null, "reqtest@anatom.se",
                    EndEntityTypes.ENDUSER.toEndEntityType(), SecConst.EMPTY_ENDENTITYPROFILE,
                    CertificateProfileConstants.CERTPROFILE_FIXED_ENDUSER, tokentype, 0, null);
            endEntityInformation.setPassword("foo123");
            endEntityInformation.setStatus(EndEntityConstants.STATUS_NEW);
            endEntityManagementSession.changeUser(admin, endEntityInformation, false);
            log.debug("Reset status to NEW");
        }
    }

    private void setupUserStatus(int status) throws Exception {
        EndEntityInformation endEntityInformation = new EndEntityInformation(TEST_USERNAME,
                "C=SE,O=PrimeKey,CN=ReqTest", caid, null, "reqtest@anatom.se",
                EndEntityTypes.ENDUSER.toEndEntityType(), SecConst.EMPTY_ENDENTITYPROFILE,
                CertificateProfileConstants.CERTPROFILE_FIXED_ENDUSER, SecConst.TOKEN_SOFT_P12, 0, null);
        endEntityInformation.setPassword("foo123");
        endEntityInformation.setStatus(status);
        endEntityManagementSession.changeUser(admin, endEntityInformation, false);
        log.debug("Set status to: " + status);
    }

    private String findPassword(String user) throws Exception {
        EndEntityInformation ei = endEntityAccessSession.findUser(admin, user);
        if (ei == null) {
            log.info(InternalEjbcaResources.getInstance().getLocalizedMessage("ra.errorentitynotexist", user));
            throw new NotFoundException(
                    InternalEjbcaResources.getInstance().getLocalizedMessage("ra.wrongusernameorpassword"));
        }
        return ei.getPassword(); // This is the clear text password. See UserData.toEndEntityInformation
    }

    public String getRoleName() {
        return this.getClass().getSimpleName();
    }
}