org.ejbca.ui.cli.CMPNestedMessageStressTest.java Source code

Java tutorial

Introduction

Here is the source code for org.ejbca.ui.cli.CMPNestedMessageStressTest.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.ui.cli;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.Signature;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;
import java.util.Vector;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERBitString;
import org.bouncycastle.asn1.DERGeneralizedTime;
import org.bouncycastle.asn1.DERInteger;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.DERObjectIdentifier;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DEROutputStream;
import org.bouncycastle.asn1.DERUTF8String;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x509.X509CertificateStructure;
import org.bouncycastle.asn1.x509.X509Extension;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.asn1.x509.X509Name;
import org.bouncycastle.jce.X509KeyUsage;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.ejbca.core.model.InternalResources;
import org.ejbca.core.protocol.cmp.CMPSendHTTP;
import org.ejbca.util.CertTools;
import org.ejbca.util.CryptoProviderTools;
import org.ejbca.util.PerformanceTest;
import org.ejbca.util.PerformanceTest.Command;
import org.ejbca.util.PerformanceTest.CommandFactory;
import org.ejbca.util.keystore.KeyTools;

import com.novosec.pkix.asn1.cmp.CMPObjectIdentifiers;
import com.novosec.pkix.asn1.cmp.CertOrEncCert;
import com.novosec.pkix.asn1.cmp.CertRepMessage;
import com.novosec.pkix.asn1.cmp.CertResponse;
import com.novosec.pkix.asn1.cmp.CertifiedKeyPair;
import com.novosec.pkix.asn1.cmp.PKIBody;
import com.novosec.pkix.asn1.cmp.PKIHeader;
import com.novosec.pkix.asn1.cmp.PKIMessage;
import com.novosec.pkix.asn1.cmp.PKIStatusInfo;
import com.novosec.pkix.asn1.crmf.AttributeTypeAndValue;
import com.novosec.pkix.asn1.crmf.CRMFObjectIdentifiers;
import com.novosec.pkix.asn1.crmf.CertReqMessages;
import com.novosec.pkix.asn1.crmf.CertReqMsg;
import com.novosec.pkix.asn1.crmf.CertRequest;
import com.novosec.pkix.asn1.crmf.CertTemplate;
import com.novosec.pkix.asn1.crmf.OptionalValidity;
import com.novosec.pkix.asn1.crmf.PBMParameter;
import com.novosec.pkix.asn1.crmf.POPOSigningKey;
import com.novosec.pkix.asn1.crmf.ProofOfPossession;

/**
 * Used to stress test the CMP interface.
 * @author primelars
 * @version $Id: CMPNestedMessageStressTest.java 15009 2012-06-18 12:49:30Z primelars $
 *
 */
class CMPNestedMessageStressTest extends ClientToolBox {
    /** Internal localization of logs and errors */
    private static final InternalResources intres = InternalResources.getInstance();

    static private class StressTest {
        final PerformanceTest performanceTest;

        final private KeyPair keyPair;
        final private X509Certificate cacert;
        final private CertificateFactory certificateFactory;
        final private Provider bcProvider = new BouncyCastleProvider();
        final private String keyId;
        final private String hostName;
        final private int port;
        final private boolean isHttp;
        final private PrivateKey innerSingKey;
        private Certificate innerCert;
        private PrivateKey outerSignKey;
        final String urlPath;
        final String resultCertFilePrefix;
        boolean isSign;
        boolean firstTime = true;
        //private int lastNextInt = 0;

        StressTest(final String _hostName, final int _port, final boolean _isHttp,
                final InputStream certInputStream, final int numberOfThreads, final int waitTime,
                final String _keyId, final String _urlPath, final String _resultCertFilePrefix,
                final PrivateKey _outerSingKey, final PrivateKey _innerSignKey, final Certificate _innerCert)
                throws Exception {
            this.hostName = _hostName;
            this.certificateFactory = CertificateFactory.getInstance("X.509", this.bcProvider);
            this.cacert = (X509Certificate) this.certificateFactory.generateCertificate(certInputStream);
            this.keyId = _keyId;
            this.port = _port;
            this.isHttp = _isHttp;
            this.urlPath = _urlPath;
            this.resultCertFilePrefix = _resultCertFilePrefix;

            final KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA");
            keygen.initialize(2048);
            this.keyPair = keygen.generateKeyPair();
            this.outerSignKey = _outerSingKey;
            this.innerSingKey = _innerSignKey;
            this.innerCert = _innerCert;

            this.performanceTest = new PerformanceTest();
            this.performanceTest.execute(new MyCommandFactory(), numberOfThreads, waitTime, System.out);
        }

