org.ejbca.core.protocol.cmp.CmpRAUnidTest.java Source code

Java tutorial

Introduction

Here is the source code for org.ejbca.core.protocol.cmp.CmpRAUnidTest.java

Source

/*************************************************************************
 *                                                                       *
 *  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.cmp;

import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.security.KeyPair;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Iterator;
import java.util.Vector;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.bouncycastle.asn1.DERObjectIdentifier;
import org.bouncycastle.asn1.DEROutputStream;
import org.bouncycastle.asn1.x509.X509Name;
import org.bouncycastle.jce.X509Principal;
import org.cesecore.core.ejb.ca.store.CertificateProfileSessionRemote;
import org.cesecore.core.ejb.ra.raadmin.EndEntityProfileSessionRemote;
import org.ejbca.config.CmpConfiguration;
import org.ejbca.core.ejb.ca.caadmin.CAAdminSessionRemote;
import org.ejbca.core.ejb.ca.caadmin.CaSessionRemote;
import org.ejbca.core.ejb.config.ConfigurationSessionRemote;
import org.ejbca.core.model.AlgorithmConstants;
import org.ejbca.core.model.ca.caadmin.CAInfo;
import org.ejbca.core.model.ca.certificateprofiles.CertificateProfile;
import org.ejbca.core.model.ca.certificateprofiles.CertificateProfileExistsException;
import org.ejbca.core.model.ca.certificateprofiles.EndUserCertificateProfile;
import org.ejbca.core.model.log.Admin;
import org.ejbca.core.model.ra.raadmin.EndEntityProfile;
import org.ejbca.core.model.ra.raadmin.EndEntityProfileExistsException;
import org.ejbca.core.protocol.unid.UnidFnrHandler;
import org.ejbca.util.CertTools;
import org.ejbca.util.CryptoProviderTools;
import org.ejbca.util.InterfaceCache;
import org.ejbca.util.keystore.KeyTools;

import com.novosec.pkix.asn1.cmp.PKIMessage;

/**
 * Tests the unid-fnr plugin.
 * Read the assert printout {@link #test01()} to understand how to set things up for the test.
 * 
 * @author primelars
 * @version $Id: CmpRAUnidTest.java 12052 2011-05-21 07:29:28Z anatom $
 */
public class CmpRAUnidTest extends CmpTestCase {

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

    private static final String PBEPASSWORD = "password";
    private static final String UNIDPREFIX = "1234-5678-";
    private static final String CPNAME = UNIDPREFIX + CmpRAUnidTest.class.getName();
    private static final String EEPNAME = UNIDPREFIX + CmpRAUnidTest.class.getName();

    /**
     * SUBJECT_DN of user used in this test, this contains special, escaped,
     * characters to test that this works with CMP RA operations
     */
    private static final String FNR = "90123456789";
    private static final String LRA = "01234";
    private static final String SUBJECT_SN = FNR + '-' + LRA;
    private static final String SUBJECT_DN = "C=SE,SN=" + SUBJECT_SN + ",CN=unid-frn";

    private final String issuerDN;
    private final KeyPair keys;

    private final int caid;
    private final Admin admin = new Admin(Admin.TYPE_BATCHCOMMANDLINE_USER);
    private final X509Certificate cacert;

    private final CAAdminSessionRemote caAdminSession = InterfaceCache.getCAAdminSession();
    private final CaSessionRemote caSession = InterfaceCache.getCaSession();
    private final CertificateProfileSessionRemote certificateProfileSession = InterfaceCache
            .getCertificateProfileSession();
    private final ConfigurationSessionRemote configurationSession = InterfaceCache.getConfigurationSession();
    private final EndEntityProfileSessionRemote endEntityProfileSession = InterfaceCache
            .getEndEntityProfileSession();

