org.ejbca.core.protocol.scep.ProtocolScepHttpTest.java Source code

Java tutorial

Introduction

Here is the source code for org.ejbca.core.protocol.scep.ProtocolScepHttpTest.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.scep;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.CRLException;
import java.security.cert.CertStoreException;
import java.security.cert.CertificateException;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Random;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.util.Streams;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Set;
import org.bouncycastle.asn1.ASN1String;
import org.bouncycastle.asn1.DERPrintableString;
import org.bouncycastle.asn1.cms.Attribute;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.cert.X509CRLHolder;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CRLConverter;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cms.CMSEnvelopedData;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessable;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedGenerator;
import org.bouncycastle.cms.RecipientInformation;
import org.bouncycastle.cms.RecipientInformationStore;
import org.bouncycastle.cms.SignerId;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSignerInfoVerifierBuilder;
import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;
import org.cesecore.SystemTestsConfiguration;
import org.cesecore.authentication.tokens.AuthenticationToken;
import org.cesecore.authentication.tokens.UsernamePrincipal;
import org.cesecore.authorization.AuthorizationDeniedException;
import org.cesecore.certificates.ca.CADoesntExistsException;
import org.cesecore.certificates.ca.X509CA;
import org.cesecore.certificates.certificate.request.ResponseStatus;
import org.cesecore.certificates.certificateprofile.CertificateProfileConstants;
import org.cesecore.certificates.endentity.EndEntityConstants;
import org.cesecore.certificates.endentity.EndEntityInformation;
import org.cesecore.certificates.endentity.EndEntityType;
import org.cesecore.certificates.endentity.EndEntityTypes;
import org.cesecore.certificates.util.AlgorithmConstants;
import org.cesecore.configuration.GlobalConfigurationSessionRemote;
import org.cesecore.junit.util.CryptoTokenRule;
import org.cesecore.junit.util.CryptoTokenTestRunner;
import org.cesecore.keys.util.KeyTools;
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.ejbca.config.ScepConfiguration;
import org.ejbca.config.WebConfiguration;
import org.ejbca.core.EjbcaException;
import org.ejbca.core.ejb.config.ConfigurationSessionRemote;
import org.ejbca.core.ejb.crl.PublishingCrlSessionRemote;
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.approval.WaitingForApprovalException;
import org.ejbca.core.model.ra.raadmin.UserDoesntFullfillEndEntityProfile;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.FixMethodOrder;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;

import com.gargoylesoftware.htmlunit.SubmitMethod;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.WebConnection;
import com.gargoylesoftware.htmlunit.WebRequestSettings;
import com.gargoylesoftware.htmlunit.WebResponse;

/**
 * Tests http pages of scep
 */
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@RunWith(CryptoTokenTestRunner.class)
public class ProtocolScepHttpTest {
    private static final Logger log = Logger.getLogger(ProtocolScepHttpTest.class);

    private static final String scepAlias = "ProtocolHttpTestScepAlias";
    private static final String resourceScep = "publicweb/apply/scep/" + scepAlias + "/pkiclient.exe";