        private CertRequest genCertReq(final String userDN, final X509Extensions extensions) throws IOException {
            final OptionalValidity myOptionalValidity = new OptionalValidity();
            final int day = 1000 * 60 * 60 * 24;
            myOptionalValidity
                    .setNotBefore(new org.bouncycastle.asn1.x509.Time(new Date(new Date().getTime() - day)));
            myOptionalValidity
                    .setNotAfter(new org.bouncycastle.asn1.x509.Time(new Date(new Date().getTime() + 10 * day)));

            final CertTemplate myCertTemplate = new CertTemplate();
            myCertTemplate.setValidity(myOptionalValidity);
            myCertTemplate.setIssuer(new X509Name(this.cacert.getSubjectDN().getName()));
            myCertTemplate.setSubject(new X509Name(userDN));
            final byte[] bytes = this.keyPair.getPublic().getEncoded();
            final ByteArrayInputStream bIn = new ByteArrayInputStream(bytes);
            final ASN1InputStream dIn = new ASN1InputStream(bIn);
            final SubjectPublicKeyInfo keyInfo = new SubjectPublicKeyInfo((ASN1Sequence) dIn.readObject());
            myCertTemplate.setPublicKey(keyInfo);
            // If we did not pass any extensions as parameter, we will create some of our own, standard ones
            if (extensions == null) {
                // SubjectAltName
                // Some altNames
                final Vector<X509Extension> values = new Vector<X509Extension>();
                final Vector<DERObjectIdentifier> oids = new Vector<DERObjectIdentifier>();
                {
                    final GeneralNames san = CertTools
                            .getGeneralNamesFromAltName("UPN=fooupn@bar.com,rfc822Name=rfc822Name@my.com");
                    final ByteArrayOutputStream bOut = new ByteArrayOutputStream();
                    final DEROutputStream dOut = new DEROutputStream(bOut);
                    dOut.writeObject(san);
                    final byte value[] = bOut.toByteArray();
                    values.add(new X509Extension(false, new DEROctetString(value)));
                    oids.add(X509Extensions.SubjectAlternativeName);
                }
                {
                    // KeyUsage
                    final int bcku = X509KeyUsage.digitalSignature | X509KeyUsage.keyEncipherment
                            | X509KeyUsage.nonRepudiation;
                    final X509KeyUsage ku = new X509KeyUsage(bcku);
                    final ByteArrayOutputStream bOut = new ByteArrayOutputStream();
                    final DEROutputStream dOut = new DEROutputStream(bOut);
                    dOut.writeObject(ku);
                    final byte value[] = bOut.toByteArray();
                    final X509Extension kuext = new X509Extension(false, new DEROctetString(value));
                    values.add(kuext);
                    oids.add(X509Extensions.KeyUsage);
                }
                // Make the complete extension package
                myCertTemplate.setExtensions(new X509Extensions(oids, values));
            } else {
                myCertTemplate.setExtensions(extensions);
            }
            return new CertRequest(new DERInteger(4), myCertTemplate);
        }