    public CmpRAUnidTest(String arg0) throws Exception {
        super(arg0);
        CryptoProviderTools.installBCProvider();
        // Try to use AdminCA1 if it exists
        final CAInfo adminca1 = this.caAdminSession.getCAInfo(this.admin, "AdminCA1");
        if (adminca1 == null) {
            final Collection<Integer> caids = this.caSession.getAvailableCAs(this.admin);
            final Iterator<Integer> iter = caids.iterator();
            int tmp = 0;
            while (iter.hasNext()) {
                tmp = iter.next().intValue();
                if (tmp != 0) {
                    break;
                }
            }
            this.caid = tmp;
        } else {
            this.caid = adminca1.getCAId();
        }
        if (this.caid == 0) {
            assertTrue("No active CA! Must have at least one active CA to run tests!", false);
        }
        final CAInfo cainfo = this.caAdminSession.getCAInfo(this.admin, this.caid);
        final Collection<Certificate> certs = cainfo.getCertificateChain();
        if (certs.size() > 0) {
            final Iterator<Certificate> certiter = certs.iterator();
            final Certificate cert = certiter.next();
            final String subject = CertTools.getSubjectDN(cert);
            if (StringUtils.equals(subject, cainfo.getSubjectDN())) {
                // Make sure we have a BC certificate
                this.cacert = (X509Certificate) CertTools.getCertfromByteArray(cert.getEncoded());
            } else {
                this.cacert = null;
            }
        } else {
            this.cacert = null;
            log.error("NO CACERT for caid " + this.caid);
        }
        this.issuerDN = this.cacert.getIssuerDN().getName();
        // Configure CMP for this test
        updatePropertyOnServer(CmpConfiguration.CONFIG_OPERATIONMODE, "ra");
        updatePropertyOnServer(CmpConfiguration.CONFIG_ALLOWRAVERIFYPOPO, "true");
        updatePropertyOnServer(CmpConfiguration.CONFIG_RESPONSEPROTECTION, "pbe");
        updatePropertyOnServer(CmpConfiguration.CONFIG_RA_AUTHENTICATIONSECRET, PBEPASSWORD);
        updatePropertyOnServer(CmpConfiguration.CONFIG_RA_CERTIFICATEPROFILE, "KeyId");
        updatePropertyOnServer(CmpConfiguration.CONFIG_RA_ENDENTITYPROFILE, "KeyId");
        updatePropertyOnServer(CmpConfiguration.CONFIG_RACANAME, cainfo.getName());
        updatePropertyOnServer(CmpConfiguration.CONFIG_CERTREQHANDLER_CLASS, UnidFnrHandler.class.getName());
        // Configure a Certificate profile (CmpRA) using ENDUSER as template
        if (this.certificateProfileSession.getCertificateProfile(this.admin, CPNAME) == null) {
            final CertificateProfile cp = new EndUserCertificateProfile();
            try { // TODO: Fix this better
                this.certificateProfileSession.addCertificateProfile(this.admin, CPNAME, cp);
            } catch (CertificateProfileExistsException e) {
                log.error("Certificate profile exists: ", e);
            }
        }
        final int cpId = this.certificateProfileSession.getCertificateProfileId(this.admin, CPNAME);
        if (this.endEntityProfileSession.getEndEntityProfile(this.admin, EEPNAME) == null) {
            final EndEntityProfile eep = new EndEntityProfile(true);
            eep.setValue(EndEntityProfile.AVAILCERTPROFILES, 0, "" + cpId);
            try {
                this.endEntityProfileSession.addEndEntityProfile(this.admin, EEPNAME, eep);
            } catch (EndEntityProfileExistsException e) {
                log.error("Could not create end entity profile.", e);
            }
        }
        this.keys = KeyTools.genKeys("512", AlgorithmConstants.KEYALGORITHM_RSA);
    }

    @Override
    public void setUp() throws Exception {
        super.setUp();
    }

    @Override
    public void tearDown() throws Exception {
        super.tearDown();
    }

    @Override
    protected void checkDN(String sExpected, X509Name actual) {
        final X509Name expected = new X509Name(sExpected);
        final Vector<DERObjectIdentifier> expectedOIDs = expected.getOIDs();
        final Vector<String> expectedValues = expected.getValues();
        final Vector<DERObjectIdentifier> actualOIDs = actual.getOIDs();
        final Vector<String> actualValues = actual.getValues();
        assertEquals("Not the expected number of elements in the created certificate.", expectedOIDs.size(),
                actualOIDs.size());
        for (int i = 0; i < expectedOIDs.size(); i++) {
            final DERObjectIdentifier oid = expectedOIDs.get(i);
            final int j = actualOIDs.indexOf(oid);
            if (!oid.equals(X509Name.SN)) {
                log.debug("Check that " + oid.getId() + " is OK. Expected '" + expectedValues.get(i) + "'. Actual '"
                        + actualValues.get(j) + "'.");
                assertEquals("Not expected " + oid, expectedValues.get(i), actualValues.get(j));
                continue;
            }
            log.debug("Special handling of the SN " + oid.getId() + ". Input '" + expectedValues.get(i)
                    + "'. Transformed '" + actualValues.get(j) + "'.");
            final String expectedSNPrefix = UNIDPREFIX + LRA;
            final String actualSNPrefix = actualValues.get(j).substring(0, expectedSNPrefix.length());
            assertEquals("New serial number prefix not as expected.", expectedSNPrefix, actualSNPrefix);
            final String actualSNRandom = actualValues.get(j).substring(expectedSNPrefix.length());
            assertTrue("Random in serial number not OK: " + actualSNRandom,
                    Pattern.compile("^\\w{6}$").matcher(actualSNRandom).matches());
        }
    }

