org.qipki.ca.tests.http.QiPkiHttpCaTest.java Source code

Java tutorial

Introduction

Here is the source code for org.qipki.ca.tests.http.QiPkiHttpCaTest.java

Source

/*
 * Copyright (c) 2010, Paul Merlin. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package org.qipki.ca.tests.http;

import info.aduna.io.FileUtil;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;

import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;

import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.jce.PKCS10CertificationRequest;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import org.qi4j.api.value.ValueBuilder;

import org.qipki.commons.crypto.states.KeyEscrowPolicy;
import org.qipki.commons.crypto.values.KeyPairSpecValue;
import org.qipki.commons.rest.values.params.CAFactoryParamsValue;
import org.qipki.commons.rest.values.params.CryptoStoreFactoryParamsValue;
import org.qipki.commons.rest.values.params.EscrowedKeyPairFactoryParamsValue;
import org.qipki.commons.rest.values.params.X509FactoryParamsValue;
import org.qipki.commons.rest.values.params.X509ProfileFactoryParamsValue;
import org.qipki.commons.rest.values.params.X509RevocationParamsValue;
import org.qipki.commons.rest.values.representations.CAValue;
import org.qipki.commons.rest.values.representations.CryptoStoreValue;
import org.qipki.commons.rest.values.representations.EscrowedKeyPairValue;
import org.qipki.commons.rest.values.representations.RestListValue;
import org.qipki.commons.rest.values.representations.X509DetailValue;
import org.qipki.commons.rest.values.representations.X509ProfileValue;
import org.qipki.commons.rest.values.representations.X509Value;
import org.qipki.crypto.algorithms.AsymetricAlgorithm;
import org.qipki.crypto.asymetric.AsymetricGeneratorParameters;
import org.qipki.crypto.storage.KeyStoreType;
import org.qipki.crypto.x509.DistinguishedName;
import org.qipki.crypto.x509.ExtendedKeyUsage;
import org.qipki.crypto.x509.KeyUsage;
import org.qipki.crypto.x509.NetscapeCertType;
import org.qipki.crypto.x509.RevocationReason;

import org.json.JSONException;
import org.json.JSONObject;
import org.junit.Ignore;

import static org.junit.Assert.*;
import org.junit.BeforeClass;
import org.junit.Test;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class QiPkiHttpCaTest extends AbstractQiPkiHttpCaTest {

    private static final Logger LOGGER = LoggerFactory.getLogger(QiPkiHttpCaTest.class);

    @BeforeClass
    public static void startQiPkiApplication() {
        qipkiApplication = new QiPkiTestApplicationHttpCa(QiPkiHttpCaTest.class.getSimpleName());
        qipkiApplication.run();
    }

    private String testCryptoStoreName = "MyCryptoStore";
    private String testCaName = "MyCa";

    @Test
    public void testOnce() throws InterruptedException, IOException, JSONException, GeneralSecurityException {
        testCA();
    }

    @Test
    @Ignore // DO NOT WORK !!!
    public void testReindex() throws IOException, JSONException {
        qipkiApplication.stop();

        LOGGER.info("WILL DELETE INDEX REPOSITORY");
        FileUtil.deltree(new File("target/qi4j-index"));
        LOGGER.info("INDEX REPOSITORY DELETED");

        qipkiApplication.run();

        LOGGER.info("HAS INDEX REPOSITORY BEEN REFILLED?");
        // Get CA list
        HttpGet get = new HttpGet(caApi.caListUri().get());
        addAcceptJsonHeader(get);
        String jsonCaList = httpClient.execute(get, strResponseHandler);
        LOGGER.debug("CAs List: {}", new JSONObject(jsonCaList).toString(2));
        RestListValue caList = valueBuilderFactory.newValueFromJSON(RestListValue.class, jsonCaList);
        assertEquals(4, caList.items().get().size());
        LOGGER.info("INDEX REPOSITORY BEEN REFILLED SUCCESSFULLY");
    }

    @Test
    public void testRepeatedly() throws InterruptedException, IOException, JSONException, GeneralSecurityException {
        for (int i = 0; i < 10; i++) {
            testCryptoStoreName = "MyTestCryptoStore" + i;
            testCaName = "MyTestCa" + i;
            testCA();
        }
    }

    @Test
    @Ignore // Ignored for speeding up the release process
    // Run 500 testCA using 20 threads doing 25 testCA each
    public void testMultiThreaded() throws InterruptedException {
        List<Thread> threads = new ArrayList<Thread>();
        List<TestRunnable> runnables = new ArrayList<TestRunnable>();
        for (int i = 0; i < 20; i++) {
            TestRunnable runnable = new TestRunnable(this, i);
            runnables.add(runnable);
            Thread thread = new Thread(runnable);
            threads.add(thread);
            thread.start();
        }
        for (Thread eachThread : threads) {
            eachThread.join();
        }
        List<Exception> failures = new ArrayList<Exception>();
        for (TestRunnable eachRunnable : runnables) {
            if (eachRunnable.failure != null) {
                failures.add(eachRunnable.failure);
                LOGGER.info("One thread failed because of '{}'", eachRunnable.failure.getMessage(),
                        eachRunnable.failure);
            }
        }
        if (!failures.isEmpty()) {
            LOGGER.info("Multithreaded test failed with {} failures", failures.size());
            fail("Multithreaded test failed with " + failures.size() + " failures");
        }
    }

    private class TestRunnable implements Runnable {

        private final int number;
        private final QiPkiHttpCaTest test;
        private Exception failure;

        public TestRunnable(QiPkiHttpCaTest test, int number) {
            this.test = test;
            this.number = number;
        }

        @Override
        public void run() {
            for (int i = 0; i < 25; i++) {
                test.testCryptoStoreName = "ThreadedTestCryptoStore" + number + "-" + i;
                test.testCaName = "ThreadedTestCa" + number + "-" + i;
                try {
                    test.testCA();
                } catch (Exception ex) {
                    failure = ex;
                    break;
                }
            }
        }

    }

    private void testCA() throws InterruptedException, IOException, JSONException, GeneralSecurityException {
        // Get CA list
        HttpGet get = new HttpGet(caApi.caListUri().get());
        addAcceptJsonHeader(get);
        String jsonCaList = httpClient.execute(get, strResponseHandler);
        LOGGER.debug("CAs List: {}", new JSONObject(jsonCaList).toString(2));
        RestListValue caList = valueBuilderFactory.newValueFromJSON(RestListValue.class, jsonCaList);
        CAValue firstCa = (CAValue) caList.items().get().get(0);

        // Get first CA as Value
        get = new HttpGet(firstCa.uri().get());
        addAcceptJsonHeader(get);
        String caJson = httpClient.execute(get, strResponseHandler);
        CAValue ca = valueBuilderFactory.newValueFromJSON(CAValue.class, caJson);
        LOGGER.debug("First CA JSON:\n{}", ca.toJSON());

        // Get first CA CRL
        get = new HttpGet(ca.crlUri().get());
        String crl = httpClient.execute(get, strResponseHandler);
        LOGGER.debug("First CA CRL:\n{}", crl);
        X509CRL x509CRL = cryptio.readCRLPEM(new StringReader(crl));

        // Create a new CryptoStore
        HttpPost post = new HttpPost(caApi.cryptoStoreListUri().get());
        addAcceptJsonHeader(post);
        CryptoStoreFactoryParamsValue csParams = paramsFactory.createCryptoStoreFactoryParams(testCryptoStoreName,
                KeyStoreType.JKS, "changeit".toCharArray());
        post.setEntity(new StringEntity(csParams.toJSON()));
        String csJson = httpClient.execute(post, strResponseHandler);
        CryptoStoreValue cryptoStore = valueBuilderFactory.newValueFromJSON(CryptoStoreValue.class, csJson);

        // Create a new CA
        post = new HttpPost(caApi.caListUri().get());
        addAcceptJsonHeader(post);
        KeyPairSpecValue keyPairSpec = cryptoValuesFactory.createKeySpec(AsymetricAlgorithm.RSA, 512);
        CAFactoryParamsValue caParams = paramsFactory.createCAFactoryParams(cryptoStore.uri().get(), testCaName, 1,
                "CN=" + testCaName, keyPairSpec, null);
        post.setEntity(new StringEntity(caParams.toJSON()));
        caJson = httpClient.execute(post, strResponseHandler);
        ca = valueBuilderFactory.newValueFromJSON(CAValue.class, caJson);

        // Create a new X509Profile
        post = new HttpPost(caApi.x509ProfileListUri().get());
        addAcceptJsonHeader(post);
        X509ProfileFactoryParamsValue profileParams = paramsFactory.createX509ProfileFactoryParams("SSLClient", 1,
                "A simple SSLClient x509 profile for unit tests",
                x509ExtValuesFactory.buildKeyUsagesValue(true,
                        EnumSet.of(KeyUsage.keyEncipherment, KeyUsage.digitalSignature)),
                x509ExtValuesFactory.buildExtendedKeyUsagesValue(false, EnumSet.of(ExtendedKeyUsage.clientAuth)),
                x509ExtValuesFactory.buildNetscapeCertTypesValue(false, EnumSet.of(NetscapeCertType.sslClient)),
                x509ExtValuesFactory.buildBasicConstraintsValue(true, false, 0), null);
        post.setEntity(new StringEntity(profileParams.toJSON()));
        String sslClientProfileJson = httpClient.execute(post, strResponseHandler);
        X509ProfileValue sslClientProfile = valueBuilderFactory.newValueFromJSON(X509ProfileValue.class,
                sslClientProfileJson);

        // Add profile to CA
        post = new HttpPost(ca.uri().get());
        addAcceptJsonHeader(post);
        ValueBuilder<CAValue> caValueBuilder = valueBuilderFactory.newValueBuilder(CAValue.class).withPrototype(ca); // Needed as Values are immutables
        ca = caValueBuilder.prototype();
        ca.allowedX509Profiles().get().add(
                paramsFactory.createX509ProfileAssignment(sslClientProfile.uri().get(), KeyEscrowPolicy.allowed));
        ca = caValueBuilder.newInstance();
        post.setEntity(new StringEntity(ca.toJSON()));
        caJson = httpClient.execute(post, strResponseHandler);
        ca = valueBuilderFactory.newValueFromJSON(CAValue.class, caJson);

        // Request certificate on X509Factory with a PKCS#10 request using the first CA
        KeyPair keyPair = asymGenerator
                .generateKeyPair(new AsymetricGeneratorParameters(AsymetricAlgorithm.RSA, 512));
        PKCS10CertificationRequest pkcs10 = x509Generator.generatePKCS10(new DistinguishedName("CN=qipki"), keyPair,
                new GeneralNames(new GeneralName(GeneralName.rfc822Name, "qipki@codeartisans.org")));
        String pkcs10PEM = cryptio.asPEM(pkcs10).toString();
        LOGGER.debug("Will request a new X509 with the following PKCS#10: " + pkcs10PEM);
        X509FactoryParamsValue x509FactoryParams = paramsFactory.createX509FactoryParams(ca.uri().get(),
                sslClientProfile.uri().get(), pkcs10PEM);
        post = new HttpPost(caApi.x509ListUri().get());
        addAcceptJsonHeader(post);
        post.setEntity(new StringEntity(x509FactoryParams.toJSON()));
        String jsonX509 = httpClient.execute(post, strResponseHandler);
        X509Value newX509 = valueBuilderFactory.newValueFromJSON(X509Value.class, jsonX509);
        LOGGER.debug("New X509 created using /api/x509/factory after POST/302/REDIRECT: {}", newX509.toJSON());

        // Get detailled info about new X509
        get = new HttpGet(newX509.detailUri().get());
        addAcceptJsonHeader(get);
        String jsonX509Detail = httpClient.execute(get, strResponseHandler);
        LOGGER.debug("New X509 detail: {}", new JSONObject(jsonX509Detail).toString(2));
        X509DetailValue newX509Detail = valueBuilderFactory.newValueFromJSON(X509DetailValue.class, jsonX509Detail);

        assertTrue(newX509Detail.keysExtensions().get().extendedKeyUsages().get().extendedKeyUsages().get()
                .contains(ExtendedKeyUsage.clientAuth));
        assertTrue(newX509Detail.keysExtensions().get().netscapeCertTypes().get().netscapeCertTypes().get()
                .contains(NetscapeCertType.sslClient));

        // Get X509 list
        get = new HttpGet(caApi.x509ListUri().get());
        addAcceptJsonHeader(get);
        String jsonX509List = httpClient.execute(get, strResponseHandler);
        LOGGER.debug("X509s List: {}", new JSONObject(jsonX509List).toString(2));
        RestListValue x509List = valueBuilderFactory.newValueFromJSON(RestListValue.class, jsonX509List);
        X509Value firstX509 = (X509Value) x509List.items().get().get(0);

        // Get first X509
        get = new HttpGet(firstX509.uri().get());
        addAcceptJsonHeader(get);
        jsonX509 = httpClient.execute(get, strResponseHandler);
        LOGGER.debug("First X509: {}", new JSONObject(jsonX509).toString(2));
        firstX509 = valueBuilderFactory.newValueFromJSON(X509Value.class, jsonX509);

        // Revoke first X509
        X509RevocationParamsValue x509RevocationParams = paramsFactory
                .createX509RevocationParams(RevocationReason.cessationOfOperation);
        post = new HttpPost(firstX509.revocationUri().get());
        addAcceptJsonHeader(post);
        post.setEntity(new StringEntity(x509RevocationParams.toJSON()));
        String jsonRevocation = httpClient.execute(post, strResponseHandler);
        LOGGER.debug(jsonRevocation);

        // Get KeyPair list
        get = new HttpGet(caApi.escrowedKeyPairListUri().get());
        addAcceptJsonHeader(get);
        String jsonKeyPairList = httpClient.execute(get, strResponseHandler);
        LOGGER.debug("EscrowedKeyPair List: {}", new JSONObject(jsonKeyPairList).toString(2));

        // Create KeyPair
        EscrowedKeyPairFactoryParamsValue escrowParams = paramsFactory
                .createEscrowedKeyPairFactoryParams(AsymetricAlgorithm.RSA, 512);
        post = new HttpPost(caApi.escrowedKeyPairListUri().get());
        addAcceptJsonHeader(post);
        post.setEntity(new StringEntity(escrowParams.toJSON()));
        String jsonEscrowed = httpClient.execute(post, strResponseHandler);
        LOGGER.debug("EscrowedKeyPair : {}", new JSONObject(jsonEscrowed).toString(2));
        EscrowedKeyPairValue ekp = valueBuilderFactory.newValueFromJSON(EscrowedKeyPairValue.class, jsonEscrowed);

        // Recover KeyPair
        get = new HttpGet(ekp.recoveryUri().get());
        addAcceptJsonHeader(get);
        String kpPem = httpClient.execute(get, strResponseHandler);
        LOGGER.debug("EscrowedKeyPair PEM: {}", kpPem);
        KeyPair keypair = cryptio.readKeyPairPEM(new StringReader(kpPem));

        // Issue X509Certificate using an escrowed keypair
        String dn = "CN=qipki-escrowed";
        LOGGER.debug("Will request a new X509 with the following DN: " + dn);
        x509FactoryParams = paramsFactory.createX509FactoryParams(ca.uri().get(), sslClientProfile.uri().get(),
                ekp.uri().get(), dn);
        post = new HttpPost(caApi.x509ListUri().get());
        addAcceptJsonHeader(post);
        post.setEntity(new StringEntity(x509FactoryParams.toJSON()));
        jsonX509 = httpClient.execute(post, strResponseHandler);
        newX509 = valueBuilderFactory.newValueFromJSON(X509Value.class, jsonX509);
        LOGGER.debug("New X509 created using /api/x509/factory and an escrowed keypair after POST/302/REDIRECT: {}",
                newX509.toJSON());

        // Getting new X509 PEM
        get = new HttpGet(newX509.pemUri().get());
        String x509pem = httpClient.execute(get, strResponseHandler);
        LOGGER.debug("X509 created from escrowed keypair PEM: {}", x509pem);
        X509Certificate x509Certificate = cryptio.readX509PEM(new StringReader(x509pem));

        // Getting EscrowedKeyPair from X509Certificate
        get = new HttpGet(newX509.recoveryUri().get());
        kpPem = httpClient.execute(get, strResponseHandler);
        LOGGER.debug("EscrowedKeyPair PEM: {}", kpPem);
        keypair = cryptio.readKeyPairPEM(new StringReader(kpPem));

        // Create local PKCS#12 keystore with keypair, certificate and full certchain
        char[] password = "changeit".toCharArray();
        KeyStore ks = KeyStore.getInstance(KeyStoreType.PKCS12.typeString(), BouncyCastleProvider.PROVIDER_NAME);
        ks.load(null, password);
        ks.setEntry("wow",
                new KeyStore.PrivateKeyEntry(keyPair.getPrivate(), new Certificate[] { x509Certificate }),
                new KeyStore.PasswordProtection(password));
        String base64encodedp12 = cryptio.base64Encode(ks, password);
        System.out.println(base64encodedp12);

        // Exporting CA in a PKCS#12 keystore
        get = new HttpGet(ca.exportUri().get() + "?password=changeit");
        byte[] responseBytes = httpClient.execute(get, bytesResponseHandler);
        ks = KeyStore.getInstance(KeyStoreType.PKCS12.typeString(), BouncyCastleProvider.PROVIDER_NAME);
        ks.load(new ByteArrayInputStream(responseBytes), password);
        base64encodedp12 = cryptio.base64Encode(ks, password);
        System.out.println(base64encodedp12);

        // Exporting CA in a JKS keystore
        get = new HttpGet(ca.exportUri().get() + "?password=changeit&kstype=jks");
        responseBytes = httpClient.execute(get, bytesResponseHandler);
        ks = KeyStore.getInstance(KeyStoreType.JKS.typeString());
        ks.load(new ByteArrayInputStream(responseBytes), password);
        base64encodedp12 = cryptio.base64Encode(ks, password);
        System.out.println(base64encodedp12);
    }

}