        private PKIMessage genPKIMessage(final SessionData sessionData, final boolean raVerifiedPopo,
                final CertRequest certRequest)
                throws NoSuchAlgorithmException, IOException, InvalidKeyException, SignatureException {

            final CertReqMsg myCertReqMsg = new CertReqMsg(certRequest);

            ProofOfPossession myProofOfPossession;
            if (raVerifiedPopo) {
                // raVerified POPO (meaning there is no POPO)
                myProofOfPossession = new ProofOfPossession(new DERNull(), 0);
            } else {
                final ByteArrayOutputStream baos = new ByteArrayOutputStream();
                final DEROutputStream mout = new DEROutputStream(baos);
                mout.writeObject(certRequest);
                mout.close();
                final byte[] popoProtectionBytes = baos.toByteArray();
                final Signature sig = Signature.getInstance(PKCSObjectIdentifiers.sha1WithRSAEncryption.getId());
                sig.initSign(this.keyPair.getPrivate());
                sig.update(popoProtectionBytes);

                final DERBitString bs = new DERBitString(sig.sign());

                final POPOSigningKey myPOPOSigningKey = new POPOSigningKey(
                        new AlgorithmIdentifier(PKCSObjectIdentifiers.sha1WithRSAEncryption), bs);
                //myPOPOSigningKey.setPoposkInput( myPOPOSigningKeyInput );
                myProofOfPossession = new ProofOfPossession(myPOPOSigningKey, 1);
            }

            myCertReqMsg.setPop(myProofOfPossession);

            final AttributeTypeAndValue av = new AttributeTypeAndValue(CRMFObjectIdentifiers.regCtrl_regToken,
                    new DERUTF8String("foo123"));
            myCertReqMsg.addRegInfo(av);

            final CertReqMessages myCertReqMessages = new CertReqMessages(myCertReqMsg);

            final PKIHeader myPKIHeader = new PKIHeader(new DERInteger(2),
                    new GeneralName(new X509Name(sessionData.getUserDN())),
                    new GeneralName(new X509Name(this.cacert.getSubjectDN().getName())));
            myPKIHeader.setMessageTime(new DERGeneralizedTime(new Date()));
            myPKIHeader.setSenderNonce(new DEROctetString(sessionData.getNonce()));
            myPKIHeader.setTransactionID(new DEROctetString(sessionData.getTransId()));

            final PKIBody myPKIBody = new PKIBody(myCertReqMessages, 0); // initialization request
            return new PKIMessage(myPKIHeader, myPKIBody);
        }

        private PKIMessage signPKIMessage(final PKIMessage msg, PrivateKey signingKey)
                throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException, SignatureException {
            PKIMessage message = msg;
            final Signature sig = Signature.getInstance(PKCSObjectIdentifiers.sha1WithRSAEncryption.getId(), "BC");
            sig.initSign(signingKey);
            sig.update(message.getProtectedBytes());
            byte[] eeSignature = sig.sign();
            message.setProtection(new DERBitString(eeSignature));
            return message;
        }

        private byte[] sendCmp(final byte[] message, final SessionData sessionData) throws Exception {
            if (StressTest.this.isHttp) {
                return sendCmpHttp(message);
            }
            return null;
        }

        private byte[] sendCmpHttp(final byte[] message) throws Exception {
            final CMPSendHTTP send = CMPSendHTTP.doIt(message, StressTest.this.hostName, StressTest.this.port,
                    StressTest.this.urlPath, false);
            if (send.responseCode != HttpURLConnection.HTTP_OK) {
                StressTest.this.performanceTest.getLog().error(
                        intres.getLocalizedMessage("cmp.responsecodenotok", Integer.valueOf(send.responseCode)));
                return null;
            }
            if (send.contentType == null) {
                StressTest.this.performanceTest.getLog().error("No content type received.");
                return null;
            }
            // Some appserver (Weblogic) responds with "application/pkixcmp; charset=UTF-8"
            if (!send.contentType.startsWith("application/pkixcmp")) {
                StressTest.this.performanceTest.getLog().info("wrong content type: " + send.contentType);
            }
            return send.response;
        }