    public void test01() throws Exception {
        final Connection connection;
        final String host = "localhost";
        final String user = "uniduser";
        final String pass = "unidpass";
        final String name = "unid";
        try {
            connection = DriverManager.getConnection("jdbc:mysql://" + host + ":3306/" + name, user, pass);
        } catch (SQLException e) {
            final StringWriter sw = new StringWriter();
            final PrintWriter pw = new PrintWriter(sw);
            pw.println();
            pw.println("You have not set up a unid-fnr DB properly to run the test.");
            pw.println(
                    "If you don't bother about it (don't if you don't know what it is) please just ignore this error.");
            pw.println("But if you want to run the test please make sure that the mysql unid-fnr DB is set up.");
            pw.println("Then execute next line at the mysql prompt:");
            pw.println("mysql> grant all on " + name + ".* to " + user + "@'" + host + "' identified by '" + pass
                    + "';");
            pw.println("And then create the DB:");
            pw.println("$ mysqladmin -u" + host + " -u" + user + " -p" + pass + " create " + name + ";.");
            pw.println(
                    "These properties must the also be defined for the jboss data source. The name of the DS must be set in cmp.properties. Not that the datasource must be a 'no-tx-datasource', like OcspDS.");
            pw.println(
                    "You also have to set the path to the 'mysql.jar' as the 'mysql.lib' system property for the test.");
            pw.println("Example how to the test with this property:");
            pw.println("ant -Dmysql.lib=/usr/share/java/mysql.jar test:run");
            log.error(sw, e);
            assertTrue(sw.toString(), false);
            return;
        }
        try {
            doTest(connection);
        } finally {
            connection.close();
        }
    }

    private void doTest(Connection dbConn) throws Exception {

        final byte[] nonce = CmpMessageHelper.createSenderNonce();
        final byte[] transid = CmpMessageHelper.createSenderNonce();
        final int reqId;
        final String unid;
        {
            // In this test SUBJECT_DN contains special, escaped characters to verify
            // that that works with CMP RA as well
            final PKIMessage one = genCertReq(this.issuerDN, SUBJECT_DN, this.keys, this.cacert, nonce, transid,
                    true, null, null, null, null);
            final PKIMessage req = protectPKIMessage(one, false, PBEPASSWORD, CPNAME, 567);
            assertNotNull(req);

            reqId = req.getBody().getIr().getCertReqMsg(0).getCertReq().getCertReqId().getValue().intValue();
            final ByteArrayOutputStream bao = new ByteArrayOutputStream();
            final DEROutputStream out = new DEROutputStream(bao);
            out.writeObject(req);
            final byte[] ba = bao.toByteArray();
            // Send request and receive response
            final byte[] resp = sendCmpHttp(ba, 200);
            checkCmpResponseGeneral(resp, this.issuerDN, SUBJECT_DN, this.cacert, nonce, transid, false,
                    PBEPASSWORD);
            final X509Certificate cert = checkCmpCertRepMessage(SUBJECT_DN, this.cacert, resp, reqId);
            unid = (String) new X509Principal(cert.getSubjectX500Principal().getEncoded()).getValues(X509Name.SN)
                    .get(0);
            log.debug("Unid: " + unid);
        }
        {
            final PreparedStatement ps = dbConn.prepareStatement("select fnr from UnidFnrMapping where unid=?");
            ps.setString(1, unid);
            final ResultSet result = ps.executeQuery();
            assertTrue("Unid '" + unid + "' not found in DB.", result.next());
            final String fnr = result.getString(1);
            log.debug("FNR read from DB: " + fnr);
            assertEquals("Right FNR not found in DB.", FNR, fnr);
        }
        {
            // Send a confirm message to the CA
            final String hash = "foo123";
            final PKIMessage confirm = genCertConfirm(SUBJECT_DN, this.cacert, nonce, transid, hash, reqId);
            assertNotNull(confirm);
            final PKIMessage req1 = protectPKIMessage(confirm, false, PBEPASSWORD, 567);
            final ByteArrayOutputStream bao = new ByteArrayOutputStream();
            final DEROutputStream out = new DEROutputStream(bao);
            out.writeObject(req1);
            final byte[] ba = bao.toByteArray();
            // Send request and receive response
            final byte[] resp = sendCmpHttp(ba, 200);
            checkCmpResponseGeneral(resp, this.issuerDN, SUBJECT_DN, this.cacert, nonce, transid, false,
                    PBEPASSWORD);
            checkCmpPKIConfirmMessage(SUBJECT_DN, this.cacert, resp);
        }
    }

    public void testZZZCleanUp() throws Exception {
        log.trace(">testZZZCleanUp");
        this.endEntityProfileSession.removeEndEntityProfile(this.admin, EEPNAME);
        this.certificateProfileSession.removeCertificateProfile(this.admin, CPNAME);
        assertTrue("Unable to clean up properly.", this.configurationSession.restoreConfiguration());
        log.trace("<testZZZCleanUp");
    }
}