Monday, December 10, 2012

Generating Certificates With BouncyCastle

Usually, after a serious public-private key generation session, the private key would need to be stored. The obvious place would be a key store. The commonly used method to accomplish this using java.security.KeyStore class:

void setKeyEntry(String alias, byte[] key, Certificate[] chain) 

You'll need at least one Certificate object to accompany the private key. You may be tempted (or already attempted) to 'skip' producing the Certificate object using this method:

void setEntry(String alias, KeyStore.Entry entry, KeyStore.ProtectionParameter protParam)

But you'll discover that a java.security.KeyStore.PrivateKeyEntry object needs a Certificate[] object passed in as well.

This led to the code below to generate the required Certificate object, just enough to fulfil the requirements to save the private key:


@Deprecated
 private Certificate[] generateCertificateOld(KeyPair keyPair) throws Exception {
        Date startDate = new Date(System.currentTimeMillis());
        Date expiryDate = DateUtils.parseDate("01/01/2100", new String[] { "dd/MM/yyyy" });
        BigInteger serialNumber = new BigInteger(String.valueOf(System.currentTimeMillis()));

        X509V1CertificateGenerator certGen = new X509V1CertificateGenerator();
        X500Principal dnName = new X500Principal("CN=Storage Certificate");

        certGen.setSerialNumber(serialNumber);
        certGen.setIssuerDN(dnName);
        certGen.setNotBefore(startDate);
        certGen.setNotAfter(expiryDate);
        certGen.setSubjectDN(dnName); // note: same as issuer
        certGen.setPublicKey(keyPair.getPublic());
        certGen.setSignatureAlgorithm("SHA256withRSA");

        return new Certificate[] {certGen.generate(keyPair.getPrivate())};
 }
 

Of course, if you have the aid of an IDE, the BouncyCastle methods used are deprecated (using v1.47). So, being a good Java coder, I've followed the advice in the BC javadocs and replaced the deprecated methods with the following equivalent code:


private Certificate[] generateCertificate(KeyPair keyPair) throws Exception {
  X509v1CertificateBuilder certGen = new JcaX509v1CertificateBuilder(
    new X500Name("CN=Storage Certificate"),
    BigInteger.valueOf(System.currentTimeMillis()),
    new Date(System.currentTimeMillis()),
    DateUtils.parseDate("01/01/2100", new String[] { "dd/MM/yyyy" }),
    new X500Name("CN=Storage Certificate"),
    keyPair.getPublic());

  JcaContentSignerBuilder contentSignerBuilder = new JcaContentSignerBuilder("SHA256withRSA");
  ContentSigner contentSigner = contentSignerBuilder.build(keyPair.getPrivate());
  
  X509CertificateHolder certHolder = certGen.build(contentSigner);

  JcaX509CertificateConverter certConverter = new JcaX509CertificateConverter();
  Certificate cert = certConverter.getCertificate(certHolder);

  Certificate[] certs = { cert };
  return certs;
 }
 

May your sanity be in check as you navigate through the waters of Java security.