        private boolean checkCmpResponseGeneral(final byte[] retMsg, final SessionData sessionData,
                final boolean requireProtection) throws Exception {
            //
            // Parse response message
            //
            final PKIMessage respObject = PKIMessage
                    .getInstance(new ASN1InputStream(new ByteArrayInputStream(retMsg)).readObject());
            if (respObject == null) {
                StressTest.this.performanceTest.getLog().error("No command response message.");
                return false;
            }

            // The signer, i.e. the CA, check it's the right CA
            final PKIHeader header = respObject.getHeader();
            if (header == null) {
                StressTest.this.performanceTest.getLog().error("No header in response message.");
                return false;
            }
            // Check that the signer is the expected CA
            final X509Name name = X509Name.getInstance(header.getSender().getName());
            if (header.getSender().getTagNo() != 4 || name == null || !name.equals(this.cacert.getSubjectDN())) {
                StressTest.this.performanceTest.getLog().error("Not signed by right issuer.");
            }

            if (header.getSenderNonce().getOctets().length != 16) {
                StressTest.this.performanceTest.getLog()
                        .error("Wrong length of received sender nonce (made up by server). Is "
                                + header.getSenderNonce().getOctets().length + " byte but should be 16.");
            }

            if (!Arrays.equals(header.getRecipNonce().getOctets(), sessionData.getNonce())) {
                StressTest.this.performanceTest.getLog()
                        .error("recipient nonce not the same as we sent away as the sender nonce. Sent: "
                                + Arrays.toString(sessionData.getNonce()) + " Received: "
                                + Arrays.toString(header.getRecipNonce().getOctets()));
            }

            if (!Arrays.equals(header.getTransactionID().getOctets(), sessionData.getTransId())) {
                StressTest.this.performanceTest.getLog().error("transid is not the same as the one we sent");
            }
            {
                // Check that the message is signed with the correct digest alg
                final AlgorithmIdentifier algId = header.getProtectionAlg();
                if (algId == null || algId.getObjectId() == null || algId.getObjectId().getId() == null) {
                    if (requireProtection) {
                        StressTest.this.performanceTest.getLog().error("Not possible to get algorithm.");
                        return false;
                    }
                    return true;
                }
                final String id = algId.getObjectId().getId();
                if (id.equals(PKCSObjectIdentifiers.sha1WithRSAEncryption.getId())) {
                    if (this.firstTime) {
                        this.firstTime = false;
                        this.isSign = true;
                        StressTest.this.performanceTest.getLog().info("Signature protection used.");
                    } else if (!this.isSign) {
                        StressTest.this.performanceTest.getLog()
                                .error("Message password protected but should be signature protected.");
                    }
                } else if (id.equals(CMPObjectIdentifiers.passwordBasedMac.getId())) {
                    if (this.firstTime) {
                        this.firstTime = false;
                        this.isSign = false;
                        StressTest.this.performanceTest.getLog().info("Password (PBE) protection used.");
                    } else if (this.isSign) {
                        StressTest.this.performanceTest.getLog()
                                .error("Message signature protected but should be password protected.");
                    }
                } else {
                    StressTest.this.performanceTest.getLog().error("No valid algorithm.");
                    return false;
                }
            }
            if (this.isSign) {
                // Verify the signature
                byte[] protBytes = respObject.getProtectedBytes();
                final DERBitString bs = respObject.getProtection();
                final Signature sig;
                try {
                    sig = Signature.getInstance(PKCSObjectIdentifiers.sha1WithRSAEncryption.getId());
                    sig.initVerify(this.cacert);
                    sig.update(protBytes);
                    if (!sig.verify(bs.getBytes())) {
                        StressTest.this.performanceTest.getLog().error("CA signature not verifying");
                    }
                } catch (Exception e) {
                    StressTest.this.performanceTest.getLog().error("Not possible to verify signature.", e);
                }
            } else {
                //final DEROctetString os = header.getSenderKID();
                //if ( os!=null )
                //    StressTest.this.performanceTest.getLog().info("Found a sender keyId: "+new String(os.getOctets()));
                // Verify the PasswordBased protection of the message
                final PBMParameter pp;
                {
                    final AlgorithmIdentifier pAlg = header.getProtectionAlg();
                    // StressTest.this.performanceTest.getLog().info("Protection type is: "+pAlg.getObjectId().getId());
                    pp = PBMParameter.getInstance(pAlg.getParameters());
                }
                final int iterationCount = pp.getIterationCount().getPositiveValue().intValue();
                // StressTest.this.performanceTest.getLog().info("Iteration count is: "+iterationCount);
                final AlgorithmIdentifier owfAlg = pp.getOwf();
                // Normal OWF alg is 1.3.14.3.2.26 - SHA1
                // StressTest.this.performanceTest.getLog().info("Owf type is: "+owfAlg.getObjectId().getId());
                final AlgorithmIdentifier macAlg = pp.getMac();
                // Normal mac alg is 1.3.6.1.5.5.8.1.2 - HMAC/SHA1
                // StressTest.this.performanceTest.getLog().info("Mac type is: "+macAlg.getObjectId().getId());
                final byte[] salt = pp.getSalt().getOctets();
                //log.info("Salt is: "+new String(salt));
                final byte[] raSecret = new String("password").getBytes();
                // HMAC/SHA1 os normal 1.3.6.1.5.5.8.1.2 or 1.2.840.113549.2.7 
                final String macOid = macAlg.getObjectId().getId();
                final SecretKey key;
                {
                    byte[] basekey = new byte[raSecret.length + salt.length];
                    for (int i = 0; i < raSecret.length; i++) {
                        basekey[i] = raSecret[i];
                    }
                    for (int i = 0; i < salt.length; i++) {
                        basekey[raSecret.length + i] = salt[i];
                    }
                    // Construct the base key according to rfc4210, section 5.1.3.1
                    final MessageDigest dig = MessageDigest.getInstance(owfAlg.getObjectId().getId(),
                            this.bcProvider);
                    for (int i = 0; i < iterationCount; i++) {
                        basekey = dig.digest(basekey);
                        dig.reset();
                    }
                    key = new SecretKeySpec(basekey, macOid);
                }
                final Mac mac = Mac.getInstance(macOid, this.bcProvider);
                mac.init(key);
                mac.reset();
                final byte[] protectedBytes = respObject.getProtectedBytes();
                final DERBitString protection = respObject.getProtection();
                mac.update(protectedBytes, 0, protectedBytes.length);
                byte[] out = mac.doFinal();
                // My out should now be the same as the protection bits
                byte[] pb = protection.getBytes();
                if (!Arrays.equals(out, pb)) {
                    StressTest.this.performanceTest.getLog().error("Wrong PBE hash");
                }
            }
            return true;
        }

