2012/04/09

Using Smart Card as Keystore in Java, signing

This is the promised sequel of the article from September 8th 2011.
It does not represent the only one way how to achieve the desired result, especially when it comes to manipulation with certificates and keys certificate management.

Loading Certificate into the Card


It seems that the easiest approach is to prepare the key store on disk and load it  into the card when ready. For that we use mostly two tools - openssl to transform keys and certificates and keytool to manage the key store.

Create the key store on disk and fill with certificates

Create a private key entry, i.e. a certificate containing a private key, by converting our certificate and private key to PKCS12 format: openssl pkcs12 -export -out cert.p12 -in cert.pem -inkey key.pem

I used org.apache.commons.ssl.KeyStoreBuilder to build a keystore from the p12 file. The password is used both for decryption of private key and encryption of the newly created Java key store. 
java -cp commons-ssl.jar org.apache.commons.ssl.KeyStoreBuilder password cert.p12

Now you can check the content of the file-based key store: keytool -keystore newkeystore.jks -list

Load the file-base key store into the card

keytool -keystore NONE -storetype PKCS11 -providerName SunPKCS11-OpenSC-PKCS11 -importkeystore -srckeystore newkeystore.jks

Check the content of the on-card key store.
keytool -keystore NONE -storetype PKCS11 -providerName SunPKCS11-OpenSC-PKCS11 -list

Signing and Verification with the On-Card Certificate


All the error handling was intentionally removed to make following example shorter. I also skipped creation of PKCS7 signed messages - you can you Bouncy Castle CMSSignedDataGenerator for that easily.

import java.security.*;
import java.security.cert.*;
import java.security.cert.Certificate;
import java.io.*;

...

// loading the key from file:
KeyStore keyStore = KeyStore.getInstance("JCEKS");
FileInputStream inputStream = new FileInputStream(storeFileName);
keyStore.load(inputStream, storePassword.toCharArray());
KeyStore.ProtectionParameter protectParameter = new KeyStore.PasswordProtection(certPass.toCharArray()); }

// loading the key from token:
KeyStore keyStore = KeyStore.getInstance("PKCS11");
KeyStore.ProtectionParameter protectParameter = null;
keyStore.load(null, storePassword.toCharArray());

// the rest does not depend on the type of the store: 
String signatureAlgorithmName = "SHA1withRSA";
KeyStore.Entry entry = keyStore.getEntry(alias, protectParam);
boolean isPrivateKeyEntry = keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class);
if (isPrivateKeyEntry)
{
  Signature signatureAlgorithm = Signature.getInstance(signatureAlgorithmName);

  // signing
  KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)entry;
  PrivateKey privateKey = privateKeyEntry.getPrivateKey();  
  signatureAlgorithm.initSign (privateKey);
  signatureAlgorithm.update (message);
  byte[] signature = signatureAlgorithm.sign();

  // verification
  Certificate[] chain = privateKeyEntry.getCertificateChain();
  X509Certificate certificate = (X509Certificate) chain[chain.length-1];
  PublicKey publicKey = certificate.getPublicKey();
  signatureAlgorithm.initVerify(publicKey);
  signatureAlgorithm.update (data);
  boolean verified = signatureAlgorithm.verify(signature);
}