org.signserver.server.cryptotokens.P11SignTest.java Source code

Java tutorial

Introduction

Here is the source code for org.signserver.server.cryptotokens.P11SignTest.java

Source

/*************************************************************************
 *                                                                       *
 *  SignServer: The OpenSource Automated Signing Server                  *
 *                                                                       *
 *  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.signserver.server.cryptotokens;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.cert.Certificate;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.fail;
import org.apache.log4j.Logger;
import org.bouncycastle.asn1.cmp.PKIStatus;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.tsp.TSPAlgorithms;
import org.bouncycastle.tsp.TimeStampRequest;
import org.bouncycastle.tsp.TimeStampRequestGenerator;
import org.bouncycastle.tsp.TimeStampResponse;
import org.bouncycastle.util.Store;
import org.bouncycastle.util.encoders.Base64;
import org.signserver.common.Base64SignerCertReqData;
import org.signserver.common.CryptoTokenOfflineException;
import org.signserver.common.GenericSignRequest;
import org.signserver.common.GenericSignResponse;
import org.signserver.common.GlobalConfiguration;
import org.signserver.common.KeyTestResult;
import org.signserver.common.PKCS10CertReqInfo;
import org.signserver.common.RequestContext;
import org.signserver.common.SODSignRequest;
import org.signserver.common.SODSignResponse;
import org.signserver.common.SignServerException;
import org.signserver.common.SignServerUtil;
import org.signserver.common.TokenOutOfSpaceException;
import org.signserver.common.util.PathUtil;
import org.signserver.ejb.interfaces.IGlobalConfigurationSession;
import org.signserver.ejb.interfaces.IWorkerSession;
import org.signserver.test.utils.builders.CryptoUtils;
import org.signserver.testutils.ModulesTestCase;

/**
 * Test signing with all signers using a PKCS11CryptoToken.
 *
 * @author Markus Kils
 * @version $Id: P11SignTest.java 5993 2015-04-10 11:23:55Z netmackan $
 */
public class P11SignTest extends ModulesTestCase {

    /** Logger for this class. */
    private static final Logger LOG = Logger.getLogger(P11SignTest.class);

    private static final int CRYPTO_TOKEN = 20100;
    private static final int WORKER_PDF = 20000;
    private static final int WORKER_TSA = 20001;
    private static final int WORKER_SOD = 20002;
    private static final int WORKER_CMS = 20003;
    private static final int WORKER_XML = 20004;
    private static final int WORKER_XML2 = 20014;
    private static final int WORKER_ODF = 20005;
    private static final int WORKER_OOXML = 20006;
    private static final int WORKER_MSA = 20007;

    private static final String MSAUTHCODE_REQUEST_DATA = "MIIBIwYKKwYBBAGCNwMCATCCARMGCSqGSIb3DQEHAaCCAQQEggEAVVSpOKf9zJYc"
            + "tyvqgeHfO9JkobPYihUZcW9TbYzAUiJGEsElNCnLUaO0+MZG0TS7hlzqKKvrdXc7"
            + "O/8C7c8YyjYF5YrLiaYS8cw3VbaQ2M1NWsLGzxF1pxsR9sMDJvfrryPaWj4eTi3Y"
            + "UqRNS+GTa4quX4xbmB0KqMpCtrvuk4S9cgaJGwxmSE7N3omzvERTUxp7nVSHtms5"
            + "lVMb082JFlABT1/o2mL5O6qFG119JeuS1+ZiL1AEy//gRs556OE1TB9UEQU2bFUm"
            + "zBD4VHvkOOB/7X944v9lmK5y9sFv+vnf/34catL1A+ZNLwtd1Qq2VirqJxRK/T61" + "QoSWj4rGpw==";

    private static final String TEST_KEY_ALIAS = "p11testkey1234";
    private static final String TEST_KEY_ALIAS_2 = "somekey123";
    private static final String CRYPTO_TOKEN_NAME = "TestCryptoTokenP11";

    private final String sharedLibraryName;
    private final String sharedLibraryPath;
    private final String slot;
    private final String pin;
    private final String existingKey1;

    private final File pdfSampleFile;
    private final File odfSampleFile;
    private final File ooxmlSampleFile;

    private final IWorkerSession workerSession = getWorkerSession();
    private final IGlobalConfigurationSession globalSession = getGlobalSession();