        private X509Certificate checkCmpCertRepMessage(final SessionData sessionData, final byte[] retMsg,
                final int requestId) throws IOException, CertificateException {
            //
            // Parse response message
            //
            final PKIMessage respObject = PKIMessage
                    .getInstance(new ASN1InputStream(new ByteArrayInputStream(retMsg)).readObject());
            if (respObject == null) {
                StressTest.this.performanceTest.getLog().error("No PKIMessage for certificate received.");
                return null;
            }
            final PKIBody body = respObject.getBody();
            if (body == null) {
                StressTest.this.performanceTest.getLog().error("No PKIBody for certificate received.");
                return null;
            }
            if (body.getTagNo() != 1) {
                StressTest.this.performanceTest.getLog().error("Cert body tag not 1.");
                return null;
            }
            final CertRepMessage c = body.getIp();
            if (c == null) {
                StressTest.this.performanceTest.getLog().error("No CertRepMessage for certificate received.");
                return null;
            }
            final CertResponse resp = c.getResponse(0);
            if (resp == null) {
                StressTest.this.performanceTest.getLog().error("No CertResponse for certificate received.");
                return null;
            }
            if (resp.getCertReqId().getValue().intValue() != requestId) {
                StressTest.this.performanceTest.getLog().error("Received CertReqId is "
                        + resp.getCertReqId().getValue().intValue() + " but should be " + requestId);
                return null;
            }
            final PKIStatusInfo info = resp.getStatus();
            if (info == null) {
                StressTest.this.performanceTest.getLog().error("No PKIStatusInfo for certificate received.");
                return null;
            }
            if (info.getStatus().getValue().intValue() != 0) {
                StressTest.this.performanceTest.getLog()
                        .error("Received Status is " + info.getStatus().getValue().intValue() + " but should be 0");
                return null;
            }
            final CertifiedKeyPair kp = resp.getCertifiedKeyPair();
            if (kp == null) {
                StressTest.this.performanceTest.getLog().error("No CertifiedKeyPair for certificate received.");
                return null;
            }
            final CertOrEncCert cc = kp.getCertOrEncCert();
            if (cc == null) {
                StressTest.this.performanceTest.getLog().error("No CertOrEncCert for certificate received.");
                return null;
            }
            final X509CertificateStructure struct = cc.getCertificate();
            if (struct == null) {
                StressTest.this.performanceTest.getLog()
                        .error("No X509CertificateStructure for certificate received.");
                return null;
            }
            final byte encoded[] = struct.getEncoded();
            if (encoded == null || encoded.length <= 0) {
                StressTest.this.performanceTest.getLog().error("No encoded certificate received.");
                return null;
            }
            final X509Certificate cert = (X509Certificate) this.certificateFactory
                    .generateCertificate(new ByteArrayInputStream(encoded));
            if (cert == null) {
                StressTest.this.performanceTest.getLog().error("Not possbile to create certificate.");
                return null;
            }
            // Remove this test to be able to test unid-fnr
            if (cert.getSubjectDN().hashCode() != new X509Name(sessionData.getUserDN()).hashCode()) {
                StressTest.this.performanceTest.getLog().error("Subject is '" + cert.getSubjectDN()
                        + "' but should be '" + sessionData.getUserDN() + '\'');
                return null;
            }
            if (cert.getIssuerX500Principal().hashCode() != this.cacert.getSubjectX500Principal().hashCode()) {
                StressTest.this.performanceTest.getLog().error("Issuer is '" + cert.getIssuerDN()
                        + "' but should be '" + this.cacert.getSubjectDN() + '\'');
                return null;
            }
            try {
                cert.verify(this.cacert.getPublicKey());
            } catch (Exception e) {
                StressTest.this.performanceTest.getLog().error("Certificate not verifying. See exception", e);
                return null;
            }
            return cert;
        }

