org.ejbca.extra.ra.ExtRATestClient.java Source code

Java tutorial

Introduction

Here is the source code for org.ejbca.extra.ra.ExtRATestClient.java

Source

/*************************************************************************
 *                                                                       *
 *  EJBCA: The OpenSource Certificate Authority                          *
 *                                                                       *
 *  This software is free software; you can redistribute it and/or       *
 *  modify it under the terms of the GNU Lesser General Public           *
 *  License as published by the Free Software Foundation; either         *
 *  version 2.1 of the License, or any later version.                    *
 *                                                                       *
 *  See terms of license at gnu.org.                                     *
 *                                                                       *
 *************************************************************************/
package org.ejbca.extra.ra;

import java.io.FileInputStream;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.Vector;

import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.ejbca.extra.db.Message;
import org.ejbca.extra.db.MessageHome;
import org.ejbca.extra.db.PKCS10Request;
import org.ejbca.extra.db.PKCS10Response;
import org.ejbca.extra.db.PKCS12Request;
import org.ejbca.extra.db.PKCS12Response;
import org.ejbca.extra.db.SubMessages;
import org.ejbca.extra.util.RAKeyStore;
import org.ejbca.util.CertTools;
import org.ejbca.util.CryptoProviderTools;

/**
 * A test client for the External RA API that can be used for simple load and performance test and
 * to figure out expected response times for different types of requests.
 * 
 * Important, if signing is used must the RA server key store be configured as an 'Super
 * Administrator' in EJBCA.
 * 
 * @version $Id: ExtRATestClient.java 10226 2010-10-19 12:59:56Z anatom $
 */
public class ExtRATestClient {

    private static final Log log = LogFactory.getLog(ExtRATestClient.class);

    //private static final String TYPE_CERT     = "CERT";
    private static final String TYPE_KEYSTORE = "KEYSTORE";

    private static final String SECURITY_UNSECURED = "UNSECURED";
    private static final String SECURITY_SIGNED = "SIGNED";
    private static final String SECURITY_ENCRYPTED = "ENCRYPTED";
    private static final String SECURITY_SIGNEDENCRYPTED = "SIGNEDENCRYPTED";

    private static final int ARG_TYPE = 0;
    private static final int ARG_KEYSTOREPATH = 1;
    private static final int ARG_PASSWORD = 2;
    private static final int ARG_ENCRYPTIONCERT = 3;
    private static final int ARG_SECURITYLEVEL = 4;
    private static final int ARG_REQUESTSPERMIN = 5;
    private static final int ARG_CONCURRENTRAS = 6;
    private static final int ARG_WAITTIME = 7;

    protected PrivateKey raKey = null;
    protected X509Certificate raCert = null;
    protected Vector cAChain = null;
    protected X509Certificate encCert = null;
    protected String securitylevel = SECURITY_UNSECURED;

    protected boolean requestKeyStore = false;

    protected int reqPerMin = 10;
    private int concurrentRAs = 2;
    protected int waitTime = 30;

    protected int generateUserRequests = 0;

    protected SecureRandom random = new SecureRandom();

    protected static final String pkcs10_1 = "MIIBkzCB/QIBADBUMQswCQYDVQQGEwJTRTETMBEGA1UECBMKU29tZS1TdGF0ZTEh"
            + "MB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQ0wCwYDVQQDEwRUZXN0"
            + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDczgi13kcTGTMmOdMU/QzvH6JV"
            + "QxL23dqdYpsV//XHO2bjKlgqqc3MpGH4QQkz/80rzFi4EwuqBpOnXo0P09I2jztk"
            + "IG4TSM+RwOfvaAMDJ1B6eeih6JX+v0A5PaWJlx1nshUuikcYJK3iNVepy39li0m3"
            + "OBwub9NnnVWXuClUGwIDAQABoAAwDQYJKoZIhvcNAQEEBQADgYEAz4NpjNraufWg"
            + "ZDv5J1muOHwZvOO9Is1L8WvMLG+jgH8Q2rPpDq8buIIWDy6VK8ghr7xhZzEZznTX"
            + "5HLSLB1a6KvktiVSKB0nmAmDU28xXLWWwkA7/68J6DvAipk00bHdxuEJ4+Mg8UJ0"
            + "Mr+aXDlmZUfghzlB70dDUy/Np/YJVb8=";