    private static final byte[] openscep = Base64
            .decode(("MIIGqwYJKoZIhvcNAQcCoIIGnDCCBpgCAQExDjAMBggqhkiG9w0CBQUAMIICuwYJ"
                    + "KoZIhvcNAQcBoIICrASCAqgwggKkBgkqhkiG9w0BBwOgggKVMIICkQIBADGB1TCB"
                    + "0gIBADA7MC8xDzANBgNVBAMTBlRlc3RDQTEPMA0GA1UEChMGQW5hVG9tMQswCQYD"
                    + "VQQGEwJTRQIIbzEhUVZYO3gwDQYJKoZIhvcNAQEBBQAEgYCksIoSXYsCQPot2DDW"
                    + "dexdFqLj1Fuz3xSpu/rLozXKxEY0n0W0JXRR9OxxuyqNw9cLZhiyWkNsJGbP/rEz"
                    + "yrXe9NXuLK5U8+qqE8OhnY9BhCxjeUJSLni6oCSi7YzwOqdg2KmifJrQQI/jZIiC"
                    + "tSISAtE6qi6DKQwLCkQLmokLrjCCAbIGCSqGSIb3DQEHATARBgUrDgMCBwQILYvZ"
                    + "rBWuC02AggGQW9o5MB/7LN4o9G4ZD1l2mHzS+g+Y/dT2qD/qIaQi1Mamv2oKx9eO"
                    + "uFtaGkBBGWZlIKg4mm/DFtvXqW8Y5ijAiQVHHPuRKNyIV6WVuFjNjhNlM+DWLJR+"
                    + "rpHEhvB6XeDo/pd+TyOKFcxedMPTD7U+j46yd46vKdmoKAiIF21R888uVSz3GDts"
                    + "NlqgvZ7VlaI++Tj7aPdOI7JTdQXZk2FWF7Ql0LBIPwk9keffptF5if5Y+aHqB0a2"
                    + "uQj1aE8Em15VG8p8MmLJOX0OA1aeqfxR0wk343r44UebliY2DE8cEnym/fmya30/"
                    + "7WYzJ7erWofO2ukg1yc93wUpyIKxt2RGIy5geqQCjCYSSGgaNFafEV2pnOVSx+7N"
                    + "9z/ICNQfDBD6b83MO7yPHC1cXcdREKHHeqaKyQLiVRk9+R/3D4vEZt682GRaUKOY"
                    + "PQXK1Be2nyZoo4gZs62nZVAliJ+chFkEUog9k9OsIvZRG7X+VEjVYBqxlE1S3ikt"
                    + "igFXiuLC/LDCi3IgVwQjfNx1/mhxsO7GSaCCAfswggH3MIIBYKADAgEDAiA4OEUy"
                    + "REVFNDcwNjhCQjM3RjE5QkE2NDdCRjAyRkQwRjANBgkqhkiG9w0BAQQFADAyMQsw"
                    + "CQYDVQQGEwJTZTERMA8GA1UEChMIUHJpbWVLZXkxEDAOBgNVBAMTB1RvbWFzIEcw"
                    + "HhcNMDMwNjAxMDgzNDQyWhcNMDMwNzAxMDgzNDQyWjAyMQswCQYDVQQGEwJTZTER"
                    + "MA8GA1UEChMIUHJpbWVLZXkxEDAOBgNVBAMTB1RvbWFzIEcwgZ8wDQYJKoZIhvcN"
                    + "AQEBBQADgY0AMIGJAoGBAOu47fpIQfzfSnEBTG2WJpKZz1891YLNulc7XgMk8hl3"
                    + "nVC4m34SaR7eXR3nCsorYEpPPmL3affaPFsBnNBQNoZLxKmQ1RKiDyu8dj90AKCP"
                    + "CFlIM2aJbKMiQad+dt45qse6k0yTrY3Yx0hMH76tRkDif4DjM5JUvdf4d/zlYcCz"
                    + "AgMBAAEwDQYJKoZIhvcNAQEEBQADgYEAGNoWI02kXNEA5sPHb3KEY8QZoYM5Kha1"
                    + "JA7HLmlXKy6geeJmk329CUnvF0Cr7zxbMkFRdUDUtR8omDDnGlBSOCkV6LLYH939"
                    + "Z8iysfaxigZkxUqUYGLtYHhsEjVgcpfKZVxTz0E2ocR2P+IuU04Duel/gU4My6Qv"
                    + "LDpwo1CQC10xggHDMIIBvwIBATBWMDIxCzAJBgNVBAYTAlNlMREwDwYDVQQKEwhQ"
                    + "cmltZUtleTEQMA4GA1UEAxMHVG9tYXMgRwIgODhFMkRFRTQ3MDY4QkIzN0YxOUJB"
                    + "NjQ3QkYwMkZEMEYwDAYIKoZIhvcNAgUFAKCBwTASBgpghkgBhvhFAQkCMQQTAjE5"
                    + "MBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTAzMDYw"
                    + "MTA4MzQ0MlowHwYJKoZIhvcNAQkEMRIEEBqGJFo7n4B8sFBCi54PckIwIAYKYIZI"
                    + "AYb4RQEJBTESBBA77Owxh2rbflhXsDYw3xsLMDAGCmCGSAGG+EUBCQcxIhMgODhF"
                    + "MkRFRTQ3MDY4QkIzN0YxOUJBNjQ3QkYwMkZEMEYwDQYJKoZIhvcNAQEBBQAEgYB4"
                    + "BPcw4NPIt4nMOFKSGg5oM1nGDPGFN7eorZV+/2uWiQfdtK4B4lzCTuNxWRT853dW"
                    + "dRDzXBCGEArlG8ef+vDD/HP9SX3MQ0NJWym48VI9bTpP/mJlUKSsfgDYHohvUlVI"
                    + "E5QFC6ILVLUmuWPGchUEAb8t30DDnmeXs8QxdqHfbQ==").getBytes());

    private static final AuthenticationToken admin = new TestAlwaysAllowLocalAuthenticationToken(
            new UsernamePrincipal("ProtocolScepHttpTest"));
    private static X509CA x509ca;
    private static X509Certificate cacert;
    private static KeyPair key1;
    private static KeyPair key2;
    private static final String userName1 = "sceptest1";
    private static final String userName2 = "sceptest2";
    private static final String userDN1 = "C=SE,O=PrimeKey,CN=" + userName1;
    private static final String userDN2 = "C=SE,O=PrimeKey,CN=" + userName2;
    private static String senderNonce = null;
    private static String transId = null;

    private Random rand = new Random();
    private String httpReqPath;
    private ScepConfiguration scepConfiguration;

    private final ConfigurationSessionRemote configurationSessionRemote = EjbRemoteHelper.INSTANCE
            .getRemoteSession(ConfigurationSessionRemote.class, EjbRemoteHelper.MODULE_TEST);
    private final EndEntityManagementSessionRemote endEntityManagementSession = EjbRemoteHelper.INSTANCE
            .getRemoteSession(EndEntityManagementSessionRemote.class);
    private final GlobalConfigurationSessionRemote globalConfigSession = EjbRemoteHelper.INSTANCE
            .getRemoteSession(GlobalConfigurationSessionRemote.class);
    private final PublishingCrlSessionRemote publishingCrlSession = EjbRemoteHelper.INSTANCE
            .getRemoteSession(PublishingCrlSessionRemote.class);

    @ClassRule
    public static CryptoTokenRule cryptoTokenRule = new CryptoTokenRule();

    @Rule
    public TestRule traceLogMethodsRule = new TraceLogMethodsRule();