        private class GetCertificate implements Command {
            final private SessionData sessionData;

            GetCertificate(final SessionData sd) {
                this.sessionData = sd;
            }

            public boolean doIt() throws Exception {
                this.sessionData.newSession();

                CertRequest certReq = genCertReq(this.sessionData.getUserDN(), null);
                PKIMessage certMsg = genPKIMessage(this.sessionData, false, certReq);
                if (certMsg == null) {
                    StressTest.this.performanceTest.getLog().error("No certificate request.");
                    return false;
                }
                AlgorithmIdentifier pAlg = new AlgorithmIdentifier(PKCSObjectIdentifiers.sha1WithRSAEncryption);
                certMsg.getHeader().setProtectionAlg(pAlg);
                certMsg.getHeader().setSenderKID(new DEROctetString("EMPTY".getBytes()));
                PKIMessage signedMsg = signPKIMessage(certMsg, innerSingKey);
                addExtraCert(signedMsg, innerCert);
                if (signedMsg == null) {
                    StressTest.this.performanceTest.getLog().error("No protected message.");
                    return false;
                }

                //               org.bouncycastle.asn1.x509.Time nb = new org.bouncycastle.asn1.x509.Time(new DERGeneralizedTime("20030211002120Z"));
                //               org.bouncycastle.asn1.x509.Time na = new org.bouncycastle.asn1.x509.Time(new Date()); 
                PKIHeader myPKIHeader = new PKIHeader(new DERInteger(2),
                        new GeneralName(new X509Name("CN=CMSSender,C=SE")),
                        new GeneralName(new X509Name(((X509Certificate) cacert).getSubjectDN().getName())));
                myPKIHeader.setMessageTime(new DERGeneralizedTime(new Date()));
                // senderNonce
                myPKIHeader.setSenderNonce(new DEROctetString(this.sessionData.nonce));
                // TransactionId
                myPKIHeader.setTransactionID(new DEROctetString(this.sessionData.nonce));
                //myPKIHeader.addGeneralInfo(new InfoTypeAndValue(ASN1Sequence.getInstance(crmfMsg)));

                PKIBody myPKIBody = new PKIBody(signedMsg, 20); // NestedMessageContent
                PKIMessage myPKIMessage = new PKIMessage(myPKIHeader, myPKIBody);
                PKIMessage cmsMessage = signPKIMessage(myPKIMessage, outerSignKey);

                this.sessionData.setReqId(signedMsg.getBody().getIr().getCertReqMsg(0).getCertReq().getCertReqId()
                        .getValue().intValue());
                final ByteArrayOutputStream bao = new ByteArrayOutputStream();
                final DEROutputStream out = new DEROutputStream(bao);
                out.writeObject(cmsMessage);
                final byte[] ba = bao.toByteArray();
                // Send request and receive response
                final byte[] resp = sendCmp(ba, this.sessionData);
                if (resp == null || resp.length <= 0) {
                    StressTest.this.performanceTest.getLog().error("No response message.");
                    return false;
                }
                if (!checkCmpResponseGeneral(resp, this.sessionData, true)) {
                    return false;
                }
                final X509Certificate cert = checkCmpCertRepMessage(this.sessionData, resp,
                        this.sessionData.getReqId());
                if (cert == null) {
                    return false;
                }
                final BigInteger serialNumber = CertTools.getSerialNumber(cert);
                if (StressTest.this.resultCertFilePrefix != null) {
                    new FileOutputStream(StressTest.this.resultCertFilePrefix + serialNumber + ".dat")
                            .write(cert.getEncoded());
                }
                StressTest.this.performanceTest.getLog().result(serialNumber);

                return true;
            }

            public String getJobTimeDescription() {
                return "Get certificate";
            }

            private void addExtraCert(PKIMessage msg, Certificate cert)
                    throws CertificateEncodingException, IOException {
                ByteArrayInputStream bIn = new ByteArrayInputStream(cert.getEncoded());
                ASN1InputStream dIn = new ASN1InputStream(bIn);
                ASN1Sequence extraCertSeq = (ASN1Sequence) dIn.readObject();
                X509CertificateStructure extraCert = new X509CertificateStructure(
                        ASN1Sequence.getInstance(extraCertSeq));
                msg.addExtraCert(extraCert);
            }
        }