    public P11SignTest() throws FileNotFoundException {
        final File home = PathUtil.getAppHome();
        pdfSampleFile = new File(home, "res/test/pdf/sample.pdf");
        odfSampleFile = new File(home, "res/signingtest/input/test.odt");
        ooxmlSampleFile = new File(home, "res/signingtest/input/test.docx");
        sharedLibraryName = getConfig().getProperty("test.p11.sharedLibraryName");
        sharedLibraryPath = getConfig().getProperty("test.p11.sharedLibraryPath");
        slot = getConfig().getProperty("test.p11.slot");
        pin = getConfig().getProperty("test.p11.pin");
        existingKey1 = getConfig().getProperty("test.p11.existingkey1");
    }

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        SignServerUtil.installBCProvider();
    }

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

    private void setupCryptoTokenProperties(final int tokenId, final boolean cache) throws Exception {
        // Setup token
        globalSession.setProperty(GlobalConfiguration.SCOPE_GLOBAL, "WORKER" + tokenId + ".CLASSPATH",
                "org.signserver.server.signers.CryptoWorker");
        globalSession.setProperty(GlobalConfiguration.SCOPE_GLOBAL, "WORKER" + tokenId + ".SIGNERTOKEN.CLASSPATH",
                PKCS11CryptoToken.class.getName());
        workerSession.setWorkerProperty(tokenId, "NAME", CRYPTO_TOKEN_NAME);
        workerSession.setWorkerProperty(tokenId, "SHAREDLIBRARYNAME", sharedLibraryName);
        workerSession.setWorkerProperty(tokenId, "SLOT", slot);
        workerSession.setWorkerProperty(tokenId, "PIN", pin);
        workerSession.setWorkerProperty(tokenId, "DEFAULTKEY", existingKey1); // Test key
        workerSession.setWorkerProperty(tokenId, "CACHE_PRIVATEKEY", String.valueOf(cache));
    }

    private void setPDFSignerOnlyProperties(final int workerId) throws Exception {
        // Setup worker
        globalSession.setProperty(GlobalConfiguration.SCOPE_GLOBAL, "WORKER" + workerId + ".CLASSPATH",
                "org.signserver.module.pdfsigner.PDFSigner");
        workerSession.setWorkerProperty(workerId, "NAME", "PDFSignerP11");
        workerSession.setWorkerProperty(workerId, "AUTHTYPE", "NOAUTH");
        workerSession.setWorkerProperty(workerId, "CRYPTOTOKEN", CRYPTO_TOKEN_NAME);
        workerSession.setWorkerProperty(workerId, "DEFAULTKEY", existingKey1);
    }

    private void setPDFSignerWithCryptoProperties(final int workerId, final boolean cache) throws Exception {
        // Setup worker
        globalSession.setProperty(GlobalConfiguration.SCOPE_GLOBAL, "WORKER" + workerId + ".CLASSPATH",
                "org.signserver.module.pdfsigner.PDFSigner");
        globalSession.setProperty(GlobalConfiguration.SCOPE_GLOBAL, "WORKER" + workerId + ".SIGNERTOKEN.CLASSPATH",
                PKCS11CryptoToken.class.getName());
        workerSession.setWorkerProperty(workerId, "NAME", "PDFSignerP11");
        workerSession.setWorkerProperty(workerId, "AUTHTYPE", "NOAUTH");
        workerSession.setWorkerProperty(workerId, "SHAREDLIBRARYNAME", sharedLibraryName);
        workerSession.setWorkerProperty(workerId, "SLOT", slot);
        workerSession.setWorkerProperty(workerId, "PIN", pin);
        workerSession.setWorkerProperty(workerId, "DEFAULTKEY", existingKey1);
        workerSession.setWorkerProperty(workerId, "CACHE_PRIVATEKEY", String.valueOf(cache));
    }

    /** Tests that the getCertificateRequest method generates a request. */
    public void testGenerateCSR() throws Exception {
        try {
            setPDFSignerWithCryptoProperties(WORKER_PDF, false);
            workerSession.reloadConfiguration(WORKER_PDF);

            // Tests generating a CSR
            PKCS10CertReqInfo certReqInfo = new PKCS10CertReqInfo("SHA1WithRSA", "CN=Worker" + WORKER_PDF, null);
            Base64SignerCertReqData csr = (Base64SignerCertReqData) getWorkerSession()
                    .getCertificateRequest(WORKER_PDF, certReqInfo, false);
            assertNotNull(csr);
            assertNotNull(csr.getBase64CertReq());
            assertTrue(csr.getBase64CertReq().length > 0);

            // Test for an non-existing key label
            setPDFSignerWithCryptoProperties(WORKER_PDF, false);
            workerSession.setWorkerProperty(WORKER_PDF, "DEFAULTKEY", "NON-EXISTING-KEY-LABEL");
            workerSession.reloadConfiguration(WORKER_PDF);
            try {
                certReqInfo = new PKCS10CertReqInfo("SHA1WithRSA", "CN=Worker" + WORKER_PDF, null);
                getWorkerSession().getCertificateRequest(WORKER_PDF, certReqInfo, false);
                fail("Should have thrown exception as the DEFAULTKEY does not exist");
            } catch (CryptoTokenOfflineException ok) { // NOPMD
                // OK
            }
        } finally {
            removeWorker(WORKER_PDF);
        }
    }

    /** Tests that the getCertificateRequest method generates a request. */
    public void testGenerateCSR_separateToken() throws Exception {
        try {
            setupCryptoTokenProperties(CRYPTO_TOKEN, false);
            setPDFSignerOnlyProperties(WORKER_PDF);
            workerSession.reloadConfiguration(CRYPTO_TOKEN);
            workerSession.reloadConfiguration(WORKER_PDF);

            // Tests generating a CSR
            PKCS10CertReqInfo certReqInfo = new PKCS10CertReqInfo("SHA1WithRSA", "CN=Worker" + WORKER_PDF, null);
            Base64SignerCertReqData csr = (Base64SignerCertReqData) getWorkerSession()
                    .getCertificateRequest(WORKER_PDF, certReqInfo, false);
            assertNotNull(csr);
            assertNotNull(csr.getBase64CertReq());
            assertTrue(csr.getBase64CertReq().length > 0);

            // Test for an non-existing key label
            workerSession.setWorkerProperty(WORKER_PDF, "DEFAULTKEY", "NON-EXISTING-KEY-LABEL");
            workerSession.reloadConfiguration(WORKER_PDF);
            try {
                certReqInfo = new PKCS10CertReqInfo("SHA1WithRSA", "CN=Worker" + WORKER_PDF, null);
                getWorkerSession().getCertificateRequest(WORKER_PDF, certReqInfo, false);
                fail("Should have thrown exception as the DEFAULTKEY does not exist");
            } catch (CryptoTokenOfflineException ok) { // NOPMD
                // OK
            }
        } finally {
            removeWorker(CRYPTO_TOKEN);
            removeWorker(WORKER_PDF);
        }
    }

    /**
     * Tests setting up a PDF Signer, giving it a certificate and sign a document.
     */
    public void testPDFSigner_uncached() throws Exception {
        try {
            setPDFSignerWithCryptoProperties(WORKER_PDF, false);
            workerSession.reloadConfiguration(WORKER_PDF);

            pdfSignerTest();
        } finally {
            removeWorker(WORKER_PDF);
        }
    }

    /**
     * Tests setting up a PDF Signer, giving it a certificate and sign a document.
     */
    public void testPDFSigner_uncached_separateToken() throws Exception {
        try {
            setupCryptoTokenProperties(CRYPTO_TOKEN, false);
            setPDFSignerOnlyProperties(WORKER_PDF);
            workerSession.reloadConfiguration(CRYPTO_TOKEN);
            workerSession.reloadConfiguration(WORKER_PDF);

            pdfSignerTest();
        } finally {
            removeWorker(CRYPTO_TOKEN);
            removeWorker(WORKER_PDF);
        }
    }

    /**
     * Tests setting up a PDF Signer, giving it a certificate and sign a document.
     */
    public void testPDFSigner_cached() throws Exception {
        try {
            setPDFSignerWithCryptoProperties(WORKER_PDF, true);
            workerSession.reloadConfiguration(WORKER_PDF);

            pdfSignerTest();
        } finally {
            removeWorker(WORKER_PDF);
        }
    }

    /**
     * Tests setting up a PDF Signer, giving it a certificate and sign a document.
     */
    public void testPDFSigner_cached_separateToken() throws Exception {
        try {
            setupCryptoTokenProperties(CRYPTO_TOKEN, true);
            setPDFSignerOnlyProperties(WORKER_PDF);
            workerSession.reloadConfiguration(CRYPTO_TOKEN);
            workerSession.reloadConfiguration(WORKER_PDF);

            pdfSignerTest();
        } finally {
            removeWorker(CRYPTO_TOKEN);
            removeWorker(WORKER_PDF);
        }
    }

    private void pdfSignerTest() throws Exception {
        // Generate CSR
        PKCS10CertReqInfo certReqInfo = new PKCS10CertReqInfo("SHA1WithRSA", "CN=Worker" + WORKER_PDF, null);
        Base64SignerCertReqData reqData = (Base64SignerCertReqData) getWorkerSession()
                .getCertificateRequest(WORKER_PDF, certReqInfo, false);

        // Issue certificate
        PKCS10CertificationRequest csr = new PKCS10CertificationRequest(Base64.decode(reqData.getBase64CertReq()));
        KeyPair issuerKeyPair = CryptoUtils.generateRSA(512);
        X509CertificateHolder cert = new X509v3CertificateBuilder(new X500Name("CN=TestP11 Issuer"), BigInteger.ONE,
                new Date(), new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(365)), csr.getSubject(),
                csr.getSubjectPublicKeyInfo())
                        .build(new JcaContentSignerBuilder("SHA256WithRSA").setProvider("BC")
                                .build(issuerKeyPair.getPrivate()));

        // Install certificate and chain
        workerSession.uploadSignerCertificate(WORKER_PDF, cert.getEncoded(), GlobalConfiguration.SCOPE_GLOBAL);
        workerSession.uploadSignerCertificateChain(WORKER_PDF, Arrays.asList(cert.getEncoded()),
                GlobalConfiguration.SCOPE_GLOBAL);
        workerSession.reloadConfiguration(WORKER_PDF);

        // Test active
        List<String> errors = workerSession.getStatus(WORKER_PDF).getFatalErrors();
        assertEquals("errors: " + errors, 0, errors.size());

        // Test signing
        signGenericDocument(WORKER_PDF, readFile(pdfSampleFile));
    }

    private byte[] readFile(File file) throws IOException {
        BufferedInputStream in = new BufferedInputStream(new FileInputStream(file));
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        int b;
        while ((b = in.read()) != -1) {
            bout.write(b);
        }
        return bout.toByteArray();
    }

    private void setTimeStampSignerProperties(final int workerId, final boolean cache) throws IOException {
        // Setup worker
        globalSession.setProperty(GlobalConfiguration.SCOPE_GLOBAL, "WORKER" + workerId + ".CLASSPATH",
                "org.signserver.module.tsa.TimeStampSigner");
        globalSession.setProperty(GlobalConfiguration.SCOPE_GLOBAL, "WORKER" + workerId + ".SIGNERTOKEN.CLASSPATH",
                PKCS11CryptoToken.class.getName());
        workerSession.setWorkerProperty(workerId, "NAME", "TSSignerP11");
        workerSession.setWorkerProperty(workerId, "AUTHTYPE", "NOAUTH");
        workerSession.setWorkerProperty(workerId, "SHAREDLIBRARYNAME", sharedLibraryName);
        workerSession.setWorkerProperty(workerId, "SLOT", slot);
        workerSession.setWorkerProperty(workerId, "PIN", pin);
        workerSession.setWorkerProperty(workerId, "DEFAULTKEY", existingKey1);
        workerSession.setWorkerProperty(workerId, "DEFAULTTSAPOLICYOID", "1.2.3");
        workerSession.setWorkerProperty(workerId, "CACHE_PRIVATEKEY", String.valueOf(cache));
    }

    /**
     * Tests setting up a TimeStamp Signer, giving it a certificate and request a time-stamp token.
     */
    public void testTSSigner_uncached() throws Exception {
        try {
            setTimeStampSignerProperties(WORKER_TSA, false);
            workerSession.reloadConfiguration(WORKER_TSA);
            tsSigner();
        } finally {
            removeWorker(WORKER_TSA);
        }
    }

    public void testTSSigner_cached() throws Exception {
        try {
            setTimeStampSignerProperties(WORKER_TSA, true);
            workerSession.reloadConfiguration(WORKER_TSA);
            tsSigner();
        } finally {
            removeWorker(WORKER_TSA);
        }
    }

    private void tsSigner() throws Exception {
        // Generate CSR
        PKCS10CertReqInfo certReqInfo = new PKCS10CertReqInfo("SHA1WithRSA", "CN=Worker" + WORKER_TSA, null);
        Base64SignerCertReqData reqData = (Base64SignerCertReqData) getWorkerSession()
                .getCertificateRequest(WORKER_TSA, certReqInfo, false);

        // Issue certificate
        PKCS10CertificationRequest csr = new PKCS10CertificationRequest(Base64.decode(reqData.getBase64CertReq()));
        KeyPair issuerKeyPair = CryptoUtils.generateRSA(512);
        X509CertificateHolder cert = new X509v3CertificateBuilder(new X500Name("CN=TestP11 Issuer"), BigInteger.ONE,
                new Date(), new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(365)), csr.getSubject(),
                csr.getSubjectPublicKeyInfo())
                        .addExtension(org.bouncycastle.asn1.x509.X509Extension.extendedKeyUsage, true,
                                new ExtendedKeyUsage(KeyPurposeId.id_kp_timeStamping))
                        .build(new JcaContentSignerBuilder("SHA256WithRSA").setProvider("BC")
                                .build(issuerKeyPair.getPrivate()));

        // Install certificate and chain
        workerSession.uploadSignerCertificate(WORKER_TSA, cert.getEncoded(), GlobalConfiguration.SCOPE_GLOBAL);
        workerSession.uploadSignerCertificateChain(WORKER_TSA, Arrays.asList(cert.getEncoded()),
                GlobalConfiguration.SCOPE_GLOBAL);
        workerSession.reloadConfiguration(WORKER_TSA);

        // Test active
        List<String> errors = workerSession.getStatus(WORKER_TSA).getFatalErrors();
        assertEquals("errors: " + errors, 0, errors.size());

        // Test signing
        TimeStampRequestGenerator timeStampRequestGenerator = new TimeStampRequestGenerator();
        TimeStampRequest timeStampRequest = timeStampRequestGenerator.generate(TSPAlgorithms.SHA1, new byte[20],
                BigInteger.valueOf(100));
        byte[] requestBytes = timeStampRequest.getEncoded();
        GenericSignRequest signRequest = new GenericSignRequest(567, requestBytes);
        final GenericSignResponse res = (GenericSignResponse) workerSession.process(WORKER_TSA, signRequest,
                new RequestContext());
        Certificate signercert = res.getSignerCertificate();
        assertNotNull(signercert);
        final TimeStampResponse timeStampResponse = new TimeStampResponse((byte[]) res.getProcessedData());
        timeStampResponse.validate(timeStampRequest);

        assertEquals("Token granted", PKIStatus.GRANTED, timeStampResponse.getStatus());
        assertNotNull("Got timestamp token", timeStampResponse.getTimeStampToken());
    }

    private void setMRTDSODSignerProperties(final int workerId, final boolean cache) throws IOException {
        // Setup worker
        globalSession.setProperty(GlobalConfiguration.SCOPE_GLOBAL, "WORKER" + workerId + ".CLASSPATH",
                "org.signserver.module.mrtdsodsigner.MRTDSODSigner");
        globalSession.setProperty(GlobalConfiguration.SCOPE_GLOBAL, "WORKER" + workerId + ".SIGNERTOKEN.CLASSPATH",
                PKCS11CryptoToken.class.getName());
        workerSession.setWorkerProperty(workerId, "NAME", "SODSignerP11");
        workerSession.setWorkerProperty(workerId, "AUTHTYPE", "NOAUTH");
        workerSession.setWorkerProperty(workerId, "SHAREDLIBRARYNAME", sharedLibraryName);
        workerSession.setWorkerProperty(workerId, "SLOT", slot);
        workerSession.setWorkerProperty(workerId, "PIN", pin);
        workerSession.setWorkerProperty(workerId, "DEFAULTKEY", existingKey1);
        workerSession.setWorkerProperty(workerId, "CACHE_PRIVATEKEY", String.valueOf(cache));
    }

    /**
     * Tests setting up a MRTD SOD Signer, giving it a certificate and requests an SOd.
     */
    public void testMRTDSODSigner_uncached() throws Exception {
        final int workerId = WORKER_SOD;
        try {
            setMRTDSODSignerProperties(workerId, false);
            workerSession.reloadConfiguration(workerId);

            mrtdsodSigner(workerId);
        } finally {
            removeWorker(workerId);
        }
    }

    public void testMRTDSODSigner_cached() throws Exception {
        final int workerId = WORKER_SOD;
        try {
            setMRTDSODSignerProperties(workerId, true);
            workerSession.reloadConfiguration(workerId);

            mrtdsodSigner(workerId);
        } finally {
            removeWorker(workerId);
        }
    }

    private void mrtdsodSigner(final int workerId) throws Exception {
        // Generate CSR
        PKCS10CertReqInfo certReqInfo = new PKCS10CertReqInfo("SHA1WithRSA", "CN=Worker" + workerId, null);
        Base64SignerCertReqData reqData = (Base64SignerCertReqData) getWorkerSession()
                .getCertificateRequest(workerId, certReqInfo, false);

        // Issue certificate
        PKCS10CertificationRequest csr = new PKCS10CertificationRequest(Base64.decode(reqData.getBase64CertReq()));
        KeyPair issuerKeyPair = CryptoUtils.generateRSA(512);
        X509CertificateHolder issuerCert = new JcaX509v3CertificateBuilder(new X500Name("CN=TestP11 Issuer"),
                BigInteger.ONE, new Date(), new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(365)),
                new X500Name("CN=TestP11 Issuer"), issuerKeyPair.getPublic())
                        .build(new JcaContentSignerBuilder("SHA256WithRSA").setProvider("BC")
                                .build(issuerKeyPair.getPrivate()));
        X509CertificateHolder cert = new X509v3CertificateBuilder(new X500Name("CN=TestP11 Issuer"), BigInteger.ONE,
                new Date(), new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(365)), csr.getSubject(),
                csr.getSubjectPublicKeyInfo())
                        .build(new JcaContentSignerBuilder("SHA256WithRSA").setProvider("BC")
                                .build(issuerKeyPair.getPrivate()));

        // Install certificate and chain
        workerSession.uploadSignerCertificate(workerId, cert.getEncoded(), GlobalConfiguration.SCOPE_GLOBAL);
        workerSession.uploadSignerCertificateChain(workerId,
                Arrays.asList(cert.getEncoded(), issuerCert.getEncoded()), GlobalConfiguration.SCOPE_GLOBAL);
        workerSession.reloadConfiguration(workerId);

        // Test active
        List<String> errors = workerSession.getStatus(workerId).getFatalErrors();
        assertEquals("errors: " + errors, 0, errors.size());

        // Test signing
        HashMap<Integer, byte[]> dgs = new HashMap<Integer, byte[]>();
        dgs.put(1, "Yy==".getBytes());
        dgs.put(2, "Yy==".getBytes());
        dgs.put(3, "Yy==".getBytes());
        final SODSignRequest signRequest = new SODSignRequest(233, dgs);
        final SODSignResponse res = (SODSignResponse) workerSession.process(workerId, signRequest,
                new RequestContext());
        Certificate signercert = res.getSignerCertificate();
        assertNotNull(signercert);
    }

    private void setCMSSignerProperties(final int workerId, final boolean cached) throws IOException {
        // Setup worker
        globalSession.setProperty(GlobalConfiguration.SCOPE_GLOBAL, "WORKER" + workerId + ".CLASSPATH",
                "org.signserver.module.cmssigner.CMSSigner");
        globalSession.setProperty(GlobalConfiguration.SCOPE_GLOBAL, "WORKER" + workerId + ".SIGNERTOKEN.CLASSPATH",
                PKCS11CryptoToken.class.getName());
        workerSession.setWorkerProperty(workerId, "NAME", "CMSSignerP11");
        workerSession.setWorkerProperty(workerId, "AUTHTYPE", "NOAUTH");
        workerSession.setWorkerProperty(workerId, "SHAREDLIBRARYNAME", sharedLibraryName);
        workerSession.setWorkerProperty(workerId, "SLOT", slot);
        workerSession.setWorkerProperty(workerId, "PIN", pin);
        workerSession.setWorkerProperty(workerId, "DEFAULTKEY", existingKey1);
        workerSession.setWorkerProperty(workerId, "CACHE_PRIVATEKEY", String.valueOf(cached));
    }

    /**
     * Tests setting up a CMS Signer, giving it a certificate and sign a file.
     */
    public void testCMSSigner_uncached() throws Exception {
        final int workerId = WORKER_CMS;
        try {
            setCMSSignerProperties(workerId, false);
            workerSession.reloadConfiguration(workerId);

            cmsSigner(workerId);
        } finally {
            removeWorker(workerId);
        }
    }

    public void testCMSSigner_cached() throws Exception {
        final int workerId = WORKER_CMS;
        try {
            setCMSSignerProperties(workerId, true);
            workerSession.reloadConfiguration(workerId);

            cmsSigner(workerId);
        } finally {
            removeWorker(workerId);
        }
    }

    private void cmsSigner(final int workerId) throws Exception {
        // Generate CSR
        PKCS10CertReqInfo certReqInfo = new PKCS10CertReqInfo("SHA1WithRSA", "CN=Worker" + workerId, null);
        Base64SignerCertReqData reqData = (Base64SignerCertReqData) getWorkerSession()
                .getCertificateRequest(workerId, certReqInfo, false);

        // Issue certificate
        PKCS10CertificationRequest csr = new PKCS10CertificationRequest(Base64.decode(reqData.getBase64CertReq()));
        KeyPair issuerKeyPair = CryptoUtils.generateRSA(512);
        X509CertificateHolder cert = new X509v3CertificateBuilder(new X500Name("CN=TestP11 Issuer"), BigInteger.ONE,
                new Date(), new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(365)), csr.getSubject(),
                csr.getSubjectPublicKeyInfo())
                        .build(new JcaContentSignerBuilder("SHA256WithRSA").setProvider("BC")
                                .build(issuerKeyPair.getPrivate()));

        // Install certificate and chain
        workerSession.uploadSignerCertificate(workerId, cert.getEncoded(), GlobalConfiguration.SCOPE_GLOBAL);
        workerSession.uploadSignerCertificateChain(workerId, Arrays.asList(cert.getEncoded()),
                GlobalConfiguration.SCOPE_GLOBAL);
        workerSession.reloadConfiguration(workerId);

        // Test active
        List<String> errors = workerSession.getStatus(workerId).getFatalErrors();
        assertEquals("errors: " + errors, 0, errors.size());

        // Test signing
        signGenericDocument(workerId, "Sample data".getBytes());
    }

    private void setXMLSignerProperties(final int workerId, final boolean cache) throws IOException {
        // Setup worker
        globalSession.setProperty(GlobalConfiguration.SCOPE_GLOBAL, "WORKER" + workerId + ".CLASSPATH",
                "org.signserver.module.xmlsigner.XMLSigner");
        globalSession.setProperty(GlobalConfiguration.SCOPE_GLOBAL, "WORKER" + workerId + ".SIGNERTOKEN.CLASSPATH",
                PKCS11CryptoToken.class.getName());
        workerSession.setWorkerProperty(workerId, "NAME", "XMLSignerP11");
        workerSession.setWorkerProperty(workerId, "AUTHTYPE", "NOAUTH");
        workerSession.setWorkerProperty(workerId, "SHAREDLIBRARYNAME", sharedLibraryName);
        workerSession.setWorkerProperty(workerId, "SLOT", slot);
        workerSession.setWorkerProperty(workerId, "PIN", pin);
        workerSession.setWorkerProperty(workerId, "DEFAULTKEY", existingKey1);
        workerSession.setWorkerProperty(workerId, "CACHE_PRIVATEKEY", String.valueOf(cache));
    }

    private void setXMLSignerPropertiesReferingToken(final int workerId, final String tokenName,
            final boolean cache) throws IOException {
        // Setup worker
        globalSession.setProperty(GlobalConfiguration.SCOPE_GLOBAL, "WORKER" + workerId + ".CLASSPATH",
                "org.signserver.module.xmlsigner.XMLSigner");
        workerSession.setWorkerProperty(workerId, "NAME", "XMLSignerRefering");
        workerSession.setWorkerProperty(workerId, "AUTHTYPE", "NOAUTH");
        workerSession.setWorkerProperty(workerId, "DEFAULTKEY", existingKey1);
        workerSession.setWorkerProperty(workerId, "CACHE_PRIVATEKEY", String.valueOf(cache));
        workerSession.setWorkerProperty(workerId, "CRYPTOTOKEN", tokenName);
    }

    /**
     * Tests setting up a XML Signer, giving it a certificate and sign a document.
     */
    public void testXMLSigner_uncached() throws Exception {
        final int workerId = WORKER_XML;
        try {
            setXMLSignerProperties(workerId, false);
            workerSession.reloadConfiguration(workerId);

            xmlSigner(workerId);
        } finally {
            removeWorker(workerId);
        }
    }

    public void testXMLSigner_cached() throws Exception {
        final int workerId = WORKER_XML;
        try {
            setXMLSignerProperties(workerId, true);
            workerSession.reloadConfiguration(workerId);

            xmlSigner(workerId);
        } finally {
            removeWorker(workerId);
        }
    }

    /**
     * Exercises a signer using a separate token and where the private key is
     * cached (in the worker).
     * @throws Exception
     */
    public void testXMLSigner_cached_separateToken() throws Exception {
        final int workerId = WORKER_XML2;
        try {
            setupCryptoTokenProperties(CRYPTO_TOKEN, false);
            workerSession.reloadConfiguration(CRYPTO_TOKEN);

            setXMLSignerPropertiesReferingToken(workerId, CRYPTO_TOKEN_NAME, true);
            workerSession.reloadConfiguration(workerId);

            xmlSigner(workerId);
        } finally {
            removeWorker(workerId);
        }
    }

    private void xmlSigner(final int workerId) throws Exception {
        // Generate CSR
        PKCS10CertReqInfo certReqInfo = new PKCS10CertReqInfo("SHA1WithRSA", "CN=Worker" + workerId, null);
        Base64SignerCertReqData reqData = (Base64SignerCertReqData) getWorkerSession()
                .getCertificateRequest(workerId, certReqInfo, false);

        // Issue certificate
        PKCS10CertificationRequest csr = new PKCS10CertificationRequest(Base64.decode(reqData.getBase64CertReq()));
        KeyPair issuerKeyPair = CryptoUtils.generateRSA(512);
        X509CertificateHolder cert = new X509v3CertificateBuilder(new X500Name("CN=TestP11 Issuer"), BigInteger.ONE,
                new Date(), new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(365)), csr.getSubject(),
                csr.getSubjectPublicKeyInfo())
                        .build(new JcaContentSignerBuilder("SHA256WithRSA").setProvider("BC")
                                .build(issuerKeyPair.getPrivate()));

        // Install certificate and chain
        workerSession.uploadSignerCertificate(workerId, cert.getEncoded(), GlobalConfiguration.SCOPE_GLOBAL);
        workerSession.uploadSignerCertificateChain(workerId, Arrays.asList(cert.getEncoded()),
                GlobalConfiguration.SCOPE_GLOBAL);
        workerSession.reloadConfiguration(workerId);

        // Test active
        List<String> errors = workerSession.getStatus(workerId).getFatalErrors();
        assertEquals("errors: " + errors, 0, errors.size());

        // Test signing
        signGenericDocument(workerId, "<sampledata/>".getBytes());

        // Test removing the DEFAULTKEY property, should result in a CryptoTokenOfflineException
        workerSession.removeWorkerProperty(workerId, "DEFAULTKEY");
        workerSession.reloadConfiguration(workerId);

        try {
            signGenericDocument(workerId, "<sampledata/>".getBytes());
            fail("Should throw a CryptoTokenOfflineException");
        } catch (CryptoTokenOfflineException e) {
            // expected
        }
    }

    private void setODFSignerProperties(final int workerId, final boolean cache) throws IOException {
        // Setup worker
        globalSession.setProperty(GlobalConfiguration.SCOPE_GLOBAL, "WORKER" + workerId + ".CLASSPATH",
                "org.signserver.module.odfsigner.ODFSigner");
        globalSession.setProperty(GlobalConfiguration.SCOPE_GLOBAL, "WORKER" + workerId + ".SIGNERTOKEN.CLASSPATH",
                PKCS11CryptoToken.class.getName());
        workerSession.setWorkerProperty(workerId, "NAME", "ODFSignerP11");
        workerSession.setWorkerProperty(workerId, "AUTHTYPE", "NOAUTH");
        workerSession.setWorkerProperty(workerId, "SHAREDLIBRARYNAME", sharedLibraryName);
        workerSession.setWorkerProperty(workerId, "SLOT", slot);
        workerSession.setWorkerProperty(workerId, "PIN", pin);
        workerSession.setWorkerProperty(workerId, "DEFAULTKEY", existingKey1);
        workerSession.setWorkerProperty(workerId, "CACHE_PRIVATEKEY", String.valueOf(cache));
    }

    /**
     * Tests setting up a ODF Signer, giving it a certificate and sign a document.
     */
    public void testODFSigner_uncached() throws Exception {
        final int workerId = WORKER_ODF;
        try {
            setODFSignerProperties(workerId, false);
            workerSession.reloadConfiguration(workerId);

            odfSigner(workerId);
        } finally {
            removeWorker(workerId);
        }
    }

    public void testODFSigner_cached() throws Exception {
        final int workerId = WORKER_ODF;
        try {
            setODFSignerProperties(workerId, true);
            workerSession.reloadConfiguration(workerId);

            odfSigner(workerId);
        } finally {
            removeWorker(workerId);
        }
    }

    private void odfSigner(final int workerId) throws Exception {
        // Generate CSR
        PKCS10CertReqInfo certReqInfo = new PKCS10CertReqInfo("SHA1WithRSA", "CN=Worker" + workerId, null);
        Base64SignerCertReqData reqData = (Base64SignerCertReqData) getWorkerSession()
                .getCertificateRequest(workerId, certReqInfo, false);

        // Issue certificate
        PKCS10CertificationRequest csr = new PKCS10CertificationRequest(Base64.decode(reqData.getBase64CertReq()));
        KeyPair issuerKeyPair = CryptoUtils.generateRSA(512);
        X509CertificateHolder cert = new X509v3CertificateBuilder(new X500Name("CN=TestP11 Issuer"), BigInteger.ONE,
                new Date(), new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(365)), csr.getSubject(),
                csr.getSubjectPublicKeyInfo())
                        .build(new JcaContentSignerBuilder("SHA256WithRSA").setProvider("BC")
                                .build(issuerKeyPair.getPrivate()));

        // Install certificate and chain
        workerSession.uploadSignerCertificate(workerId, cert.getEncoded(), GlobalConfiguration.SCOPE_GLOBAL);
        workerSession.uploadSignerCertificateChain(workerId, Arrays.asList(cert.getEncoded()),
                GlobalConfiguration.SCOPE_GLOBAL);
        workerSession.reloadConfiguration(workerId);

        // Test active
        List<String> errors = workerSession.getStatus(workerId).getFatalErrors();
        assertEquals("errors: " + errors, 0, errors.size());

        // Test signing
        signGenericDocument(workerId, readFile(odfSampleFile));
    }

    private void setOOXMLSignerProperties(final int workerId, final boolean cache) throws IOException {
        // Setup worker
        globalSession.setProperty(GlobalConfiguration.SCOPE_GLOBAL, "WORKER" + workerId + ".CLASSPATH",
                "org.signserver.module.ooxmlsigner.OOXMLSigner");
        globalSession.setProperty(GlobalConfiguration.SCOPE_GLOBAL, "WORKER" + workerId + ".SIGNERTOKEN.CLASSPATH",
                PKCS11CryptoToken.class.getName());
        workerSession.setWorkerProperty(workerId, "NAME", "OOXMLSignerP11");
        workerSession.setWorkerProperty(workerId, "AUTHTYPE", "NOAUTH");
        workerSession.setWorkerProperty(workerId, "SHAREDLIBRARYNAME", sharedLibraryName);
        workerSession.setWorkerProperty(workerId, "SLOT", slot);
        workerSession.setWorkerProperty(workerId, "PIN", pin);
        workerSession.setWorkerProperty(workerId, "DEFAULTKEY", existingKey1);
        workerSession.setWorkerProperty(workerId, "CACHE_PRIVATEKEY", String.valueOf(cache));
    }

    /**
     * Tests setting up a OOXML Signer, giving it a certificate and sign a document.
     */
    public void testOOXMLSigner_uncached() throws Exception {
        final int workerId = WORKER_OOXML;
        try {
            setOOXMLSignerProperties(workerId, false);
            workerSession.reloadConfiguration(workerId);
            ooxmlSigner(workerId);
        } finally {
            removeWorker(workerId);
        }
    }

    public void testOOXMLSigner_cached() throws Exception {
        final int workerId = WORKER_OOXML;
        try {
            setOOXMLSignerProperties(workerId, true);
            workerSession.reloadConfiguration(workerId);
            ooxmlSigner(workerId);
        } finally {
            removeWorker(workerId);
        }
    }

    private void ooxmlSigner(final int workerId) throws Exception {
        // Generate CSR
        PKCS10CertReqInfo certReqInfo = new PKCS10CertReqInfo("SHA1WithRSA", "CN=Worker" + workerId, null);
        Base64SignerCertReqData reqData = (Base64SignerCertReqData) getWorkerSession()
                .getCertificateRequest(workerId, certReqInfo, false);

        // Issue certificate
        PKCS10CertificationRequest csr = new PKCS10CertificationRequest(Base64.decode(reqData.getBase64CertReq()));
        KeyPair issuerKeyPair = CryptoUtils.generateRSA(512);
        X509CertificateHolder cert = new X509v3CertificateBuilder(new X500Name("CN=TestP11 Issuer"), BigInteger.ONE,
                new Date(), new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(365)), csr.getSubject(),
                csr.getSubjectPublicKeyInfo())
                        .build(new JcaContentSignerBuilder("SHA256WithRSA").setProvider("BC")
                                .build(issuerKeyPair.getPrivate()));

        // Install certificate and chain
        workerSession.uploadSignerCertificate(workerId, cert.getEncoded(), GlobalConfiguration.SCOPE_GLOBAL);
        workerSession.uploadSignerCertificateChain(workerId, Arrays.asList(cert.getEncoded()),
                GlobalConfiguration.SCOPE_GLOBAL);
        workerSession.reloadConfiguration(workerId);

        // Test active
        List<String> errors = workerSession.getStatus(workerId).getFatalErrors();
        assertEquals("errors: " + errors, 0, errors.size());

        // Test signing
        signGenericDocument(workerId, readFile(ooxmlSampleFile));
    }

    private void setMSAuthTimeStampSignerProperties(final int workerId, final boolean cache) throws IOException {
        // Setup worker
        globalSession.setProperty(GlobalConfiguration.SCOPE_GLOBAL, "WORKER" + workerId + ".CLASSPATH",
                "org.signserver.module.tsa.MSAuthCodeTimeStampSigner");
        globalSession.setProperty(GlobalConfiguration.SCOPE_GLOBAL, "WORKER" + workerId + ".SIGNERTOKEN.CLASSPATH",
                PKCS11CryptoToken.class.getName());
        workerSession.setWorkerProperty(workerId, "NAME", "MSAuthTSSignerP11");
        workerSession.setWorkerProperty(workerId, "AUTHTYPE", "NOAUTH");
        workerSession.setWorkerProperty(workerId, "SHAREDLIBRARYNAME", sharedLibraryName);
        workerSession.setWorkerProperty(workerId, "SLOT", slot);
        workerSession.setWorkerProperty(workerId, "PIN", pin);
        workerSession.setWorkerProperty(workerId, "DEFAULTKEY", existingKey1);
        workerSession.setWorkerProperty(workerId, "DEFAULTTSAPOLICYOID", "1.2.3");
        workerSession.setWorkerProperty(workerId, "CACHE_PRIVATEKEY", String.valueOf(cache));
    }

    /**
     * Tests setting up a MSAuthCodeTimeStamp Signer, giving it a certificate and request a time-stamp token.
     */
    public void testMSAuthTSSigner_uncached() throws Exception {
        final int workerId = WORKER_MSA;
        try {
            setMSAuthTimeStampSignerProperties(workerId, false);
            workerSession.reloadConfiguration(workerId);
            msauthTSSigner(workerId);
        } finally {
            removeWorker(workerId);
        }
    }

    public void testMSAuthTSSigner_cached() throws Exception {
        final int workerId = WORKER_MSA;
        try {
            setMSAuthTimeStampSignerProperties(workerId, true);
            workerSession.reloadConfiguration(workerId);
            msauthTSSigner(workerId);
        } finally {
            removeWorker(workerId);
        }
    }

    private void msauthTSSigner(final int workerId) throws Exception {
        // Generate CSR
        PKCS10CertReqInfo certReqInfo = new PKCS10CertReqInfo("SHA1WithRSA", "CN=Worker" + workerId, null);
        Base64SignerCertReqData reqData = (Base64SignerCertReqData) getWorkerSession()
                .getCertificateRequest(workerId, certReqInfo, false);

        // Issue certificate
        PKCS10CertificationRequest csr = new PKCS10CertificationRequest(Base64.decode(reqData.getBase64CertReq()));
        KeyPair issuerKeyPair = CryptoUtils.generateRSA(512);
        X509CertificateHolder cert = new X509v3CertificateBuilder(new X500Name("CN=TestP11 Issuer"), BigInteger.ONE,
                new Date(), new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(365)), csr.getSubject(),
                csr.getSubjectPublicKeyInfo())
                        .addExtension(org.bouncycastle.asn1.x509.X509Extension.extendedKeyUsage, true,
                                new ExtendedKeyUsage(KeyPurposeId.id_kp_timeStamping))
                        .build(new JcaContentSignerBuilder("SHA256WithRSA").setProvider("BC")
                                .build(issuerKeyPair.getPrivate()));

        // Install certificate and chain
        workerSession.uploadSignerCertificate(workerId, cert.getEncoded(), GlobalConfiguration.SCOPE_GLOBAL);
        workerSession.uploadSignerCertificateChain(workerId, Arrays.asList(cert.getEncoded()),
                GlobalConfiguration.SCOPE_GLOBAL);
        workerSession.reloadConfiguration(workerId);

        // Test active
        List<String> errors = workerSession.getStatus(workerId).getFatalErrors();
        assertEquals("errors: " + errors, 0, errors.size());

        // Test signing
        GenericSignRequest signRequest = new GenericSignRequest(678, MSAUTHCODE_REQUEST_DATA.getBytes());
        final GenericSignResponse res = (GenericSignResponse) workerSession.process(workerId, signRequest,
                new RequestContext());
        Certificate signercert = res.getSignerCertificate();
        assertNotNull(signercert);

        byte[] buf = res.getProcessedData();
        CMSSignedData s = new CMSSignedData(Base64.decode(buf));

        int verified = 0;
        Store certStore = s.getCertificates();
        SignerInformationStore signers = s.getSignerInfos();
        Collection c = signers.getSigners();
        Iterator it = c.iterator();

        while (it.hasNext()) {
            SignerInformation signer = (SignerInformation) it.next();
            Collection certCollection = certStore.getMatches(signer.getSID());

            Iterator certIt = certCollection.iterator();
            X509CertificateHolder signerCert = (X509CertificateHolder) certIt.next();

            if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(signerCert))) {
                verified++;
            }
        }

        assertEquals("signer verified", 1, verified);
    }

    /**
     * Test having default PKCS11CryptoToken properties.
     * Tests setting up a CMS Signer, giving it a certificate and sign a file.
     */
    public void testDefaultGlobalProperties() throws Exception {
        final int workerId = WORKER_CMS;
        try {
            // Setup worker
            globalSession.setProperty(GlobalConfiguration.SCOPE_GLOBAL, "WORKER" + workerId + ".CLASSPATH",
                    "org.signserver.module.cmssigner.CMSSigner");
            globalSession.setProperty(GlobalConfiguration.SCOPE_GLOBAL,
                    "WORKER" + workerId + ".SIGNERTOKEN.CLASSPATH", PKCS11CryptoToken.class.getName());
            globalSession.setProperty(GlobalConfiguration.SCOPE_GLOBAL, "DEFAULT.SHAREDLIBRARYNAME",
                    sharedLibraryName);
            globalSession.setProperty(GlobalConfiguration.SCOPE_GLOBAL, "DEFAULT.SLOT", slot);
            workerSession.setWorkerProperty(workerId, "NAME", "CMSSignerP11");
            workerSession.setWorkerProperty(workerId, "AUTHTYPE", "NOAUTH");
            workerSession.setWorkerProperty(workerId, "PIN", pin);
            workerSession.setWorkerProperty(workerId, "DEFAULTKEY", existingKey1);
            workerSession.reloadConfiguration(workerId);

            cmsSigner(workerId);
        } finally {
            removeWorker(workerId);
            globalSession.removeProperty(GlobalConfiguration.SCOPE_GLOBAL, "DEFAULT.SHAREDLIBRARY");
            globalSession.removeProperty(GlobalConfiguration.SCOPE_GLOBAL, "DEFAULT.SHAREDLIBRARYNAME");
            globalSession.removeProperty(GlobalConfiguration.SCOPE_GLOBAL, "DEFAULT.SLOT");
        }
    }

    private Set<String> getKeyAliases(final int workerId) throws Exception {
        Collection<KeyTestResult> testResults = workerSession.testKey(workerId, "all", pin.toCharArray());
        final HashSet<String> results = new HashSet<String>();
        for (KeyTestResult testResult : testResults) {
            results.add(testResult.getAlias());
        }
        return results;
    }

    public void testGenerateKey() throws Exception {
        LOG.info("testGenerateKey");

        final int workerId = WORKER_CMS;
        try {
            setCMSSignerProperties(workerId, false);
            workerSession.reloadConfiguration(workerId);

            // Check available aliases
            Set<String> aliases1 = getKeyAliases(workerId);

            if (aliases1.isEmpty()) {
                throw new Exception("getKeyAliases is not working or the slot is empty");
            }

            // If the key already exists, try to remove it first
            if (aliases1.contains(TEST_KEY_ALIAS)) {
                workerSession.removeKey(workerId, TEST_KEY_ALIAS);
                aliases1 = getKeyAliases(workerId);
            }
            if (aliases1.contains(TEST_KEY_ALIAS)) {
                throw new Exception("Pre-condition failed: Key with alias " + TEST_KEY_ALIAS
                        + " already exists and removing it failed");
            }

            // Generate a testkey
            workerSession.generateSignerKey(workerId, "RSA", "1024", TEST_KEY_ALIAS, pin.toCharArray());

            // Now expect the new TEST_KEY_ALIAS
            Set<String> expected = new HashSet<String>(aliases1);
            expected.add(TEST_KEY_ALIAS);
            Set<String> aliases2 = getKeyAliases(workerId);
            assertEquals("new key added", expected, aliases2);

        } finally {
            try {
                workerSession.removeKey(workerId, TEST_KEY_ALIAS);
            } catch (SignServerException ignored) {
            }
            removeWorker(workerId);
        }
    }

    /**
     * Tests that key generation is not allowed when the number of keys has
     * reached the KEYGENERATIONLIMIT.
     * Also checks that when allowing for one more keys, the next key can be
     * generated.
     */
    @SuppressWarnings("ThrowableResultIgnored")
    public void testKeyGenerationLimit() throws Exception {
        LOG.info("testKeyGenerationLimit");

        final int workerId = WORKER_CMS;
        try {
            setCMSSignerProperties(workerId, true);
            workerSession.reloadConfiguration(workerId);

            // Add a reference key
            workerSession.generateSignerKey(workerId, "RSA", "1024", TEST_KEY_ALIAS_2, pin.toCharArray());

            // Check available aliases
            final int keys = getKeyAliases(workerId).size();

            // Set the current number of keys as maximum
            workerSession.setWorkerProperty(workerId, "KEYGENERATIONLIMIT", String.valueOf(keys));
            workerSession.reloadConfiguration(workerId);

            // Key generation should fail
            try {
                workerSession.generateSignerKey(workerId, "RSA", "1024", TEST_KEY_ALIAS, pin.toCharArray());
                fail("Should have failed because of no space in token");
            } catch (TokenOutOfSpaceException expected) { // NOPMD
                // OK
            }

            // Allow for one more keys to be created
            workerSession.setWorkerProperty(workerId, "KEYGENERATIONLIMIT", String.valueOf(keys + 1));
            workerSession.reloadConfiguration(workerId);

            // Generate a new key
            try {
                workerSession.generateSignerKey(workerId, "RSA", "1024", TEST_KEY_ALIAS, pin.toCharArray());
            } catch (CryptoTokenOfflineException ex) {
                fail("Should have worked but got: " + ex.getLocalizedMessage());
            }

            final int keys2 = getKeyAliases(workerId).size();
            assertEquals("one more key", keys + 1, keys2);

            // Key generation should fail
            try {
                workerSession.generateSignerKey(workerId, "RSA", "1024", TEST_KEY_ALIAS, pin.toCharArray());
                fail("Should have failed because of no space in token");
            } catch (TokenOutOfSpaceException expected) { // NOPMD
                // OK
            }
        } finally {
            try {
                workerSession.removeKey(workerId, TEST_KEY_ALIAS);
            } catch (SignServerException ignored) {
            }
            try {
                workerSession.removeKey(workerId, TEST_KEY_ALIAS_2);
            } catch (SignServerException ignored) {
            }
            removeWorker(workerId);
        }
    }

    public void testGenerateKey_separateToken() throws Exception {
        LOG.info("testGenerateKey_separateToken");

        final int tokenId = CRYPTO_TOKEN;
        try {
            setupCryptoTokenProperties(tokenId, false);
            workerSession.reloadConfiguration(tokenId);

            // Check available aliases
            Set<String> aliases1 = getKeyAliases(tokenId);

            if (aliases1.isEmpty()) {
                throw new Exception("getKeyAliases is not working or the slot is empty");
            }

            // If the key already exists, try to remove it first
            if (aliases1.contains(TEST_KEY_ALIAS)) {
                workerSession.removeKey(tokenId, TEST_KEY_ALIAS);
                aliases1 = getKeyAliases(tokenId);
            }
            if (aliases1.contains(TEST_KEY_ALIAS)) {
                throw new Exception("Pre-condition failed: Key with alias " + TEST_KEY_ALIAS
                        + " already exists and removing it failed");
            }

            // Generate a testkey
            workerSession.generateSignerKey(tokenId, "RSA", "1024", TEST_KEY_ALIAS, pin.toCharArray());

            // Now expect the new TEST_KEY_ALIAS
            Set<String> expected = new HashSet<String>(aliases1);
            expected.add(TEST_KEY_ALIAS);
            Set<String> aliases2 = getKeyAliases(tokenId);
            assertEquals("new key added", expected, aliases2);

        } finally {
            try {
                workerSession.removeKey(tokenId, TEST_KEY_ALIAS);
            } catch (SignServerException ignored) {
            }
            removeWorker(tokenId);
        }
    }

    public void testRemoveKey() throws Exception {
        LOG.info("testRemoveKey");

        final int workerId = WORKER_CMS;
        try {
            setCMSSignerProperties(workerId, false);
            workerSession.reloadConfiguration(workerId);

            // Check available aliases
            Set<String> aliases1 = getKeyAliases(workerId);

            if (aliases1.isEmpty()) {
                throw new Exception("getKeyAliases is not working or the slot is empty");
            }

            if (!aliases1.contains(TEST_KEY_ALIAS)) {
                // Generate a testkey
                workerSession.generateSignerKey(workerId, "RSA", "1024", TEST_KEY_ALIAS, pin.toCharArray());
                aliases1 = getKeyAliases(workerId);
            }
            if (!aliases1.contains(TEST_KEY_ALIAS)) {
                throw new Exception("Pre-condition failed: Key with alias " + TEST_KEY_ALIAS
                        + " did not exist and it could not be created");
            }

            // Remove the key
            workerSession.removeKey(workerId, TEST_KEY_ALIAS);

            // Now expect the TEST_KEY_ALIAS to have been removed
            Set<String> aliases2 = getKeyAliases(workerId);
            Set<String> expected = new HashSet<String>(aliases1);
            expected.remove(TEST_KEY_ALIAS);
            assertEquals("new key removed", expected, aliases2);
        } finally {
            removeWorker(workerId);
        }
    }

    public void testRemoveKey_separateToken() throws Exception {
        LOG.info("testRemoveKey_separateToken");

        final int tokenId = CRYPTO_TOKEN;
        try {
            setCMSSignerProperties(tokenId, false);
            workerSession.reloadConfiguration(tokenId);

            // Check available aliases
            Set<String> aliases1 = getKeyAliases(tokenId);

            if (aliases1.isEmpty()) {
                throw new Exception("getKeyAliases is not working or the slot is empty");
            }

            if (!aliases1.contains(TEST_KEY_ALIAS)) {
                // Generate a testkey
                workerSession.generateSignerKey(tokenId, "RSA", "1024", TEST_KEY_ALIAS, pin.toCharArray());
                aliases1 = getKeyAliases(tokenId);
            }
            if (!aliases1.contains(TEST_KEY_ALIAS)) {
                throw new Exception("Pre-condition failed: Key with alias " + TEST_KEY_ALIAS
                        + " did not exist and it could not be created");
            }

            // Remove the key
            workerSession.removeKey(tokenId, TEST_KEY_ALIAS);

            // Now expect the TEST_KEY_ALIAS to have been removed
            Set<String> aliases2 = getKeyAliases(tokenId);
            Set<String> expected = new HashSet<String>(aliases1);
            expected.remove(TEST_KEY_ALIAS);
            assertEquals("new key removed", expected, aliases2);
        } finally {
            removeWorker(tokenId);
        }
    }

    /**
     * Test that missing the SHAREDLIBRARY property results
     * in a descriptive error reported by getFatalErrors().
     * 
     * @throws Exception
     */
    public void testNoSharedLibrary() throws Exception {
        LOG.info("testNoSharedLibrary");

        final int workerId = WORKER_XML;

        try {
            final String expectedPrefix = "Failed to initialize crypto token: Missing SHAREDLIBRARYNAME property";
            setXMLSignerProperties(workerId, false);
            workerSession.removeWorkerProperty(workerId, "SHAREDLIBRARYNAME");
            workerSession.reloadConfiguration(workerId);

            final List<String> errors = workerSession.getStatus(workerId).getFatalErrors();
            boolean foundError = false;

            for (final String error : errors) {
                if (error.startsWith(expectedPrefix)) {
                    foundError = true;
                    break;
                }
            }
            assertTrue("Should contain error: " + errors, foundError);
        } finally {
            removeWorker(workerId);
        }
    }

    /**
     * Test that setting a non-existing P11 shared library results
     * in a descriptive error reported by getFatalErrors().
     * 
     * @throws Exception
     */
    public void testNonExistingSharedLibrary() throws Exception {
        LOG.info("testNonExistingSharedLibrary");

        final int workerId = WORKER_XML;

        try {
            final String expectedErrorPrefix = "Failed to initialize crypto token: SHAREDLIBRARYNAME NonExistingLibrary is not referring to a defined value";
            setXMLSignerProperties(workerId, false);
            workerSession.setWorkerProperty(workerId, "SHAREDLIBRARYNAME", "NonExistingLibrary");
            workerSession.reloadConfiguration(workerId);

            final List<String> errors = workerSession.getStatus(workerId).getFatalErrors();
            boolean foundError = false;

            for (final String error : errors) {
                if (error.startsWith(expectedErrorPrefix)) {
                    foundError = true;
                    break;
                }
            }

            assertTrue("Should contain error about lib name but was: " + errors, foundError);
        } finally {
            removeWorker(workerId);
        }
    }

    /**
     * Test that specifying the old property SHAREDLIBRARY not pointing to
     * a value defined in the P11 library list will give a deprecation error.
     */
    public void testOldSharedLibraryPropertyPointingToUndefined() throws Exception {
        LOG.info("testOldSharedLibraryPropertyPointingToUndefined");

        final int workerId = WORKER_XML;

        try {
            final String expectedErrorPrefix = "Failed to initialize crypto token: SHAREDLIBRARY is not permitted when pointing to a library not defined at deploy-time";
            setXMLSignerProperties(workerId, false);
            workerSession.removeWorkerProperty(workerId, "SHAREDLIBRARYNAME");
            workerSession.setWorkerProperty(workerId, "SHAREDLIBRARY", "/opt/lib/libundefinedp11.so");
            workerSession.reloadConfiguration(workerId);

            final List<String> errors = workerSession.getStatus(workerId).getFatalErrors();
            boolean foundError = false;

            for (final String error : errors) {
                if (error.startsWith(expectedErrorPrefix)) {
                    foundError = true;
                    break;
                }
            }
            assertTrue("Should contain error about lib name but was: " + errors, foundError);
        } finally {
            removeWorker(workerId);
        }
    }

    /**
     * Test that specifying the old property SHAREDLIBRARY pointing to a library
     * defined in deploy-time works.
     * 
     * @throws Exception 
     */
    public void testOldSharedLibraryPropertyPointingToDefined() throws Exception {
        LOG.info("testOldSharedLibraryPropertyPointingToDefined");

        final int workerId = WORKER_XML;

        try {
            final String unexpectedErrorPrefix = "Failed to initialize crypto token: SHAREDLIBRARY is not permitted when pointing to a library not defined at deploy-time";

            setXMLSignerProperties(workerId, false);
            workerSession.removeWorkerProperty(workerId, "SHAREDLIBRARYNAME");
            workerSession.setWorkerProperty(workerId, "SHAREDLIBRARY", sharedLibraryPath);
            workerSession.reloadConfiguration(workerId);

            final List<String> errors = workerSession.getStatus(workerId).getFatalErrors();
            boolean foundError = false;

            for (final String error : errors) {
                if (error.startsWith(unexpectedErrorPrefix)) {
                    foundError = true;
                    break;
                }
            }

            assertFalse("Should not contain error: " + errors, foundError);
        } finally {
            removeWorker(workerId);
        }
    }

    /**
     * Test that setting both the old and new property at the same time
     * is not allowed when referring to different libraries.
     * 
     * @throws Exception 
     */
    public void testBothP11LibraryNameAndOldSharedLibraryProperty() throws Exception {
        LOG.info("testBothP11LibraryNameAndOldSharedLibraryProperty");

        final int workerId = WORKER_XML;

        try {
            final String expectedErrorPrefix = "Failed to initialize crypto token: Can not specify both SHAREDLIBRARY and SHAREDLIBRARYNAME at the same time";

            setXMLSignerProperties(workerId, false);
            workerSession.setWorkerProperty(workerId, "SHAREDLIBRARY", sharedLibraryPath);
            workerSession.setWorkerProperty(workerId, "SHAREDLIBRARYNAME", "SoftHSM");
            workerSession.reloadConfiguration(workerId);

            final List<String> errors = workerSession.getStatus(workerId).getFatalErrors();
            boolean foundError = false;

            for (final String error : errors) {
                if (error.startsWith(expectedErrorPrefix)) {
                    foundError = true;
                    break;
                }
            }

            assertTrue("Should contain error: " + errors, foundError);
        } finally {
            removeWorker(workerId);
        }
    }

    /**
     * Test that setting both the old and new property at the same time
     * is allowed for backwards compatability when pointing to the same
     * library.
     * 
     * @throws Exception 
     */
    public void testBothP11LibraryNameAndOldSharedLibraryPropertyReferringSame() throws Exception {
        LOG.info("testBothP11LibraryNameAndOldSharedLibraryProperty");

        final int workerId = WORKER_XML;

        try {
            final String unexpectedErrorPrefix = "Failed to initialize crypto token: Can not specify both SHAREDLIBRARY and SHAREDLIBRARYNAME at the same time";

            setXMLSignerProperties(workerId, false);
            workerSession.setWorkerProperty(workerId, "SHAREDLIBRARY", sharedLibraryPath);
            workerSession.setWorkerProperty(workerId, "SHAREDLIBRARYNAME", sharedLibraryName);
            workerSession.reloadConfiguration(workerId);

            final List<String> errors = workerSession.getStatus(workerId).getFatalErrors();
            boolean foundError = false;

            for (final String error : errors) {
                if (error.startsWith(unexpectedErrorPrefix)) {
                    foundError = true;
                    break;
                }
            }

            assertFalse("Should not contain error: " + errors, foundError);
        } finally {
            removeWorker(workerId);
        }
    }
}