    private MessageHome msghome = null;

    ExtRATestClient(String[] args) throws Exception {
        CryptoProviderTools.installBCProvider();
        if (args.length != 8) {
            log.debug("Number of arguments: " + args.length);
            help();
            System.exit(-1); // NOPMD, it's not a JEE app
        } else {
            requestKeyStore = args[ARG_TYPE].equalsIgnoreCase(TYPE_KEYSTORE);
            EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("external-ra-cli");
            msghome = new MessageHome(entityManagerFactory, MessageHome.MESSAGETYPE_EXTRA, true);
            securitylevel = args[ARG_SECURITYLEVEL];
            if (!securitylevel.equalsIgnoreCase(SECURITY_UNSECURED)
                    && !securitylevel.equalsIgnoreCase(SECURITY_SIGNED)
                    && !securitylevel.equalsIgnoreCase(SECURITY_ENCRYPTED)
                    && !securitylevel.equalsIgnoreCase(SECURITY_SIGNEDENCRYPTED)) {
                throw new Exception("Invalid SecurityLevel: " + securitylevel);
            }
            if (securitylevel.equalsIgnoreCase(SECURITY_SIGNED)
                    || securitylevel.equalsIgnoreCase(SECURITY_SIGNEDENCRYPTED)) {
                RAKeyStore rakeystore = new RAKeyStore(args[ARG_KEYSTOREPATH], args[ARG_PASSWORD]);
                Certificate[] chain = rakeystore.getKeyStore().getCertificateChain(rakeystore.getAlias());
                cAChain = new Vector();
                for (int i = 0; i < chain.length; i++) {
                    if (((X509Certificate) chain[i]).getBasicConstraints() != -1) {
                        cAChain.add(chain[i]);
                    }
                }
                raKey = (PrivateKey) rakeystore.getKeyStore().getKey(rakeystore.getAlias(),
                        args[ARG_PASSWORD].toCharArray());
                raCert = (X509Certificate) rakeystore.getKeyStore().getCertificate(rakeystore.getAlias());
            }
            if (securitylevel.equalsIgnoreCase(SECURITY_ENCRYPTED)
                    || securitylevel.equalsIgnoreCase(SECURITY_SIGNEDENCRYPTED)) {
                CertificateFactory cf = CertTools.getCertificateFactory();
                encCert = (X509Certificate) cf.generateCertificate(new FileInputStream(args[ARG_ENCRYPTIONCERT]));
            }
            reqPerMin = Integer.parseInt(args[ARG_REQUESTSPERMIN]);
            concurrentRAs = Integer.parseInt(args[ARG_CONCURRENTRAS]);
            waitTime = Integer.parseInt(args[ARG_WAITTIME]);
        }
    }

    public static void main(String[] args) throws Exception {
        ExtRATestClient testclient = new ExtRATestClient(args);
        testclient.run();
    }