        class SessionData {
            final private byte[] nonce = new byte[16];
            final private byte[] transid = new byte[16];
            private int lastNextInt = 0;
            private String userDN;
            private int reqId;
            Socket socket;
            final private static int howOftenToGenerateSameUsername = 3; // 0 = never, 1 = 100% chance, 2=50% chance etc.. 

            SessionData() {
                super();
            }

            Socket getSocket() throws UnknownHostException, IOException {
                if (StressTest.this.isHttp) {
                    return null;
                }
                if (this.socket == null || this.socket.isClosed() || !this.socket.isBound()
                        || !this.socket.isConnected() || this.socket.isInputShutdown()
                        || this.socket.isOutputShutdown()) {
                    StressTest.this.performanceTest.getLog()
                            .info("New socket created for thread with '" + this.transid + "'.");
                    this.socket = new Socket(StressTest.this.hostName, StressTest.this.port);
                    this.socket.setKeepAlive(true);
                }
                return this.socket;
            }

            private String getRandomAllDigitString(int length) {
                final String s = Integer.toString(StressTest.this.performanceTest.getRandom().nextInt());
                return s.substring(s.length() - length);
            }

            private String getFnrLra() {
                return getRandomAllDigitString(6) + getRandomAllDigitString(5) + '-' + getRandomAllDigitString(5);
            }

            private int getRandomAndRepeated() {
                // Initialize with some new value every time the test is started
                // Return the same value once in a while so we have multiple requests for the same username
                if (this.lastNextInt == 0 || howOftenToGenerateSameUsername == 0
                        || StressTest.this.performanceTest.getRandom().nextInt()
                                % howOftenToGenerateSameUsername != 0) {
                    this.lastNextInt = StressTest.this.performanceTest.getRandom().nextInt();
                }
                return this.lastNextInt;
            }

            void newSession() {
                this.userDN = "CN=CMP Test User Nr " + getRandomAndRepeated() + ",serialNumber=" + getFnrLra();
                StressTest.this.performanceTest.getRandom().nextBytes(this.nonce);
                StressTest.this.performanceTest.getRandom().nextBytes(this.transid);
            }

            int getReqId() {
                return this.reqId;
            }

            void setReqId(int i) {
                this.reqId = i;
            }

            String getUserDN() {
                return this.userDN;
            }

            byte[] getTransId() {
                return this.transid;
            }

            byte[] getNonce() {
                return this.nonce;
            }
        }

        private class MyCommandFactory implements CommandFactory {
            public Command[] getCommands() throws Exception {
                final SessionData sessionData = new SessionData();
                return new Command[] { new GetCertificate(sessionData) };//, new Revoke(sessionData)};
            }
        }
    }

