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

Java tutorial

Introduction

Here is the source code for org.ejbca.ui.cli.CMPKeyUpdateStressTest.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.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 javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.lang.StringUtils;
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.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x509.X509CertificateStructure;
import org.bouncycastle.asn1.x509.X509Name;
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 com.novosec.pkix.asn1.cmp.CMPObjectIdentifiers;
import com.novosec.pkix.asn1.cmp.CertConfirmContent;
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.
 */
class CMPKeyUpdateStressTest extends ClientToolBox {
    /** Internal localization of logs and errors */
    private static final InternalResources intres = InternalResources.getInstance();

    static private class StressTest {
        final PerformanceTest performanceTest;

        final private PrivateKey oldKey;
        final private KeyPair newKeyPair;
        final private Certificate extraCert;
        final private X509Certificate cacert;
        final private String eepassword;
        final private CertificateFactory certificateFactory;
        final private Provider bcProvider = new BouncyCastleProvider();
        final private String hostName;
        final private int port;
        final String urlPath;
        final String resultCertFilePrefix;
        boolean isSign;
        boolean firstTime = true;

        StressTest(final String _hostName, final int _port, final int numberOfThreads, final int waitTime,
                final String _urlPath, final String _resultCertFilePrefix, final String _eepassword,
                final Certificate _cacert, final PrivateKey _oldKey, final Certificate _extraCert)
                throws Exception {

            this.hostName = _hostName;
            this.certificateFactory = CertificateFactory.getInstance("X.509", this.bcProvider);
            this.cacert = (X509Certificate) _cacert;
            this.eepassword = _eepassword;
            this.port = _port;
            this.urlPath = _urlPath;
            this.resultCertFilePrefix = _resultCertFilePrefix;

            final KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA");
            keygen.initialize(2048);
            this.newKeyPair = keygen.generateKeyPair();
            this.oldKey = _oldKey;
            this.extraCert = _extraCert;

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

        private CertRequest genKeyUpdateReq() 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);
            final byte[] bytes = this.newKeyPair.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);
            return new CertRequest(new DERInteger(4), myCertTemplate);
        }

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

            final CertReqMsg myCertReqMsg = new CertReqMsg(keyUpdateRequest);

            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(keyUpdateRequest);
                mout.close();
                final byte[] popoProtectionBytes = baos.toByteArray();
                final Signature sig = Signature.getInstance(PKCSObjectIdentifiers.sha1WithRSAEncryption.getId());
                sig.initSign(this.oldKey);
                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(CertTools.getSubjectDN(extraCert))),
                    new GeneralName(new X509Name(this.cacert.getSubjectDN().getName())));
            myPKIHeader.setMessageTime(new DERGeneralizedTime(new Date()));
            myPKIHeader.setSenderNonce(new DEROctetString(sessionData.getNonce()));
            myPKIHeader.setSenderKID(new DEROctetString(sessionData.getNonce()));
            myPKIHeader.setTransactionID(new DEROctetString(sessionData.getTransId()));

            final PKIBody myPKIBody = new PKIBody(myCertReqMessages, 7); // key update request
            return new PKIMessage(myPKIHeader, myPKIBody);
        }

        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);
        }

        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 PKIMessage protectPKIMessage(final PKIMessage msg, final boolean badObjectId, final String password)
                throws NoSuchAlgorithmException, InvalidKeyException {
            // SHA1
            final AlgorithmIdentifier owfAlg = new AlgorithmIdentifier("1.3.14.3.2.26");
            // 567 iterations
            final int iterationCount = 567;
            // HMAC/SHA1
            final AlgorithmIdentifier macAlg = new AlgorithmIdentifier("1.2.840.113549.2.7");
            final byte[] salt = "foo123".getBytes();
            final DEROctetString derSalt = new DEROctetString(salt);
            final PKIMessage ret;

            // Create the PasswordBased protection of the message
            final PKIHeader head = msg.getHeader();
            head.setSenderKID(new DEROctetString("EMPTY".getBytes()));
            final DERInteger iteration = new DERInteger(iterationCount);

            // Create the new protected return message
            String objectId = "1.2.840.113533.7.66.13";
            if (badObjectId) {
                objectId += ".7";
            }
            final PBMParameter pp = new PBMParameter(derSalt, owfAlg, iteration, macAlg);
            final AlgorithmIdentifier pAlg = new AlgorithmIdentifier(new DERObjectIdentifier(objectId), pp);
            head.setProtectionAlg(pAlg);

            final PKIBody body = msg.getBody();
            ret = new PKIMessage(head, body);

            // Calculate the protection bits
            final byte[] raSecret = password.getBytes();
            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();
            }
            // For HMAC/SHA1 there is another oid, that is not known in BC, but the result is the same so...
            final String macOid = macAlg.getObjectId().getId();
            final byte[] protectedBytes = ret.getProtectedBytes();
            final Mac mac = Mac.getInstance(macOid, this.bcProvider);
            final SecretKey key = new SecretKeySpec(basekey, macOid);
            mac.init(key);
            mac.reset();
            mac.update(protectedBytes, 0, protectedBytes.length);
            final byte[] out = mac.doFinal();
            final DERBitString bs = new DERBitString(out);

            // Finally store the protection bytes in the msg
            ret.setProtection(bs);

            return ret;
        }

        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() != 8) {
                StressTest.this.performanceTest.getLog().error("Cert body tag not 8.");
                return null;
            }
            final CertRepMessage c = body.getKup();
            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(CertTools.getSubjectDN(extraCert)).hashCode()) {
                StressTest.this.performanceTest.getLog().error("Subject is '" + cert.getSubjectDN()
                        + "' but should be '" + CertTools.getSubjectDN(extraCert) + '\'');
                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 boolean checkCmpPKIConfirmMessage(final SessionData sessionData, final byte retMsg[])
                throws IOException {
            //
            // Parse response message
            //
            final PKIMessage respObject = PKIMessage
                    .getInstance(new ASN1InputStream(new ByteArrayInputStream(retMsg)).readObject());
            if (respObject == null) {
                StressTest.this.performanceTest.getLog().error("Not possbile to get response message.");
                return false;
            }
            final PKIHeader header = respObject.getHeader();
            if (header.getSender().getTagNo() != 4) {
                StressTest.this.performanceTest.getLog().error("Wrong tag in respnse message header. Is "
                        + header.getSender().getTagNo() + " should be 4.");
                return false;
            }
            {
                final X509Name name = X509Name.getInstance(header.getSender().getName());
                String senderDN = name.toString().replaceAll(" ", "");
                String caDN = this.cacert.getSubjectDN().toString().replaceAll(" ", "");
                if (!StringUtils.equals(senderDN, caDN)) {
                    StressTest.this.performanceTest.getLog().error(
                            "Wrong CA DN. Is '" + name + "' should be '" + this.cacert.getSubjectDN() + "'.");
                    return false;
                }
            }
            {
                final X509Name name = X509Name.getInstance(header.getRecipient().getName());
                if (name.hashCode() != new X509Name(CertTools.getSubjectDN(extraCert)).hashCode()) {
                    StressTest.this.performanceTest.getLog().error("Wrong recipient DN. Is '" + name
                            + "' should be '" + CertTools.getSubjectDN(extraCert) + "'.");
                    return false;
                }
            }
            final PKIBody body = respObject.getBody();
            if (body == null) {
                StressTest.this.performanceTest.getLog().error("No PKIBody for response received.");
                return false;
            }
            if (body.getTagNo() != 19) {
                StressTest.this.performanceTest.getLog().error("Cert body tag not 19. It was " + body.getTagNo());

                StressTest.this.performanceTest.getLog()
                        .error(body.getError().getPKIStatus().getStatusString().getString(0).getString());

                return false;
            }
            final DERNull n = body.getConf();
            if (n == null) {
                StressTest.this.performanceTest.getLog().error("Confirmation is null.");
                return false;
            }
            return true;
        }

        private PKIMessage genCertConfirm(final SessionData sessionData, final String hash) {

            PKIHeader myPKIHeader = new PKIHeader(new DERInteger(2),
                    new GeneralName(new X509Name(CertTools.getSubjectDN(this.extraCert))),
                    new GeneralName(new X509Name(this.cacert.getSubjectDN().getName())));
            myPKIHeader.setMessageTime(new DERGeneralizedTime(new Date()));
            // senderNonce
            myPKIHeader.setSenderNonce(new DEROctetString(sessionData.getNonce()));
            // TransactionId
            myPKIHeader.setTransactionID(new DEROctetString(sessionData.getTransId()));

            CertConfirmContent cc = new CertConfirmContent(new DEROctetString(hash.getBytes()),
                    new DERInteger(sessionData.getReqId()));
            PKIBody myPKIBody = new PKIBody(cc, 24); // Cert Confirm
            PKIMessage myPKIMessage = new PKIMessage(myPKIHeader, myPKIBody);
            return myPKIMessage;
        }

        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 keyUpdateReq = genKeyUpdateReq();
                PKIMessage certMsg = genPKIMessage(this.sessionData, true, keyUpdateReq);
                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, oldKey);
                addExtraCert(signedMsg, extraCert);
                if (signedMsg == null) {
                    StressTest.this.performanceTest.getLog().error("No protected message.");
                    return false;
                }

                this.sessionData.setReqId(signedMsg.getBody().getKur().getCertReqMsg(0).getCertReq().getCertReqId()
                        .getValue().intValue());
                final ByteArrayOutputStream bao = new ByteArrayOutputStream();
                final DEROutputStream out = new DEROutputStream(bao);
                out.writeObject(signedMsg);
                final byte[] ba = bao.toByteArray();
                // Send request and receive response
                final byte[] resp = sendCmpHttp(ba);
                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;
                }
                String fp = CertTools.getFingerprintAsString((Certificate) cert);
                this.sessionData.setFP(fp);
                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 class SendConfirmMessageToCA implements Command {
            final private SessionData sessionData;

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

            public boolean doIt() throws Exception {
                final String hash = this.sessionData.getFP(); //"foo123";
                final PKIMessage con = genCertConfirm(this.sessionData, hash);
                if (con == null) {
                    StressTest.this.performanceTest.getLog().error("Not possible to generate PKIMessage.");
                    return false;
                }
                //final String password = PBEPASSWORD;
                //final String password = StressTest.this.performanceTest.getRandom().nextInt()%10!=0 ? PBEPASSWORD : PBEPASSWORD+"a";
                final PKIMessage confirm = protectPKIMessage(con, false, "foo123");
                final ByteArrayOutputStream bao = new ByteArrayOutputStream();
                final DEROutputStream out = new DEROutputStream(bao);
                out.writeObject(confirm);
                final byte ba[] = bao.toByteArray();
                // Send request and receive response
                final byte[] resp = sendCmpHttp(ba);
                if (resp == null || resp.length <= 0) {
                    StressTest.this.performanceTest.getLog().error("No response message.");
                    return false;
                }
                if (!checkCmpResponseGeneral(resp, this.sessionData, false)) {
                    return false;
                }
                if (!checkCmpPKIConfirmMessage(this.sessionData, resp)) {
                    return false;
                }
                //StressTest.this.performanceTest.getLog().info("User with DN '"+this.sessionData.getUserDN()+"' finished.");
                return true;
            }

            public String getJobTimeDescription() {
                return "Send confirmation to CA";
            }
        }

        class SessionData {
            final private byte[] nonce = new byte[16];
            final private byte[] transid = new byte[16];
            private int reqId;
            private String newcertfp;

            SessionData() {
                super();
            }

            void newSession() {
                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;
            }

            void setFP(String fp) {
                this.newcertfp = fp;
            }

            String getFP() {
                return this.newcertfp;
            }

            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 SendConfirmMessageToCA(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 int port;
        final String urlPath;
        final String resultFilePrefix;
        if (args.length < 5) {
            System.out.println(args[0]
                    + " <host name> <keystore (p12)> <keystore password> <friendlyname in keystore> [<number of threads>] [<wait time (ms) between each thread is started>] [<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=normal, cmp.allowraverifypopo=true, cmp.allowautomatickeyupdate=true, cmp.allowupdatewithsamekey=true");
            //            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).");
            System.out.println(
                    "Ejbca expects the following: There exists an end entity with a generated certificate. The end entity's certificate and its private key are stored in the keystore used "
                            + "in the commandline. The end entity's certificate's 'friendly name' in the keystore is the one used in the command line. Such keystore can be obtained, for example, by specifying "
                            + "the token to be 'P12' when creating the end entity and then download the keystore by choosing 'create keystore' from the public web");
            return;
        }
        hostName = args[1];
        keystoreFile = args[2];
        keystorePassword = args[3];
        certNameInKeystore = args[4];
        numberOfThreads = args.length > 5 ? Integer.parseInt(args[5].trim()) : 1;
        waitTime = args.length > 6 ? Integer.parseInt(args[6].trim()) : 0;
        port = args.length > 7 ? Integer.parseInt(args[7].trim()) : 8080;
        //        isHttp = true;
        urlPath = args.length > 8 && args[8].toLowerCase().indexOf("null") < 0 ? args[8].trim() : null;
        resultFilePrefix = args.length > 9 ? args[9].trim() : null;

        CryptoProviderTools.installBCProviderIfNotAvailable();

        Certificate cacert = null;
        Certificate extracert = null;
        PrivateKey oldCertKey = null;

        FileInputStream file_inputstream;
        try {
            file_inputstream = new FileInputStream(keystoreFile);
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            keyStore.load(file_inputstream, keystorePassword.toCharArray());
            Key key = keyStore.getKey(certNameInKeystore, keystorePassword.toCharArray());
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(key.getEncoded());
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            oldCertKey = keyFactory.generatePrivate(keySpec);
            //extracert = keyStore.getCertificate(certNameInKeystore);

            Certificate[] certs = keyStore.getCertificateChain(certNameInKeystore);
            extracert = certs[0];
            cacert = certs[1];

        } 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);
        }

        try {
            new StressTest(hostName, port, numberOfThreads, waitTime, urlPath, resultFilePrefix, keystorePassword,
                    cacert, oldCertKey, extracert);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

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

}