    public void help() {
        System.out.println("External RA API Test Client");
        System.out.println(
                "Usage :  <CERT | KEYSTORE> <KeyStorePath> <KeyStorePwd> <EncCert> <SecurityLevel> <RequestsPerMin> <ConcurrentRAs> <WaitTime>");
        System.out.println("Where :");
        System.out.println(
                " <CERT | KEYSTORE> : Type of test, CERT creates single PKCS10 requests, KEYSTORE creates one PKCS10 and one PKCS12 request for each message");
        System.out.println(
                " <KeyStorePath>    : The path to the keystore used to sign/encrypt messages. Use NOKEYSTORE for unencrypted security level.");
        System.out.println(
                " <KeyStorePwd>     : Password to unlock the keystore,. Use NOPWD for unencrypted security level.");
        System.out.println(
                " <EncCert>         : Path to certificate (DER) used to encrypt messages,. Use NOCERT for unencrypted security level.");
        System.out.println(" <SecurityLevel>   : Security Level, Valid values are " + SECURITY_UNSECURED + ", "
                + SECURITY_SIGNED + ", " + SECURITY_ENCRYPTED + ", " + SECURITY_SIGNEDENCRYPTED + "");
        System.out.println(" <RequestsPerMin>  : Requests to generate every minute per concurrent RA.");
        System.out.println(" <ConcurrentRAs>   : Number of concurrent RAs that will create requests.");
        System.out.println(
                " <WaitTime>        : Number of seconds to wait for answer before exception is thrown.\n\n");
        System.out.println(
                "The database connection is configured in META-INF/persistence.xml. The JDBC driver JAR should be copied to the endorsed directory.\n");
        System.out.println("Examples : ");
        System.out.println("  Simple test sending unsecured requests every 5 s in one thread ");
        System.out.println("  expecting an answer within 60 s :");
        System.out.println("    java -jar externalra-cli.jar CERT NOKEYSTORE NOPWD NOCERT " + SECURITY_UNSECURED
                + " 12 1 60 \n");
        System.out.println(
                "  Advanced test using encrypted and signed requests for pkcs10 cert and a pkcs12 keystore ");
        System.out.println("  every 1 min in two threads, expecting an answer within 60 s :");
        System.out.println(
                "    java -jar externalra-cli.jar KEYSTORE rakeystore.p12 foo123 enccert.cer SIGNEDENCRYPTED 1 2 60 \n");
    }

    /**
     * Starts the test application
     */
    public void run() {
        for (int i = 1; i <= concurrentRAs; i++) {
            ConcurrentRAThread rAThread = new ConcurrentRAThread("Thread-" + i);
            println("Starting new Thread : " + "Thread-" + i);
            rAThread.start();
        }
    }

    public synchronized void createUser(String username, SubMessages submessages) {
        msghome.create(username, submessages);
    }

    public synchronized Message findByUser(String username) {
        return msghome.findByMessageId(username);
    }

    public synchronized void println(String message) {
        System.out.println(message);
        System.out.flush();
    }

    private class ConcurrentRAThread extends Thread { // NOPMD, it's not a JEE app
        private boolean run = false;
        private String threadName = "";
        private long serialNumber = 0;

        public ConcurrentRAThread(String threadName) {
            this.threadName = threadName;
        }

        public void run() {
            run = true;
            while (run) {
                try {
                    // Start Request Thread
                    RequestThread reqThread = new RequestThread(threadName, serialNumber++);
                    reqThread.start();

                } catch (Exception e) {
                    e.printStackTrace();
                }
                try {
                    sleep(getTimeToNextRequests());
                } catch (InterruptedException e) {
                }
            }
        }

        private long getTimeToNextRequests() {
            return (60000 / reqPerMin) + (random.nextLong() % 1000);
        }

        public void stopThread() {
            this.run = false;
        }
    }

    private class RequestThread extends Thread { // NOPMD, it's not a JEE app
        private boolean run = false;
        private String threadName = "";
        private long serialNumber = 0;

        public RequestThread(String threadName, long serialNumber) {
            this.threadName = threadName;
            this.serialNumber = serialNumber;
        }