    /* (non-Javadoc)
     * @see org.ejbca.ui.cli.ClientToolBox#execute(java.lang.String[])
     */
    @Override
    protected void execute(String[] args) {
        final String hostName;
        final String keystoreFile;
        final String keystorePassword;
        final String certNameInKeystore;
        final int numberOfThreads;
        final int waitTime;
        final String certFileName;
        final File certFile;
        final String keyId;
        final int port;
        final boolean isHttp;
        final String urlPath;
        final String resultFilePrefix;
        if (args.length < 6) {
            System.out.println(args[0]
                    + " <host name> <CA certificate file name> <CMS keystore (p12)> <keystore password> <CMS certificate in keystore> [<number of threads>] [<wait time (ms) between each thread is started>] [<KeyId to be sent to server>] [<port>] [<URL path of servlet. use 'null' to get EJBCA (not proxy) default>] [<certificate file prefix. set this if you want all received certificates stored on files>]");
            System.out.println(
                    "EJBCA build configuration requirements: cmp.operationmode=ra, cmp.allowraverifypopo=true, cmp.authenticationmodule=EndEntityCertificate, cmp.authenticationparameters=AdminCA1, checkadminauthorization=false, cmp.racertificatepath=/tmp/racerts");
            System.out.println(
                    "EJBCA build configuration optional: cmp.ra.certificateprofile=KeyId cmp.ra.endentityprofile=KeyId (used when the KeyId argument should be used as profile name).");
            return;
        }
        hostName = args[1];
        certFileName = args[2];
        certFile = new File(certFileName);
        keystoreFile = args[3];
        keystorePassword = args[4];
        certNameInKeystore = args[5];
        numberOfThreads = args.length > 6 ? Integer.parseInt(args[6].trim()) : 1;
        waitTime = args.length > 7 ? Integer.parseInt(args[7].trim()) : 0;
        keyId = args.length > 8 ? args[8].trim() : "EMPTY";
        port = args.length > 9 ? Integer.parseInt(args[9].trim()) : 8080;
        isHttp = true;
        urlPath = args.length > 10 && args[10].toLowerCase().indexOf("null") < 0 ? args[10].trim() : null;
        resultFilePrefix = args.length > 11 ? args[11].trim() : null;

        CryptoProviderTools.installBCProviderIfNotAvailable();

        Certificate innerCert = null;
        PrivateKey innerSignKey = null;

        FileInputStream file_inputstream;
        try {
            file_inputstream = new FileInputStream(keystoreFile);
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            keyStore.load(file_inputstream, keystorePassword.toCharArray());
            System.out.println("Keystore size " + keyStore.size());
            Enumeration aliases = keyStore.aliases();
            while (aliases.hasMoreElements()) {
                System.out.println(aliases.nextElement());
            }
            Key key = keyStore.getKey(certNameInKeystore, keystorePassword.toCharArray());
            System.out.println("Key information " + key.getAlgorithm() + " " + key.getFormat());
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(key.getEncoded());
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            innerSignKey = keyFactory.generatePrivate(keySpec);
            innerCert = keyStore.getCertificate(certNameInKeystore);
        } catch (FileNotFoundException e2) {
            e2.printStackTrace();
            System.exit(-1);
        } catch (KeyStoreException e) {
            e.printStackTrace();
            System.exit(-1);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            System.exit(-1);
        } catch (CertificateException e) {
            e.printStackTrace();
            System.exit(-1);
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(-1);
        } catch (UnrecoverableKeyException e) {
            e.printStackTrace();
            System.exit(-1);
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
            System.exit(-1);
        }

        KeyPair outerSignKeys = null;
        try {
            outerSignKeys = KeyTools.genKeys("1024", "RSA");
            X509Certificate signCert = CertTools.genSelfCert("CN=cmpStressTest,C=SE", 5000000, null,
                    outerSignKeys.getPrivate(), outerSignKeys.getPublic(),
                    PKCSObjectIdentifiers.sha1WithRSAEncryption.getId(), true, "BC");

            Vector<Certificate> certCollection = new Vector<Certificate>();
            certCollection.add(signCert);
            byte[] pemRaCert = CertTools.getPEMFromCerts(certCollection);

            FileOutputStream out = new FileOutputStream(new File("/tmp/racerts/cmpStressTest.pem"));
            out.write(pemRaCert);
            out.close();
        } catch (NoSuchAlgorithmException e1) {
            e1.printStackTrace();
            System.exit(-1);
        } catch (NoSuchProviderException e1) {
            e1.printStackTrace();
            System.exit(-1);
        } catch (InvalidAlgorithmParameterException e1) {
            e1.printStackTrace();
            System.exit(-1);
        } catch (InvalidKeyException e) {
            e.printStackTrace();
            System.exit(-1);
        } catch (CertificateEncodingException e) {
            e.printStackTrace();
            System.exit(-1);
        } catch (SignatureException e) {
            e.printStackTrace();
            System.exit(-1);
        } catch (IllegalStateException e) {
            e.printStackTrace();
            System.exit(-1);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            System.exit(-1);
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(-1);
        } catch (CertificateException e) {
            e.printStackTrace();
            System.exit(-1);
        }

        try {
            if (!certFile.canRead()) {
                System.out.println("File " + certFile.getCanonicalPath() + " not a valid file name.");
                return;
            }
            //            Security.addProvider(new BouncyCastleProvider());
            new StressTest(hostName, port, isHttp, new FileInputStream(certFile), numberOfThreads, waitTime, keyId,
                    urlPath, resultFilePrefix, outerSignKeys.getPrivate(), innerSignKey, innerCert);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /* (non-Javadoc)
     * @see org.ejbca.ui.cli.ClientToolBox#getName()
     */
    @Override
    protected String getName() {
        return "CMPNestedMessageStressTest";
    }

}