Friday 7 December 2012

Slow creation of PKCS12(PFX) certificate

This java code works on Windows system with an acceptable speed (around 1 sec), but it takes up to 1 minute on Linux system.

After some investigation, I found it is also down to slow SecureRandom issue on Linux.

The workaround is easy, use the switch -Djava.security.egd=file:/dev/./urandom if you are using JRE 1.5+.

I did a test on CentOS(an ESXi VM), see how different the results are!

java -classpath .:bcprov.jar KeyPairCost
---- test start ----
Generate KeyPair takes 176 msec
Store takes 58923 msec
---- test end ----

java -Djava.security.egd=file:/dev/./urandom -classpath .:bcprov.jar KeyPairCost
---- test start ----
Generate KeyPair takes 297 msec
Store takes 404 msec
---- test end ----

Java Code

import java.io.ByteArrayOutputStream;
import java.util.*;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.Security;

import java.math.BigInteger;
import java.security.cert.X509Certificate;

import org.bouncycastle.x509.X509V1CertificateGenerator;
import org.bouncycastle.jce.X509Principal;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class KeyPairCost
{
    static X509V1CertificateGenerator  v1CertGen = new X509V1CertificateGenerator();

    public static void generateKeyPairCost() throws Exception
    {
        // signers name 
        String  issuer = "KeyPairCost";
        // subjects name - the same as we are self signed.
        String  subject = "KeyPairCost";
  
  java.util.Date date1 = new java.util.Date();
  
  KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
  keyGen.initialize(1024); //default SecureRandom
  KeyPair keypair = keyGen.genKeyPair();
  PrivateKey privKey = keypair.getPrivate();
  PublicKey pubKey = keypair.getPublic();
  
  java.util.Date date2 = new java.util.Date();
  long diff = date2.getTime()-date1.getTime();
  System.out.println("Generate KeyPair takes " + diff + " msec");
  
        // create the certificate - version 1
  v1CertGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
        v1CertGen.setIssuerDN(new X509Principal(issuer));
        v1CertGen.setNotBefore(new Date(System.currentTimeMillis() - 1000L * 60 * 60 * 24 * 30)); // one month
        v1CertGen.setNotAfter(new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 30 * 1200 ))); // 10 years
        v1CertGen.setSubjectDN(new X509Principal(subject));
        v1CertGen.setPublicKey(pubKey);
        v1CertGen.setSignatureAlgorithm("SHA1WithRSAEncryption");

        X509Certificate cert = v1CertGen.generate(privKey,"BC");

       
        
//  now write it to key store        
        KeyStore ks = KeyStore.getInstance("PKCS12");
        ks.load(null, "changeit".toCharArray());

        Certificate[] chain = new Certificate[1];
        chain[0] = cert;
        
        ks.setKeyEntry("dualidp", privKey, "changeit".toCharArray(), chain);

        ByteArrayOutputStream bOut = new ByteArrayOutputStream();

        ks.store(bOut, "changeit".toCharArray());
        
  java.util.Date date3 = new java.util.Date();
  diff = date3.getTime()-date2.getTime();
  System.out.println("Store takes " + diff + " msec");

     }
 
 
 
 public static void main(String[] args)
 {
  Security.addProvider(new BouncyCastleProvider());
  System.out.println("----  test start ----");
  try {
   generateKeyPairCost();
  } catch (Exception e) {
   e.printStackTrace();
  }
  System.out.println("----  test end ----");
 }
 
}