        public void run() {
            // Generate request
            String username = "TEST_" + threadName + "_REQ-" + serialNumber;
            long pkcs10RequestId = 0;
            long pkcs12RequestId = 0;
            long starttime = new Date().getTime();
            SubMessages submgs = generateSubMessage();
            pkcs10RequestId = createPKCS10Request(username, submgs);
            if (requestKeyStore) {
                pkcs12RequestId = createPKCS12Request(username, submgs);
            }
            createUser(username, submgs);

            run = true;
            // Wait for response
            boolean processed = false;
            Message msg = null;
            int wait = waitTime;
            while (wait >= 0 && run) {
                msg = findByUser(username);
                if (msg != null && msg.getStatus().equals(Message.STATUS_PROCESSED)) {
                    processed = true;
                    break;
                }
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                }
                wait--;
            }
            if (!processed) {
                println("Error : Couldn't get processed response within the specified waitTime : Username :"
                        + username + ", WaitTime : " + waitTime);

            } else {
                SubMessages respmsgs = null;
                if (raKey != null) {
                    respmsgs = msg.getSubMessages(raKey, cAChain, null);
                } else {
                    respmsgs = msg.getSubMessages(null, null, null);
                }
                PKCS10Response pkcs10resp = (PKCS10Response) respmsgs.getSubMessages().get(0);
                PKCS12Response pkcs12resp = null;
                if (requestKeyStore) {
                    pkcs12resp = (PKCS12Response) respmsgs.getSubMessages().get(1);
                }
                if (pkcs10resp.getRequestId() != pkcs10RequestId) {
                    println("Error in PKCS10 Request requestId doesn't match responseId for user : " + username
                            + ", request Id : " + pkcs10RequestId + " = " + pkcs10resp.getRequestId());
                }
                if (requestKeyStore && pkcs12resp.getRequestId() != pkcs12RequestId) {
                    println("Error in PKCS12 Request requestId doesn't match responseId for user : " + username
                            + ", request Id : " + pkcs12RequestId + " = " + pkcs12resp.getRequestId());
                }
                if (!pkcs10resp.isSuccessful()) {
                    println("Error in PKCS10 Request for user : " + username + ", message : "
                            + pkcs10resp.getFailInfo());
                }
                if (requestKeyStore && !pkcs12resp.isSuccessful()) {
                    println("Error in PKCS12 Request for user : " + username + ", message : "
                            + pkcs12resp.getFailInfo());
                }
                long endtime = new Date().getTime();
                float processtime = ((float) (endtime - starttime)) / 1000;
                if (pkcs10resp.isSuccessful() && !requestKeyStore) {
                    println("  " + username + " Generated Sucessfully in " + processtime
                            + " seconds, Total Requests " + ++generateUserRequests);
                }
                if (requestKeyStore && pkcs10resp.isSuccessful() && pkcs12resp.isSuccessful()) {
                    println("  " + username + " Generated Sucessfully in " + processtime
                            + " seconds, Total Requests " + ++generateUserRequests);
                }
            }

        }

        private long createPKCS10Request(String username, SubMessages submessages) {
            long requestId = random.nextLong();
            submessages.addSubMessage(
                    new PKCS10Request(requestId, username, "CN=PKCS10REQ", "RFC822NAME=PKCS10Request@test.com",
                            "PKCS10Request@test.com", null, "EMPTY", "ENDUSER", "AdminCA1", pkcs10_1));
            return requestId;
        }

        private long createPKCS12Request(String username, SubMessages submessages) {
            long requestId = random.nextLong();
            submessages.addSubMessage(new PKCS12Request(requestId, username, "CN=PKCS12REQ",
                    "RFC822NAME=PKCS12Request@test.com", "PKCS12Request@test.com", null, "EMPTY", "ENDUSER",
                    "AdminCA1", "foo123", PKCS12Request.KEYALG_RSA, "1024", true));
            return requestId;
        }

        private SubMessages generateSubMessage() {
            if (securitylevel.equalsIgnoreCase(SECURITY_SIGNEDENCRYPTED)) {
                return new SubMessages(raCert, raKey, encCert);
            }
            if (securitylevel.equalsIgnoreCase(SECURITY_SIGNED)) {
                return new SubMessages(raCert, raKey, null);
            }
            if (securitylevel.equalsIgnoreCase(SECURITY_ENCRYPTED)) {
                return new SubMessages(null, null, encCert);
            }
            return new SubMessages(null, null, null);
        }

        public void stopThread() {
            this.run = false;
        }
    }
}