mitm.common.security.certpath.CertPathBuilderTest.java Source code

Java tutorial

Introduction

Here is the source code for mitm.common.security.certpath.CertPathBuilderTest.java

Source

/*
 * Copyright (c) 2008-2011, Martijn Brinkers, Djigzo.
 * 
 * This file is part of Djigzo email encryption.
 *
 * Djigzo is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License 
 * version 3, 19 November 2007 as published by the Free Software 
 * Foundation.
 *
 * Djigzo is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public 
 * License along with Djigzo. If not, see <http://www.gnu.org/licenses/>
 *
 * Additional permission under GNU AGPL version 3 section 7
 * 
 * If you modify this Program, or any covered work, by linking or 
 * combining it with aspectjrt.jar, aspectjweaver.jar, tyrex-1.0.3.jar, 
 * freemarker.jar, dom4j.jar, mx4j-jmx.jar, mx4j-tools.jar, 
 * spice-classman-1.0.jar, spice-loggerstore-0.5.jar, spice-salt-0.8.jar, 
 * spice-xmlpolicy-1.0.jar, saaj-api-1.3.jar, saaj-impl-1.3.jar, 
 * wsdl4j-1.6.1.jar (or modified versions of these libraries), 
 * containing parts covered by the terms of Eclipse Public License, 
 * tyrex license, freemarker license, dom4j license, mx4j license,
 * Spice Software License, Common Development and Distribution License
 * (CDDL), Common Public License (CPL) the licensors of this Program grant 
 * you additional permission to convey the resulting work.
 */
package mitm.common.security.certpath;

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

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.NoSuchProviderException;
import java.security.cert.CRLException;
import java.security.cert.CertPathBuilderException;
import java.security.cert.CertPathBuilderResult;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertStore;
import java.security.cert.CertStoreException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.TrustAnchor;
import java.security.cert.X509CRL;
import java.security.cert.X509CertSelector;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.List;
import java.util.Set;

import mitm.common.hibernate.HibernateSessionSource;
import mitm.common.hibernate.SessionManager;
import mitm.common.hibernate.SessionManagerImpl;
import mitm.common.hibernate.StandardHibernateSessionSourceImpl;
import mitm.common.security.SecurityFactoryFactoryException;
import mitm.common.security.bouncycastle.InitializeBouncycastle;
import mitm.common.security.certificate.CertificateUtils;
import mitm.common.security.certificate.X509CertificateInspector;
import mitm.common.security.certstore.X509CertStoreExt;
import mitm.common.security.certstore.hibernate.X509CertStoreExtAutoCommitFactory;
import mitm.common.security.certstore.hibernate.X509CertStoreExtHibernate;
import mitm.common.security.certstore.jce.X509CertStoreParameters;
import mitm.common.security.crlstore.CRLStoreException;
import mitm.common.security.crlstore.X509CRLStoreExt;
import mitm.common.security.crlstore.hibernate.X509CRLStoreExtAutoCommitFactory;
import mitm.common.security.provider.MITMProvider;
import mitm.common.util.BigIntegerUtils;
import mitm.test.TestUtils;

import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.log4j.PropertyConfigurator;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

public class CertPathBuilderTest {
    private static final File hibernateConfig = new File("test/resources/hibernate.cfg.xml");

    private static HibernateSessionSource sessionSource;
    private static SessionManager sessionManager;
    private static CertStore certStore;
    private static CertStore rootStore;
    private static X509CertStoreParameters certStoreParams;
    private static X509CertStoreParameters rootStoreParams;
    private static Set<TrustAnchor> trustAnchors;

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
        PropertyConfigurator.configure("conf/log4j.properties");

        InitializeBouncycastle.initialize();

        sessionSource = new StandardHibernateSessionSourceImpl(hibernateConfig);

        sessionManager = new SessionManagerImpl(sessionSource);

        MITMProvider.initialize(sessionManager);

        certStoreParams = new X509CertStoreParameters(
                new X509CertStoreExtAutoCommitFactory(sessionSource, "certificates").create(),
                new X509CRLStoreExtAutoCommitFactory(sessionSource, "certificates").create());

        certStore = CertStore.getInstance(MITMProvider.DATABASE_CERTSTORE, certStoreParams, "mitm");