    @BeforeClass
    public static void beforeClass() throws Exception {
        // Pre-generate key for all requests to speed things up a bit
        try {
            key1 = KeyTools.genKeys("512", AlgorithmConstants.KEYALGORITHM_RSA);
            key2 = KeyTools.genKeys("512", AlgorithmConstants.KEYALGORITHM_RSA);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        x509ca = cryptoTokenRule.createX509Ca();
        cacert = (X509Certificate) x509ca.getCACertificate();
    }

    @Before
    public void setUp() throws Exception {
        final String httpHost = SystemTestsConfiguration
                .getRemoteHost(configurationSessionRemote.getProperty(WebConfiguration.CONFIG_HTTPSSERVERHOSTNAME));
        final String httpPort = SystemTestsConfiguration.getRemotePortHttp(
                configurationSessionRemote.getProperty(WebConfiguration.CONFIG_HTTPSERVERPUBHTTP));
        httpReqPath = "http://" + httpHost + ":" + httpPort + "/ejbca";

        scepConfiguration = (ScepConfiguration) globalConfigSession
                .getCachedConfiguration(ScepConfiguration.SCEP_CONFIGURATION_ID);
        scepConfiguration.addAlias(scepAlias);
        globalConfigSession.saveConfiguration(admin, scepConfiguration);

    }

    @After
    public void tearDown() throws Exception {
        // remove user
        try {
            endEntityManagementSession.deleteUser(admin, userName1);
            log.debug("deleted user: " + userName1);
        } catch (Exception e) {
            // NOPMD: ignore
        }
        try {
            endEntityManagementSession.deleteUser(admin, userName2);
            log.debug("deleted user: " + userName2);
        } catch (Exception e) {
            // NOPMD: ignore
        }

        scepConfiguration.removeAlias(scepAlias);
        globalConfigSession.saveConfiguration(admin, scepConfiguration);
    }

    @AfterClass
    public static void afterClass() {
        cryptoTokenRule.cleanUp();
    }

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

    @Test
    public void test01Access() throws Exception {
        // Hit scep
        final WebClient webClient = new WebClient();
        WebConnection con = webClient.getWebConnection();

        // Gives a 400: Bad Request
        WebRequestSettings settings = new WebRequestSettings(new URL(httpReqPath + '/' + resourceScep));
        WebResponse resp = con.getResponse(settings);
        assertEquals("Response code", 400, resp.getStatusCode());
    }

    @Test
    public void test02AccessTest() throws Exception {

        ScepConfiguration scepConfig = (ScepConfiguration) globalConfigSession
                .getCachedConfiguration(ScepConfiguration.SCEP_CONFIGURATION_ID);
        boolean remove = false;
        if (!scepConfig.aliasExists("scep")) {
            scepConfig.addAlias("scep");
            globalConfigSession.saveConfiguration(admin, scepConfig);
            remove = true;
        }

        String resourceName = "/ejbca/publicweb/apply/scep/pkiclient.exe?operation=GetCACert&message="
                + x509ca.getName();
        String httpHost = SystemTestsConfiguration.getRemoteHost("127.0.0.1");
        String httpPort = SystemTestsConfiguration.getRemotePortHttp(
                configurationSessionRemote.getProperty(WebConfiguration.CONFIG_HTTPSERVERPUBHTTP));
        String httpBaseUrl = "http://" + httpHost + ":" + httpPort;

        String url = httpBaseUrl + resourceName;
        final HttpURLConnection con;
        URL u = new URL(url);
        con = (HttpURLConnection) u.openConnection();
        con.setRequestMethod("GET");
        con.getDoOutput();
        con.connect();

        int ret = con.getResponseCode();
        log.debug("HTTP response code: " + ret);
        if (ret == 200) {
            log.debug(Streams.asString(con.getInputStream()));
        }
        con.disconnect();

        if (remove) {
            scepConfig.removeAlias("scep");
            globalConfigSession.saveConfiguration(admin, scepConfig);
        }
        assertEquals("HTTP GET is not supported. (This test expects " + httpBaseUrl + resourceName + " to exist)",
                200, ret);

    }

    /**
     * Tests that the right configuration alias is extracted from the SCEP URL. 
     * 
     * A SCEP request for a non-existing alias is sent. Expected an error message caused by the absence of the expected SCEP alias 
     * 
     * @throws Exception
     */
    @Test
    public void test02Access() throws Exception {

        ScepConfiguration scepConfig = (ScepConfiguration) globalConfigSession
                .getCachedConfiguration(ScepConfiguration.SCEP_CONFIGURATION_ID);

        sendScepAliasRequest(scepConfig, "alias123", "alias123", "SCEP alias 'alias123' does not exist"); // "alias123" in the request causes Ejbca to use "alias123" as SCEP alias
        sendScepAliasRequest(scepConfig, "123", "123", "SCEP alias '123' does not exist"); // "123" in the request causes Ejbca to use "123" as SCEP alias
        sendScepAliasRequest(scepConfig, "", "scep", "SCEP alias 'scep' does not exist"); // No alias in the request causes Ejbca to use "scep" (the default alias) as SCEP alias
        sendScepAliasRequest(scepConfig, null, "scep", "SCEP alias 'scep' does not exist"); // No alias in the request causes Ejbca to use "scep" (the default alias) as SCEP alias
        sendScepAliasRequest(scepConfig, "alias??&!!foo", null, "Wrong URL. No alias found"); // Specifying alias with non-alphanumeric characters causes the request to fail. 
    }

    /**
     * Tests a random old scep message from OpenScep
     * 
     * @throws Exception error
     */
    @Test
    public void test03OpenScep() throws Exception {
        // send message to server and see what happens
        final WebClient webClient = new WebClient();
        WebConnection con = webClient.getWebConnection();
        WebRequestSettings settings = new WebRequestSettings(new URL(httpReqPath + '/' + resourceScep),
                SubmitMethod.GET);
        ArrayList<NameValuePair> l = new ArrayList<NameValuePair>();
        l.add(new NameValuePair("operation", "PKIOperation"));
        l.add(new NameValuePair("message", new String(Base64.encode(openscep))));
        settings.setRequestParameters(l);
        WebResponse resp = con.getResponse(settings);
        // TODO: since our request most certainly uses the wrong CA cert to
        // encrypt the
        // request, it will fail. If we get something back, we came a little bit
        // at least :)
        // We should get a NOT_FOUND error back.
        assertEquals("Response code", 404, resp.getStatusCode());
    }

    @Test
    public void test04ScepRequestOKSHA1() throws Exception {
        // find a CA create a user and
        // send SCEP req to server and get good response with cert

        scepConfiguration.setIncludeCA(scepAlias, true);
        globalConfigSession.saveConfiguration(admin, scepConfiguration);

        // Make user that we know...
        createScepUser(userName1, userDN1);

        byte[] msgBytes = genScepRequest(false, CMSSignedGenerator.DIGEST_SHA1, userDN1);
        // Send message with GET
        byte[] retMsg = sendScep(false, msgBytes);
        assertNotNull(retMsg);
        checkScepResponse(retMsg, userDN1, senderNonce, transId, false, CMSSignedGenerator.DIGEST_SHA1, false);

    }

    @Test
    public void test05ScepRequestOKMD5() throws Exception {
        // find a CA create a user and
        // send SCEP req to server and get good response with cert

        scepConfiguration.setIncludeCA(scepAlias, true);
        globalConfigSession.saveConfiguration(admin, scepConfiguration);

        // Make user that we know...
        createScepUser(userName1, userDN1);

        byte[] msgBytes = genScepRequest(false, CMSSignedGenerator.DIGEST_MD5, userDN1);
        // Send message with GET
        byte[] retMsg = sendScep(false, msgBytes);
        assertNotNull(retMsg);
        checkScepResponse(retMsg, userDN1, senderNonce, transId, false, CMSSignedGenerator.DIGEST_MD5, false);

    }

    @Test
    public void test06ScepRequestPostOK() throws Exception {
        // find a CA, create a user and
        // send SCEP req to server and get good response with cert

        scepConfiguration.setIncludeCA(scepAlias, true);
        globalConfigSession.saveConfiguration(admin, scepConfiguration);

        createScepUser(userName1, userDN1);

        byte[] msgBytes = genScepRequest(false, CMSSignedGenerator.DIGEST_SHA1, userDN1);
        // Send message with GET
        byte[] retMsg = sendScep(true, msgBytes);
        assertNotNull(retMsg);
        checkScepResponse(retMsg, userDN1, senderNonce, transId, false, CMSSignedGenerator.DIGEST_SHA1, false);

    }

    @Test
    public void test07ScepRequestPostOKNoCA() throws Exception {
        // find a CA, create a user and
        // send SCEP req to server and get good response with cert

        scepConfiguration.setIncludeCA(scepAlias, false);
        globalConfigSession.saveConfiguration(admin, scepConfiguration);

        createScepUser(userName1, userDN1);

        byte[] msgBytes = genScepRequest(false, CMSSignedGenerator.DIGEST_SHA1, userDN1);
        // Send message with GET
        byte[] retMsg = sendScep(true, msgBytes);
        assertNotNull(retMsg);
        checkScepResponse(retMsg, userDN1, senderNonce, transId, false, CMSSignedGenerator.DIGEST_SHA1, true);

    }

    @Test
    public void test08ScepGetCACert() throws Exception {
        {
            String reqUrl = httpReqPath + '/' + resourceScep + "?operation=GetCACert&message="
                    + URLEncoder.encode(x509ca.getName(), "UTF-8");
            URL url = new URL(reqUrl);
            HttpURLConnection con = (HttpURLConnection) url.openConnection();
            con.setRequestMethod("GET");
            con.getDoOutput();
            con.connect();
            assertEquals("Response code is not 200 (OK)", 200, con.getResponseCode());
            // Some appserver (Weblogic) responds with
            // "application/x-x509-ca-cert; charset=UTF-8"
            assertTrue(con.getContentType().startsWith("application/x-x509-ca-cert"));
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            // This works for small requests, and SCEP requests are small enough
            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();
            assertNotNull("Response can not be null.", respBytes);
            assertTrue(respBytes.length > 0);
            X509Certificate cert = (X509Certificate) CertTools.getCertfromByteArray(respBytes);
            // Check that we got the right cert back
            assertEquals(cacert.getSubjectDN().getName(), cert.getSubjectDN().getName());
        }

        // 
        // Test the same message but without message component, it should use a default CA
        {
            // Try with a non extisting CA first, should respond with a 404
            updatePropertyOnServer("scep.defaultca", "NonExistingCAForSCEPTest");
            String reqUrl = httpReqPath + '/' + resourceScep + "?operation=GetCACert";
            URL url = new URL(reqUrl);
            HttpURLConnection con = (HttpURLConnection) url.openConnection();
            con.setRequestMethod("GET");
            con.getDoOutput();
            con.connect();
            assertEquals("Response code is not 404 (not found)", 404, con.getResponseCode());
            // Try with the good CA            
            updatePropertyOnServer("scep.defaultca", x509ca.getName());
            con = (HttpURLConnection) url.openConnection();
            con.setRequestMethod("GET");
            con.getDoOutput();
            con.connect();
            assertEquals("Response code is not 200 (OK)", 200, con.getResponseCode());
            // Some appserver (Weblogic) responds with
            // "application/x-x509-ca-cert; charset=UTF-8"
            assertTrue(con.getContentType().startsWith("application/x-x509-ca-cert"));
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            // This works for small requests, and SCEP requests are small enough
            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();
            assertNotNull("Response can not be null.", respBytes);
            assertTrue(respBytes.length > 0);
            X509Certificate cert = (X509Certificate) CertTools.getCertfromByteArray(respBytes);
            // Check that we got the right cert back
            assertEquals(cacert.getSubjectDN().getName(), cert.getSubjectDN().getName());
        }
    }

    @Test
    public void test09ScepGetCrl() throws Exception {

        scepConfiguration.setIncludeCA(scepAlias, false);
        globalConfigSession.saveConfiguration(admin, scepConfiguration);
        publishingCrlSession.forceCRL(admin, x509ca.getCAId());
        byte[] msgBytes = genScepRequest(true, CMSSignedGenerator.DIGEST_SHA1, userDN1);
        // Send message with GET
        byte[] retMsg = sendScep(false, msgBytes);
        assertNotNull(retMsg);
        checkScepResponse(retMsg, userDN1, senderNonce, transId, true, CMSSignedGenerator.DIGEST_SHA1, false);

    }

    @Test
    public void test10ScepGetCACaps() throws Exception {
        String reqUrl = httpReqPath + '/' + resourceScep + "?operation=GetCACaps&message="
                + URLEncoder.encode(x509ca.getName(), "UTF-8");
        URL url = new URL(reqUrl);
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        con.setRequestMethod("GET");
        con.getDoOutput();
        con.connect();
        assertEquals("Response code", 200, con.getResponseCode());
        // Some appserver (Weblogic) responds with "text/plain; charset=UTF-8"
        assertTrue(con.getContentType().startsWith("text/plain"));
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        // This works for small requests, and SCEP requests are small enough
        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();
        assertNotNull("Response can not be null.", respBytes);
        assertTrue(respBytes.length > 0);
        assertEquals(new String(respBytes), "POSTPKIOperation\nRenewal\nSHA-1");
    }

    @Test
    public void test11EnforcementOfUniquePublicKeys() throws Exception {
        scepConfiguration.setIncludeCA(scepAlias, false);
        globalConfigSession.saveConfiguration(admin, scepConfiguration);

        // create new user for new DN.
        createScepUser(userName2, userDN2);

        final byte[] msgBytes = genScepRequest(false, CMSSignedGenerator.DIGEST_SHA1, userDN2);
        // Send message with GET
        final byte[] retMsg = sendScep(true, msgBytes, HttpServletResponse.SC_BAD_REQUEST);

        String returnMessageString = new String(retMsg);
        String localizedMessage = InternalResourcesStub.getInstance()
                .getLocalizedMessage("createcert.key_exists_for_another_user", userName2);
        if ("createcert.key_exists_for_another_user".equals(localizedMessage)) {
            String currentDirectory = System.getProperty("user.dir");
            throw new Error("Test can't continue, can't find language resource files. Current directory is "
                    + currentDirectory);
        }
        assertTrue(returnMessageString.indexOf(localizedMessage) >= 0);
    }

    @Test
    public void test12EnforcementOfUniqueDN() throws Exception {

        scepConfiguration.setIncludeCA(scepAlias, false);
        globalConfigSession.saveConfiguration(admin, scepConfiguration);

        createScepUser(userName2, userDN2);
        // new user will have a DN of a certificate already issued for another
        // user.
        changeScepUser(userName2, userDN1);

        final byte[] msgBytes = genScepRequest(false, CMSSignedGenerator.DIGEST_SHA1, userDN2, key2,
                BouncyCastleProvider.PROVIDER_NAME);
        // Send message with GET
        final byte[] retMsg = sendScep(true, msgBytes, HttpServletResponse.SC_BAD_REQUEST);
        String returnMessageString = new String(retMsg);
        String localizedMessage = InternalResourcesStub.getInstance().getLocalizedMessage(
                "createcert.subjectdn_exists_for_another_user", userName2, "'" + userName1 + "'");

        if ("createcert.subjectdn_exists_for_another_user".equals(localizedMessage)) {
            String currentDirectory = System.getProperty("user.dir");
            throw new Error("Test can't continue, can't find language resource files. Current directory is "
                    + currentDirectory);
        }
        assertTrue(returnMessageString.indexOf(localizedMessage) >= 0);
    }

    //
    // Private helper methods
    //
    /**
     * Sends a SCEP request with the alias requestAlias in the URL and expects a SCEP error message 
     * that can inform us about the alias extracted from the URL.
     */
    private void sendScepAliasRequest(ScepConfiguration scepConfig, String requestAlias, String extractedAlias,
            String expectedErrMsg) throws Exception {

        if (extractedAlias != null) {
            if (scepConfig.aliasExists(extractedAlias)) {
                scepConfig.renameAlias(extractedAlias,
                        "backUpAlias" + extractedAlias + "ForAliasTesting001122334455");
                globalConfigSession.saveConfiguration(admin, scepConfig);
            }
        }

        try {
            String resource = "publicweb/apply/scep/" + (requestAlias != null ? requestAlias + "/" : "")
                    + "pkiclient.exe";
            String urlString = httpReqPath + '/' + resource + "?operation=PKIOperation";
            log.info("http URL: " + urlString);

            String reqUrl = urlString + "&message=" + URLEncoder.encode("Test Scep message", "UTF-8");
            URL url = new URL(reqUrl);
            final HttpURLConnection con = (HttpURLConnection) url.openConnection();
            con.setRequestMethod("GET");
            con.getDoOutput();
            con.connect();
            assertEquals("Unexpected HTTP response code.", HttpServletResponse.SC_BAD_REQUEST,
                    con.getResponseCode()); // OK response (will use alias "alias123")

            InputStream err = con.getErrorStream();
            byte[] errB = new byte[1024];
            err.read(errB);
            err.close();
            String response = new String(errB);
            assertTrue("Response does not contain the correct error message",
                    StringUtils.contains(response, expectedErrMsg));
        } finally {
            if (extractedAlias != null) {
                if (scepConfig.aliasExists("backUpAlias" + extractedAlias + "ForAliasTesting001122334455")) {
                    scepConfig.renameAlias("backUpAlias" + extractedAlias + "ForAliasTesting001122334455",
                            extractedAlias);
                    globalConfigSession.saveConfiguration(admin, scepConfig);
                }
            }
        }
    }

    private EndEntityInformation getEndEntityInformation(String userName, String userDN) {
        final EndEntityInformation data = new EndEntityInformation(userName, userDN, x509ca.getCAId(), null,
                "sceptest@primekey.se", new EndEntityType(EndEntityTypes.ENDUSER), SecConst.EMPTY_ENDENTITYPROFILE,
                CertificateProfileConstants.CERTPROFILE_FIXED_ENDUSER, SecConst.TOKEN_SOFT_PEM, 0, null);
        data.setPassword("foo123");
        data.setStatus(EndEntityConstants.STATUS_NEW);
        return data;
    }

    private void createScepUser(String userName, String userDN)
            throws EndEntityExistsException, CADoesntExistsException, AuthorizationDeniedException,
            UserDoesntFullfillEndEntityProfile, WaitingForApprovalException, EjbcaException {
        if (!endEntityManagementSession.existsUser(userName)) {
            endEntityManagementSession.addUser(admin, getEndEntityInformation(userName, userDN), false);
        } else {
            changeScepUser(userName, userDN);
        }
    }

    private void changeScepUser(String userName, String userDN)
            throws CADoesntExistsException, AuthorizationDeniedException, UserDoesntFullfillEndEntityProfile,
            WaitingForApprovalException, EjbcaException {
        endEntityManagementSession.changeUser(admin, getEndEntityInformation(userName, userDN), false);
        log.debug("changing user: " + userName + ", foo123, " + userDN);
    }

    private byte[] genScepRequest(boolean makeCrlReq, String digestoid, String userDN)
            throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, SignatureException,
            InvalidAlgorithmParameterException, CertStoreException, IOException, CMSException,
            IllegalStateException, OperatorCreationException, CertificateException {
        return genScepRequest(makeCrlReq, digestoid, userDN, key1, BouncyCastleProvider.PROVIDER_NAME);
    }

    private byte[] genScepRequest(boolean makeCrlReq, String digestoid, String userDN, KeyPair keyPair,
            String signatureProvider) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException,
            SignatureException, InvalidAlgorithmParameterException, CertStoreException, IOException, CMSException,
            OperatorCreationException, CertificateException {
        ScepRequestGenerator gen = new ScepRequestGenerator();
        gen.setKeys(keyPair, signatureProvider);
        gen.setDigestOid(digestoid);
        byte[] msgBytes = null;
        // Create a transactionId
        byte[] randBytes = new byte[16];
        this.rand.nextBytes(randBytes);
        byte[] digest = CertTools.generateMD5Fingerprint(randBytes);
        transId = new String(Base64.encode(digest));
        final X509Certificate senderCertificate = CertTools.genSelfCert("CN=SenderCertificate", 24 * 60 * 60 * 1000,
                null, keyPair.getPrivate(), keyPair.getPublic(), AlgorithmConstants.SIGALG_SHA1_WITH_RSA, false);
        if (makeCrlReq) {
            msgBytes = gen.generateCrlReq(userDN, transId, cacert, senderCertificate, keyPair.getPrivate());
        } else {
            msgBytes = gen.generateCertReq(userDN, "foo123", transId, cacert, senderCertificate,
                    keyPair.getPrivate());
        }
        assertNotNull(msgBytes);
        senderNonce = gen.getSenderNonce();
        byte[] nonceBytes = Base64.decode(senderNonce.getBytes());
        assertTrue(nonceBytes.length == 16);
        return msgBytes;
    }

    private void checkScepResponse(byte[] retMsg, String userDN, String _senderNonce, String _transId,
            boolean crlRep, String digestOid, boolean noca)
            throws CMSException, OperatorCreationException, NoSuchProviderException, CRLException,
            InvalidKeyException, NoSuchAlgorithmException, SignatureException, CertificateException {

        // Parse response message
        //
        CMSSignedData s = new CMSSignedData(retMsg);
        // The signer, i.e. the CA, check it's the right CA
        SignerInformationStore signers = s.getSignerInfos();
        @SuppressWarnings("unchecked")
        Collection<SignerInformation> col = signers.getSigners();
        assertTrue(col.size() > 0);
        Iterator<SignerInformation> iter = col.iterator();
        SignerInformation signerInfo = iter.next();
        // Check that the message is signed with the correct digest alg
        assertEquals(signerInfo.getDigestAlgOID(), digestOid);
        SignerId sinfo = signerInfo.getSID();
        // Check that the signer is the expected CA
        assertEquals(CertTools.stringToBCDNString(cacert.getIssuerDN().getName()),
                CertTools.stringToBCDNString(sinfo.getIssuer().toString()));
        // Verify the signature
        JcaDigestCalculatorProviderBuilder calculatorProviderBuilder = new JcaDigestCalculatorProviderBuilder()
                .setProvider(BouncyCastleProvider.PROVIDER_NAME);
        JcaSignerInfoVerifierBuilder jcaSignerInfoVerifierBuilder = new JcaSignerInfoVerifierBuilder(
                calculatorProviderBuilder.build()).setProvider(BouncyCastleProvider.PROVIDER_NAME);
        boolean ret = signerInfo.verify(jcaSignerInfoVerifierBuilder.build(cacert.getPublicKey()));
        assertTrue(ret);
        // Get authenticated attributes
        AttributeTable tab = signerInfo.getSignedAttributes();
        // --Fail info
        Attribute attr = tab.get(new ASN1ObjectIdentifier(ScepRequestMessage.id_failInfo));
        // No failInfo on this success message
        assertNull(attr);
        // --Message type
        attr = tab.get(new ASN1ObjectIdentifier(ScepRequestMessage.id_messageType));
        assertNotNull(attr);
        ASN1Set values = attr.getAttrValues();
        assertEquals(values.size(), 1);
        ASN1String str = DERPrintableString.getInstance((values.getObjectAt(0)));
        String messageType = str.getString();
        assertEquals("3", messageType);
        // --Success status
        attr = tab.get(new ASN1ObjectIdentifier(ScepRequestMessage.id_pkiStatus));
        assertNotNull(attr);
        values = attr.getAttrValues();
        assertEquals(values.size(), 1);
        str = DERPrintableString.getInstance((values.getObjectAt(0)));
        assertEquals(ResponseStatus.SUCCESS.getStringValue(), str.getString());
        // --SenderNonce
        attr = tab.get(new ASN1ObjectIdentifier(ScepRequestMessage.id_senderNonce));
        assertNotNull(attr);
        values = attr.getAttrValues();
        assertEquals(values.size(), 1);
        ASN1OctetString octstr = ASN1OctetString.getInstance(values.getObjectAt(0));
        // SenderNonce is something the server came up with, but it should be 16
        // chars
        assertTrue(octstr.getOctets().length == 16);
        // --Recipient Nonce
        attr = tab.get(new ASN1ObjectIdentifier(ScepRequestMessage.id_recipientNonce));
        assertNotNull(attr);
        values = attr.getAttrValues();
        assertEquals(values.size(), 1);
        octstr = ASN1OctetString.getInstance(values.getObjectAt(0));
        // recipient nonce should be the same as we sent away as sender nonce
        assertEquals(_senderNonce, new String(Base64.encode(octstr.getOctets())));
        // --Transaction ID
        attr = tab.get(new ASN1ObjectIdentifier(ScepRequestMessage.id_transId));
        assertNotNull(attr);
        values = attr.getAttrValues();
        assertEquals(values.size(), 1);
        str = DERPrintableString.getInstance((values.getObjectAt(0)));
        // transid should be the same as the one we sent
        assertEquals(_transId, str.getString());

        //
        // Check different message types
        //
        if (messageType.equals("3")) {
            // First we extract the encrypted data from the CMS enveloped data
            // contained
            // within the CMS signed data
            final CMSProcessable sp = s.getSignedContent();
            final byte[] content = (byte[]) sp.getContent();
            final CMSEnvelopedData ed = new CMSEnvelopedData(content);
            final RecipientInformationStore recipients = ed.getRecipientInfos();
            Store certstore;

            @SuppressWarnings("unchecked")
            Collection<RecipientInformation> c = recipients.getRecipients();
            assertEquals(c.size(), 1);
            Iterator<RecipientInformation> riIterator = c.iterator();
            byte[] decBytes = null;
            RecipientInformation recipient = riIterator.next();
            JceKeyTransEnvelopedRecipient rec = new JceKeyTransEnvelopedRecipient(key1.getPrivate());
            rec.setContentProvider(BouncyCastleProvider.PROVIDER_NAME);
            decBytes = recipient.getContent(rec);
            // This is yet another CMS signed data
            CMSSignedData sd = new CMSSignedData(decBytes);
            // Get certificates from the signed data
            certstore = sd.getCertificates();

            if (crlRep) {
                // We got a reply with a requested CRL
                @SuppressWarnings("unchecked")
                final Collection<X509CRLHolder> crls = (Collection<X509CRLHolder>) sd.getCRLs().getMatches(null);
                assertEquals(crls.size(), 1);
                final Iterator<X509CRLHolder> it = crls.iterator();
                // CRL is first (and only)
                final X509CRL retCrl = new JcaX509CRLConverter().getCRL(it.next());
                log.info("Got CRL with DN: " + retCrl.getIssuerDN().getName());

                // check the returned CRL
                assertEquals(CertTools.getSubjectDN(cacert), CertTools.getIssuerDN(retCrl));
                retCrl.verify(cacert.getPublicKey());
            } else {
                // We got a reply with a requested certificate
                @SuppressWarnings("unchecked")
                final Collection<X509CertificateHolder> certs = (Collection<X509CertificateHolder>) certstore
                        .getMatches(null);
                // EJBCA returns the issued cert and the CA cert (cisco vpn
                // client requires that the ca cert is included)
                if (noca) {
                    assertEquals(certs.size(), 1);
                } else {
                    assertEquals(certs.size(), 2);
                }
                final Iterator<X509CertificateHolder> it = certs.iterator();
                // Issued certificate must be first
                boolean verified = false;
                boolean gotcacert = false;
                JcaX509CertificateConverter jcaX509CertificateConverter = new JcaX509CertificateConverter();
                while (it.hasNext()) {
                    X509Certificate retcert = jcaX509CertificateConverter.getCertificate(it.next());
                    log.info("Got cert with DN: " + retcert.getSubjectDN().getName());

                    // check the returned certificate
                    String subjectdn = CertTools.stringToBCDNString(retcert.getSubjectDN().getName());
                    if (CertTools.stringToBCDNString(userDN).equals(subjectdn)) {
                        // issued certificate
                        assertEquals(CertTools.stringToBCDNString(userDN), subjectdn);
                        assertEquals(CertTools.getSubjectDN(cacert), CertTools.getIssuerDN(retcert));
                        retcert.verify(cacert.getPublicKey());
                        assertTrue(checkKeys(key1.getPrivate(), retcert.getPublicKey()));
                        verified = true;
                    } else {
                        // ca certificate
                        assertEquals(CertTools.getSubjectDN(cacert), CertTools.getSubjectDN(retcert));
                        gotcacert = true;
                    }
                }
                assertTrue(verified);
                if (noca) {
                    assertFalse(gotcacert);
                } else {
                    assertTrue(gotcacert);
                }
            }
        }

    }

    /**
     * checks that a public and private key matches by signing and verifying a message
     */
    private boolean checkKeys(PrivateKey priv, PublicKey pub)
            throws SignatureException, NoSuchAlgorithmException, InvalidKeyException {
        Signature signer = Signature.getInstance("SHA1WithRSA");
        signer.initSign(priv);
        signer.update("PrimeKey".getBytes());
        byte[] signature = signer.sign();

        Signature signer2 = Signature.getInstance("SHA1WithRSA");
        signer2.initVerify(pub);
        signer2.update("PrimeKey".getBytes());
        return signer2.verify(signature);
    }

    private byte[] sendScep(boolean post, byte[] scepPackage) throws IOException {
        return sendScep(post, scepPackage, HttpServletResponse.SC_OK);
    }

    private byte[] sendScep(boolean post, byte[] scepPackage, int responseCode) throws IOException {
        // POST the SCEP request
        // we are going to do a POST
        String urlString = httpReqPath + '/' + resourceScep + "?operation=PKIOperation";
        log.debug("UrlString =" + urlString);
        final HttpURLConnection con;
        if (post) {
            URL url = new URL(urlString);
            con = (HttpURLConnection) url.openConnection();
            con.setDoOutput(true);
            con.setRequestMethod("POST");
            con.connect();
            // POST it
            OutputStream os = con.getOutputStream();
            os.write(scepPackage);
            os.close();
        } else {
            String reqUrl = urlString + "&message="
                    + URLEncoder.encode(new String(Base64.encode(scepPackage)), "UTF-8");
            URL url = new URL(reqUrl);
            con = (HttpURLConnection) url.openConnection();
            con.setRequestMethod("GET");
            con.getDoOutput();
            con.connect();
        }

        assertEquals("Response code", responseCode, con.getResponseCode());
        // Some appserver (Weblogic) responds with
        // "application/x-pki-message; charset=UTF-8"
        if (responseCode == HttpServletResponse.SC_OK) {
            assertTrue(con.getContentType().startsWith("application/x-pki-message"));
        } else {
            assertTrue(con.getContentType().startsWith("text/html"));
        }
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        // This works for small requests, and SCEP requests are small enough
        final InputStream in;
        if (responseCode == HttpServletResponse.SC_OK) {
            in = con.getInputStream();
        } else {
            in = con.getErrorStream();
        }
        int b = in.read();
        while (b != -1) {
            baos.write(b);
            b = in.read();
        }
        baos.flush();
        in.close();
        byte[] respBytes = baos.toByteArray();
        assertNotNull("Response can not be null.", respBytes);
        assertTrue(respBytes.length > 0);
        return respBytes;
    }

    private void updatePropertyOnServer(String property, String value) {
        final String msg = "Setting property on server: " + property + "=" + value;
        log.debug(msg);
        boolean ret = EjbRemoteHelper.INSTANCE
                .getRemoteSession(ConfigurationSessionRemote.class, EjbRemoteHelper.MODULE_TEST)
                .updateProperty(property, value);
        if (!ret) {
            throw new IllegalStateException("Failed operation: " + msg);
        }
    }

    static class InternalResourcesStub extends InternalEjbcaResources {

        private static final long serialVersionUID = 1L;
        private static final Logger log = Logger.getLogger(InternalResourcesStub.class);

        private InternalResourcesStub() {

            setupResources();

        }

        private void setupResources() {
            String primaryLanguage = PREFEREDINTERNALRESOURCES.toLowerCase();
            String secondaryLanguage = SECONDARYINTERNALRESOURCES.toLowerCase();

            InputStream primaryStream = null;
            InputStream secondaryStream = null;

            primaryLanguage = "en";
            secondaryLanguage = "se";
            try {
                primaryStream = new FileInputStream(
                        "src/intresources/intresources." + primaryLanguage + ".properties");
                secondaryStream = new FileInputStream(
                        "src/intresources/intresources." + secondaryLanguage + ".properties");

                try {
                    primaryEjbcaResource.load(primaryStream);
                    secondaryEjbcaResource.load(secondaryStream);
                } catch (IOException e) {
                    log.error("Error reading internal resourcefile", e);
                }

            } catch (FileNotFoundException e) {
                log.error("Localization files not found", e);

            } finally {
                try {
                    if (primaryStream != null) {
                        primaryStream.close();
                    }
                    if (secondaryStream != null) {
                        secondaryStream.close();
                    }
                } catch (IOException e) {
                    log.error("Error closing internal resources language streams: ", e);
                }
            }

        }

        public static synchronized InternalEjbcaResources getInstance() {
            if (instance == null) {
                instance = new InternalResourcesStub();
            }
            return instance;
        }

    }
}