        rootStoreParams = new X509CertStoreParameters(
                new X509CertStoreExtAutoCommitFactory(sessionSource, "roots").create());
        rootStore = CertStore.getInstance(MITMProvider.DATABASE_CERTSTORE, rootStoreParams, "mitm");
    }

    @Before
    public void setup() throws Exception {
        certStoreParams.getCertStore().removeAllEntries();
        certStoreParams.getCRLStore().removeAllEntries();

        rootStoreParams.getCertStore().removeAllEntries();
    }

    private static void addCRL(String filename, X509CRLStoreExt crlStore) throws CRLStoreException {
        File crlFile = new File("test/resources/testdata/crls", filename);

        X509CRL crl;
        try {
            crl = TestUtils.loadX509CRL(crlFile);
        } catch (CertificateException e) {
            throw new CRLStoreException(e);
        } catch (NoSuchProviderException e) {
            throw new CRLStoreException(e);
        } catch (SecurityFactoryFactoryException e) {
            throw new CRLStoreException(e);
        } catch (CRLException e) {
            throw new CRLStoreException(e);
        } catch (FileNotFoundException e) {
            throw new CRLStoreException(e);
        }

        crlStore.addCRL(crl);
    }

    private static void addCertificates(String filename, X509CertStoreExt certStore)
            throws CertificateException, NoSuchProviderException, CertStoreException, IOException {
        File file = new File("test/resources/testdata/certificates", filename);

        Collection<? extends Certificate> certificates = CertificateUtils.readCertificates(file);

        for (Certificate certificate : certificates) {
            if (certificate instanceof X509Certificate) {
                if (!certStore.contains((X509Certificate) certificate)) {
                    certStore.addCertificate((X509Certificate) certificate);
                }
            }
        }
    }

    /*
     * Test if flush/clear improves import and it does. Don't know why the fastest setting is 
     * when flush/clear is done every iteration
     */
    private static void addCertificatesBulk(String filename)
            throws CertificateException, NoSuchProviderException, CertStoreException, IOException {
        File file = new File("test/resources/testdata/certificates", filename);

        Collection<? extends Certificate> certificates = CertificateUtils.readCertificates(file);

        int i = 0;

        Session session = sessionSource.newSession();

        sessionManager.setSession(session);

        X509CertStoreExt certStore = new X509CertStoreExtHibernate("certificates", sessionManager);

        Transaction tx = session.beginTransaction();

        try {
            for (Certificate certificate : certificates) {
                if (i % 1 == 0) {
                    session.flush();
                    session.clear();
                }

                if (i % 100 == 0) {
                    tx.commit();
                    tx = session.beginTransaction();
                }

                if (certificate instanceof X509Certificate) {
                    if (!certStore.contains((X509Certificate) certificate)) {
                        certStore.addCertificate((X509Certificate) certificate);
                    }
                }

                i++;
            }
            tx.commit();
        } finally {
            sessionSource.closeSession(session);
        }
    }

    private static Set<TrustAnchor> getTrustAnchors() throws CertStoreException {
        TrustAnchorBuilder builder = new SimpleTrustAnchorBuilder();

        builder.addCertificates(rootStore);

        Set<TrustAnchor> trustAnchors = builder.getTrustAnchors();

        return trustAnchors;
    }

    /*
     * This tests checks whether a certificate is accepted which has a missing AlgorithmIdentifier parameter 
     * for the outer cert and a NULL parameter for the inner TBS cert. This is strange but most PKI implementation
     * accept such a certificate.
     * 
     *  According to RFC 3370
     *
     * The parameter should be absent when generating a certificate but applications must accept a NULL parameter.
     *
     *"   There are two possible encodings for the SHA-1 AlgorithmIdentifier
     *   parameters field.  The two alternatives arise from the fact that when
     *   the 1988 syntax for AlgorithmIdentifier was translated into the 1997
     *   syntax, the OPTIONAL associated with the AlgorithmIdentifier
     *   parameters got lost.  Later the OPTIONAL was recovered via a defect
     *   report, but by then many people thought that algorithm parameters
     *   were mandatory.  Because of this history some implementations encode
     *   parameters as a NULL element and others omit them entirely.  The
     *   correct encoding is to omit the parameters field; however,
     *   implementations MUST also handle a SHA-1 AlgorithmIdentifier
     *   parameters field which contains a NULL.
     *
     *   The AlgorithmIdentifier parameters field is OPTIONAL.  If present,
     *   the parameters field MUST contain a NULL.  Implementations MUST
     *   accept SHA-1 AlgorithmIdentifiers with absent parameters.
     *   Implementations MUST accept SHA-1 AlgorithmIdentifiers with NULL
     *   parameters.  Implementations SHOULD generate SHA-1
     *   AlgorithmIdentifiers with absent parameters.
     */
    @Test
    public void testAlgorithmIdentifierComparisonFailed() throws Exception {
        addCertificates("AC_MINEFI_DPMA.cer", certStoreParams.getCertStore());
        addCertificates("MINEFI_AUTORITE_DE_CERTIFICATION_RACINE.cer", rootStoreParams.getCertStore());

        CertificatePathBuilder builder = new PKIXCertificatePathBuilder();
        builder.addCertStore(certStore);
        builder.setTrustAnchors(getTrustAnchors());

        X509CertSelector selector = new X509CertSelector();

        selector.setSerialNumber(BigIntegerUtils.hexDecode("30303031303935373731383130383135"));
        selector.setIssuer("CN=MINEFI-AUTORITE DE CERTIFICATION RACINE, OU=AGENCE AUTORITE, O=MINEFI, C=FR");

        CertPathBuilderResult results = builder.buildPath(selector);

        assertNotNull(results.getCertPath());
        assertEquals(1, results.getCertPath().getCertificates().size());
    }

    @Test
    public void testNoTrustAnchors() throws Exception {
        addCertificates("windows-xp-all-intermediates.p7b", certStoreParams.getCertStore());
        addCertificates("mitm-test-ca.cer", certStoreParams.getCertStore());
        addCertificates("testCertificates.p7b", certStoreParams.getCertStore());

        CertificatePathBuilder builder = new PKIXCertificatePathBuilder();
        builder.addCertStore(certStore);

        X509CertSelector selector = new X509CertSelector();

        selector.setSerialNumber(BigIntegerUtils.hexDecode("115FD110A82F742D0AE14A71B651962"));
        selector.setIssuer("EMAILADDRESS=ca@example.com, CN=MITM Test CA, L=Amsterdam, ST=NH, C=NL");

        try {
            builder.buildPath(selector);

            fail("Should have failed");
        } catch (CertPathBuilderException e) {
            assertEquals(PKIXCertificatePathBuilder.NO_ROOTS_ERROR_MESSAGE, e.getMessage());
        }
    }

    /*
     * The CRL is not signed with the CA private key but by another private key
     */
    @Test
    public void testBuildPathCRLSignedByIncorrectKey() throws Exception {
        // add roots
        addCertificates("windows-xp-all-roots.p7b", rootStoreParams.getCertStore());
        addCertificates("mitm-test-root.cer", rootStoreParams.getCertStore());

        addCertificates("windows-xp-all-intermediates.p7b", certStoreParams.getCertStore());
        addCertificates("mitm-test-ca.cer", certStoreParams.getCertStore());
        addCertificates("testCertificates.p7b", certStoreParams.getCertStore());

        addCRL("test-root-ca-not-revoked.crl", certStoreParams.getCRLStore());
        addCRL("test-ca-signed-incorrect-key.crl", certStoreParams.getCRLStore());

        trustAnchors = getTrustAnchors();

        X509CertSelector selector = new X509CertSelector();

        selector.setSerialNumber(BigIntegerUtils.hexDecode("115FD110A82F742D0AE14A71B651962"));
        selector.setIssuer("EMAILADDRESS=ca@example.com, CN=MITM Test CA, L=Amsterdam, ST=NH, C=NL");

        CertificatePathBuilder builder = new PKIXCertificatePathBuilder();

        builder.setTrustAnchors(trustAnchors);
        builder.addCertPathChecker(new SMIMEExtendedKeyUsageCertPathChecker());
        builder.addCertStore(certStore);
        builder.setRevocationEnabled(true);

        try {
            builder.buildPath(selector);

            fail();
        } catch (CertPathBuilderException e) {
            // should be thrown because the crl was not signed by the CA but the issuer is the CA
            Throwable rootCause = ExceptionUtils.getRootCause(e);

            assertEquals("CRL does not verify with supplied public key.", rootCause.getMessage());
        }
    }

    /*
     * Same test as above but now there is also the correct CRL. This should succeed.
     */
    @Test
    public void testBuildPathCRLSignedByIncorrectKeyAndCorrectKey() throws Exception {
        // add roots
        addCertificates("windows-xp-all-roots.p7b", rootStoreParams.getCertStore());
        addCertificates("mitm-test-root.cer", rootStoreParams.getCertStore());

        addCertificates("windows-xp-all-intermediates.p7b", certStoreParams.getCertStore());
        addCertificates("mitm-test-ca.cer", certStoreParams.getCertStore());
        addCertificates("testCertificates.p7b", certStoreParams.getCertStore());

        addCRL("test-root-ca-not-revoked.crl", certStoreParams.getCRLStore());
        addCRL("test-ca.crl", certStoreParams.getCRLStore());
        addCRL("test-ca-signed-incorrect-key.crl", certStoreParams.getCRLStore());

        trustAnchors = getTrustAnchors();

        X509CertSelector selector = new X509CertSelector();

        selector.setSerialNumber(BigIntegerUtils.hexDecode("115FD110A82F742D0AE14A71B651962"));
        selector.setIssuer("EMAILADDRESS=ca@example.com, CN=MITM Test CA, L=Amsterdam, ST=NH, C=NL");

        CertificatePathBuilder builder = new PKIXCertificatePathBuilder();

        builder.setTrustAnchors(trustAnchors);
        builder.addCertPathChecker(new SMIMEExtendedKeyUsageCertPathChecker());
        builder.addCertStore(certStore);
        builder.setRevocationEnabled(true);

        CertPathBuilderResult result = builder.buildPath(selector);

        assertEquals(2, result.getCertPath().getCertificates().size());
    }

    /*
     * Test path building for a certificate with critical extended key usage containing EMAILPROTECTION when 
     * SMIMEExtendedKeyUsageCertPathChecker is added to the path checkers
     */
    @Test
    public void testBuildPathManyCertificates() throws Exception {
        // add roots
        addCertificates("windows-xp-all-roots.p7b", rootStoreParams.getCertStore());
        addCertificates("mitm-test-root.cer", rootStoreParams.getCertStore());

        long start = System.currentTimeMillis();

        addCertificatesBulk("random-self-signed-1000.p7b");
        //addCertificatesBulk("random-self-signed-10000.p7b");
        //addCertificatesBulk("random-self-signed-40000.p7b");

        System.out.println("Seconds : " + (System.currentTimeMillis() - start) * 0.001);

        addCertificates("mitm-test-ca.cer", certStoreParams.getCertStore());
        addCertificates("testCertificates.p7b", certStoreParams.getCertStore());

        addCRL("test-ca.crl", certStoreParams.getCRLStore());
        addCRL("test-root-ca-not-revoked.crl", certStoreParams.getCRLStore());

        int tries = 100;

        start = System.currentTimeMillis();

        TrustAnchorBuilder trustAnchorBuilder = new CertStoreTrustAnchorBuilder(rootStoreParams.getCertStore(),
                0 /* milliseconds */);

        for (int i = 0; i < tries; i++) {
            X509CertSelector selector = new X509CertSelector();

            selector.setSerialNumber(BigIntegerUtils.hexDecode("116A448F117FF69FE4F2D4D38F689D7"));
            selector.setIssuer("EMAILADDRESS=ca@example.com, CN=MITM Test CA, L=Amsterdam, ST=NH, C=NL");

            CertificatePathBuilder builder = new PKIXCertificatePathBuilder();

            builder.setTrustAnchors(trustAnchorBuilder.getTrustAnchors());
            builder.addCertPathChecker(new SMIMEExtendedKeyUsageCertPathChecker());
            builder.addCertStore(certStore);
            builder.setRevocationEnabled(true);

            CertPathBuilderResult result = builder.buildPath(selector);

            assertEquals(2, result.getCertPath().getCertificates().size());
        }

        double end = (System.currentTimeMillis() - start) * 0.001 / tries;

        System.out.println("Seconds / build: " + end);

        start = System.currentTimeMillis();

        Collection<? extends Certificate> certificates = certStore.getCertificates(new X509CertSelector());

        end = (System.currentTimeMillis() - start) * 0.001 / certificates.size();

        System.out.println("Seconds / certificate: " + end);
    }

    @Test
    public void testBuildPathCRLUnavailable() throws Exception {
        // add roots
        addCertificates("windows-xp-all-roots.p7b", rootStoreParams.getCertStore());
        addCertificates("mitm-test-root.cer", rootStoreParams.getCertStore());

        addCertificates("windows-xp-all-intermediates.p7b", certStoreParams.getCertStore());
        addCertificates("mitm-test-ca.cer", certStoreParams.getCertStore());
        addCertificates("testCertificates.p7b", certStoreParams.getCertStore());

        trustAnchors = getTrustAnchors();

        X509CertSelector selector = new X509CertSelector();

        selector.setSerialNumber(BigIntegerUtils.hexDecode("115FD110A82F742D0AE14A71B651962"));
        selector.setIssuer("EMAILADDRESS=ca@example.com, CN=MITM Test CA, L=Amsterdam, ST=NH, C=NL");

        CertificatePathBuilder builder = new PKIXCertificatePathBuilder();

        builder.setTrustAnchors(trustAnchors);
        builder.addCertPathChecker(new SMIMEExtendedKeyUsageCertPathChecker());
        builder.addCertStore(certStore);
        builder.setRevocationEnabled(true);

        try {
            builder.buildPath(selector);

            fail();
        } catch (CertPathBuilderException e) {
            Throwable cause = ExceptionUtils.getCause(e);

            assertTrue(cause.getMessage().startsWith("No CRLs found"));
        }
    }

    @Test
    public void testBuildPathCRLUnavailableButCRLCheckOff() throws Exception {
        // add roots
        addCertificates("windows-xp-all-roots.p7b", rootStoreParams.getCertStore());
        addCertificates("mitm-test-root.cer", rootStoreParams.getCertStore());

        addCertificates("windows-xp-all-intermediates.p7b", certStoreParams.getCertStore());
        addCertificates("mitm-test-ca.cer", certStoreParams.getCertStore());
        addCertificates("testCertificates.p7b", certStoreParams.getCertStore());

        trustAnchors = getTrustAnchors();

        X509CertSelector selector = new X509CertSelector();

        selector.setSerialNumber(BigIntegerUtils.hexDecode("115FD110A82F742D0AE14A71B651962"));
        selector.setIssuer("EMAILADDRESS=ca@example.com, CN=MITM Test CA, L=Amsterdam, ST=NH, C=NL");

        CertificatePathBuilder builder = new PKIXCertificatePathBuilder();

        builder.setTrustAnchors(trustAnchors);
        builder.addCertPathChecker(new SMIMEExtendedKeyUsageCertPathChecker());
        builder.addCertStore(certStore);
        builder.setRevocationEnabled(false);

        CertPathBuilderResult result = builder.buildPath(selector);

        List<? extends Certificate> certificates = result.getCertPath().getCertificates();

        assertEquals(2, certificates.size());

        CertStore store = CertStore.getInstance("Collection", new CollectionCertStoreParameters(certificates));

        Collection<? extends Certificate> foundCertificates = store.getCertificates(selector);

        assertEquals(1, foundCertificates.size());
    }

    @Test
    public void testBuildPathRootNotFound() throws Exception {
        // root store cannot be empty so we just load something
        addCertificates("dod-mega-crl.cer", rootStoreParams.getCertStore());

        addCertificates("mitm-test-ca.cer", certStoreParams.getCertStore());
        addCertificates("testCertificates.p7b", certStoreParams.getCertStore());

        addCRL("test-ca.crl", certStoreParams.getCRLStore());
        addCRL("test-root-ca-not-revoked.crl", certStoreParams.getCRLStore());

        trustAnchors = getTrustAnchors();

        X509CertSelector selector = new X509CertSelector();

        selector.setSerialNumber(BigIntegerUtils.hexDecode("115FCD741088707366E9727452C9770"));
        selector.setIssuer("EMAILADDRESS=ca@example.com, CN=MITM Test CA, L=Amsterdam, ST=NH, C=NL");

        CertificatePathBuilder builder = new PKIXCertificatePathBuilder();

        builder.setTrustAnchors(trustAnchors);
        builder.addCertStore(certStore);

        try {
            builder.buildPath(selector);

            fail();
        } catch (CertPathBuilderException e) {
            assertEquals("No issuer certificate for certificate in certification path found.", e.getMessage());
        }
    }

    @Test
    public void testBuildPathTargetNotFound() throws Exception {
        // add roots
        addCertificates("mitm-test-root.cer", rootStoreParams.getCertStore());

        addCertificates("mitm-test-ca.cer", certStoreParams.getCertStore());
        addCertificates("testCertificates.p7b", certStoreParams.getCertStore());

        addCRL("test-ca.crl", certStoreParams.getCRLStore());
        addCRL("test-root-ca-not-revoked.crl", certStoreParams.getCRLStore());

        trustAnchors = getTrustAnchors();

        X509CertSelector selector = new X509CertSelector();

        selector.setSerialNumber(BigIntegerUtils.hexDecode("123"));
        selector.setIssuer("EMAILADDRESS=ca@example.com, CN=MITM Test CA, L=Amsterdam, ST=NH, C=NL");

        CertificatePathBuilder builder = new PKIXCertificatePathBuilder();

        builder.setTrustAnchors(trustAnchors);
        builder.addCertStore(certStore);

        try {
            builder.buildPath(selector);

            fail();
        } catch (CertPathBuilderException e) {
            assertEquals("No certificate found matching targetContraints.", e.getMessage());
        }
    }

    @Test
    public void testBuildPathEndCertRevoked() throws Exception {
        // add roots
        addCertificates("mitm-test-root.cer", rootStoreParams.getCertStore());

        addCertificates("mitm-test-ca.cer", certStoreParams.getCertStore());
        addCertificates("testCertificates.p7b", certStoreParams.getCertStore());

        addCRL("test-ca.crl", certStoreParams.getCRLStore());
        addCRL("test-root-ca-not-revoked.crl", certStoreParams.getCRLStore());

        trustAnchors = getTrustAnchors();

        X509CertSelector selector = new X509CertSelector();

        selector.setSerialNumber(BigIntegerUtils.hexDecode("115FCD741088707366E9727452C9770"));
        selector.setIssuer("EMAILADDRESS=ca@example.com, CN=MITM Test CA, L=Amsterdam, ST=NH, C=NL");

        CertificatePathBuilder builder = new PKIXCertificatePathBuilder();

        builder.setTrustAnchors(trustAnchors);
        builder.addCertPathChecker(new SMIMEExtendedKeyUsageCertPathChecker());
        builder.addCertStore(certStore);
        builder.setRevocationEnabled(true);

        try {
            builder.buildPath(selector);

            fail();
        } catch (CertPathBuilderException e) {
            // CertPathValidatorException should have been thrown because the certificate is revoked 
            Throwable cause = ExceptionUtils.getCause(e);

            assertTrue(cause.getMessage().startsWith("Certificate revocation after Fri Nov 30"));
            assertTrue(cause.getMessage().endsWith("2007, reason: privilegeWithdrawn"));
        }
    }

    @Test
    public void testBuildPathCACertRevoked() throws Exception {
        // add roots
        addCertificates("mitm-test-root.cer", rootStoreParams.getCertStore());

        addCertificates("mitm-test-ca.cer", certStoreParams.getCertStore());
        addCertificates("testCertificates.p7b", certStoreParams.getCertStore());

        addCRL("test-ca.crl", certStoreParams.getCRLStore());
        addCRL("test-root-ca-revoked.crl", certStoreParams.getCRLStore());

        trustAnchors = getTrustAnchors();

        X509CertSelector selector = new X509CertSelector();

        selector.setSerialNumber(BigIntegerUtils.hexDecode("115FD110A82F742D0AE14A71B651962"));
        selector.setIssuer("EMAILADDRESS=ca@example.com, CN=MITM Test CA, L=Amsterdam, ST=NH, C=NL");

        CertificatePathBuilder builder = new PKIXCertificatePathBuilder();

        builder.setTrustAnchors(trustAnchors);
        builder.addCertPathChecker(new SMIMEExtendedKeyUsageCertPathChecker());
        builder.addCertStore(certStore);
        builder.setRevocationEnabled(true);

        try {
            builder.buildPath(selector);

            fail();
        } catch (CertPathBuilderException e) {
            // CertPathValidatorException should have been thrown because the certificate has a 
            // key usage extension that is critical.
            Throwable cause = ExceptionUtils.getCause(e);

            assertTrue(cause.getMessage().startsWith("Certificate revocation after Fri Nov 30"));
            assertTrue(cause.getMessage().endsWith("2007, reason: cACompromise"));
        }
    }

    @Test
    public void testBuildPath() throws Exception {
        // add roots
        addCertificates("windows-xp-all-roots.p7b", rootStoreParams.getCertStore());
        addCertificates("mitm-test-root.cer", rootStoreParams.getCertStore());

        addCertificates("windows-xp-all-intermediates.p7b", certStoreParams.getCertStore());
        addCertificates("mitm-test-ca.cer", certStoreParams.getCertStore());
        addCertificates("testCertificates.p7b", certStoreParams.getCertStore());

        addCRL("intel-basic-enterprise-issuing-CA.crl", certStoreParams.getCRLStore());
        addCRL("itrus.com.cn.crl", certStoreParams.getCRLStore());
        addCRL("test-ca.crl", certStoreParams.getCRLStore());
        addCRL("test-root-ca-not-revoked.crl", certStoreParams.getCRLStore());
        addCRL("ThawteSGCCA.crl", certStoreParams.getCRLStore());

        final int tries = 5;

        long start = System.currentTimeMillis();

        for (int i = 0; i < tries; i++) {
            trustAnchors = getTrustAnchors();

            X509CertSelector selector = new X509CertSelector();

            selector.setSerialNumber(BigIntegerUtils.hexDecode("115FD110A82F742D0AE14A71B651962"));
            selector.setIssuer("EMAILADDRESS=ca@example.com, CN=MITM Test CA, L=Amsterdam, ST=NH, C=NL");

            CertificatePathBuilder builder = new PKIXCertificatePathBuilder();

            builder.setTrustAnchors(trustAnchors);
            builder.addCertPathChecker(new SMIMEExtendedKeyUsageCertPathChecker());
            builder.addCertStore(certStore);
            builder.setRevocationEnabled(true);

            CertPathBuilderResult result = builder.buildPath(selector);

            List<? extends Certificate> certificates = result.getCertPath().getCertificates();

            assertEquals(2, certificates.size());
            assertEquals("115FD110A82F742D0AE14A71B651962",
                    X509CertificateInspector.getSerialNumberHex((X509Certificate) certificates.get(0)));
            assertEquals("115FCAD6B536FD8D49E72922CD1F0DA",
                    X509CertificateInspector.getSerialNumberHex((X509Certificate) certificates.get(1)));
        }

        System.out.println("testBuildPath. Seconds / try: " + (System.currentTimeMillis() - start) * 0.001 / tries);
    }

    /*
     * Test a certificate path building with a certificate containing a critical extended key usage. Default
     * version of BC path builder does not understand this critical extension. 
     */
    @Test
    public void testBuildPathEKUCriticalNoEmailProtection() throws Exception {
        // add roots
        addCertificates("mitm-test-root.cer", rootStoreParams.getCertStore());

        addCertificates("mitm-test-ca.cer", certStoreParams.getCertStore());
        addCertificates("testCertificates.p7b", certStoreParams.getCertStore());

        addCRL("test-ca.crl", certStoreParams.getCRLStore());
        addCRL("test-root-ca-not-revoked.crl", certStoreParams.getCRLStore());

        trustAnchors = getTrustAnchors();

        X509CertSelector selector = new X509CertSelector();

        selector.setSerialNumber(BigIntegerUtils.hexDecode("116A448F117FF69FE4F2D4D38F689D7"));
        selector.setIssuer("EMAILADDRESS=ca@example.com, CN=MITM Test CA, L=Amsterdam, ST=NH, C=NL");

        CertificatePathBuilder builder = new PKIXCertificatePathBuilder();

        builder.setTrustAnchors(trustAnchors);
        builder.addCertStore(certStore);
        builder.setRevocationEnabled(true);

        try {
            builder.buildPath(selector);

            fail();
        } catch (CertPathBuilderException e) {
            // CertPathValidatorException should have been thrown because the certificate has a 
            // key usage extension that is critical.
            Throwable cause = ExceptionUtils.getCause(e);

            assertTrue(cause instanceof CertPathValidatorException);
            assertNotNull(cause);
            assertEquals("Certificate has unsupported critical extension", cause.getMessage());
        }
    }

    /*
     * Test path building for a certificate with critical extended key usage containing EMAILPROTECTION when 
     * SMIMEExtendedKeyUsageCertPathChecker is added to the path checkers
     */
    @Test
    public void testBuildPathEKUCriticalCertPathCheckerAdded() throws Exception {
        // add roots
        addCertificates("mitm-test-root.cer", rootStoreParams.getCertStore());

        addCertificates("mitm-test-ca.cer", certStoreParams.getCertStore());
        addCertificates("testCertificates.p7b", certStoreParams.getCertStore());

        addCRL("test-ca.crl", certStoreParams.getCRLStore());
        addCRL("test-root-ca-not-revoked.crl", certStoreParams.getCRLStore());

        trustAnchors = getTrustAnchors();

        X509CertSelector selector = new X509CertSelector();

        selector.setSerialNumber(BigIntegerUtils.hexDecode("116A448F117FF69FE4F2D4D38F689D7"));
        selector.setIssuer("EMAILADDRESS=ca@example.com, CN=MITM Test CA, L=Amsterdam, ST=NH, C=NL");

        CertificatePathBuilder builder = new PKIXCertificatePathBuilder();

        builder.setTrustAnchors(trustAnchors);
        builder.addCertPathChecker(new SMIMEExtendedKeyUsageCertPathChecker());
        builder.addCertStore(certStore);
        builder.setRevocationEnabled(true);

        CertPathBuilderResult result = builder.buildPath(selector);

        assertEquals(2, result.getCertPath().getCertificates().size());
    }

    /*
     * Test a certificate path building with a certificate containing a critical extended key usage without the 
     * EMAILPROTECTION parameter. Default version of BC path builder does not understand this critical extension so 
     * we add a SMIMEExtendedKeyUsageCertPathChecker. The certificate does not contain EMAILPROTECTION so the 
     * CertPath checker will throw an exception.
     */
    @Test
    public void testBuildPathEKUCriticalNoEmailProtectionCertPathCheckerAdded() throws Exception {
        // add roots
        addCertificates("mitm-test-root.cer", rootStoreParams.getCertStore());

        addCertificates("mitm-test-ca.cer", certStoreParams.getCertStore());
        addCertificates("testCertificates.p7b", certStoreParams.getCertStore());

        addCRL("test-ca.crl", certStoreParams.getCRLStore());
        addCRL("test-root-ca-not-revoked.crl", certStoreParams.getCRLStore());

        trustAnchors = getTrustAnchors();

        X509CertSelector selector = new X509CertSelector();

        selector.setSerialNumber(BigIntegerUtils.hexDecode("115FD035BA042503BCC6CA44680F9F8"));
        selector.setIssuer("EMAILADDRESS=ca@example.com, CN=MITM Test CA, L=Amsterdam, ST=NH, C=NL");

        CertificatePathBuilder builder = new PKIXCertificatePathBuilder();

        builder.setTrustAnchors(trustAnchors);
        builder.addCertPathChecker(new SMIMEExtendedKeyUsageCertPathChecker());
        builder.addCertStore(certStore);
        builder.setRevocationEnabled(true);

        try {
            builder.buildPath(selector);

            fail();
        } catch (CertPathBuilderException e) {
            // CertPathValidatorException should have been thrown because the certificate has a 
            // key usage extension that is critical.
            Throwable cause = ExceptionUtils.getRootCause(e);

            assertTrue(cause instanceof CertPathValidatorException);

            assertEquals(SMIMEExtendedKeyUsageCertPathChecker.MISSING_SMIME_EXTENDED_KEY_USAGE, cause.getMessage());
        }
    }
}