Friday, November 19, 2010

smart Card tutorial : Part 2

In my previous article smart Card tutorial : Part 1, i explained how you can use the Cryptographic Token Interface Standard PKCS#11 in order to access your smart card or USB Token, and perform some simple operations like listing the connected tokens, and displaying each device's informations (Serial Number, Vendor etc...).
This second part will cover some advanced usage of PKCS#11 like :
  • Generating on-card RSA Keypair.
  • Storing certificates and objects.

Generating on-card RSA Keypair

In order to use your smart card for authentication or digital signature, you need first to store your private key and certificate inside it, which obviously means you need to generate the Key-pair first! for that you can either generate a PKCS#12 keystore using Openssl, or your certificate authority management software like EJBCA , but in doing so, there is a chance that someone may intercept your ".p12" file, and therefore compromise your private key, not to mention that the admin can keep a copy of the file to himself.
A more secure way is generating the key-pair inside the smart card, this way the private key never leaves it, always protected, and more importantly : only the card holder will have access to the private key, and none else, not even the admin will have such access.
So, let's see how we can use PKCS#11 to achieve this, we will be working on the same "SmartCardTutorial" class that we created in the previous article.
First, we will define a method that returns the first available connected Token :

 public Token getFirstAvailableToken() {  
     Token result = null;  
     try {  
 //Get all available slots  
       Slot[] slots = this.getTokenSlots();  
       if (slots.length != 0) {  
 //Get the first available token  
         result = slots[0].getToken();  
       }  
     } catch (TokenException ex) {  
       Logger.getLogger(SmartCardTutorial.class.getName()).log(Level.SEVERE, null, ex);  
     }  
     return result;  
   }  

Now we can define a method that generates the keypair on the card, and returns the public key for later use. We will need to define templates for both the private and public key that we want to generate : the public exponent, the keys length etc...

 public KeyPair generateKeyPair(Session session) throws Exception {  
     Mechanism keyPairGenerationMechanism = Mechanism.RSA_PKCS_KEY_PAIR_GEN;  
     RSAPublicKey rsaPublicKeyTemplate = new RSAPublicKey();  
     RSAPrivateKey rsaPrivateKeyTemplate = new RSAPrivateKey();  
     // set the general attributes for the public key  
     // We want keys of length 2048 bits  
     rsaPublicKeyTemplate.getModulusBits().setLongValue(new Long(2048));  
     // With public exponent equal 2^16 + 1  
     byte[] publicExponentBytes = {0x01, 0x00, 0x01};  
     rsaPublicKeyTemplate.getPublicExponent().setByteArrayValue(publicExponentBytes);  
     rsaPublicKeyTemplate.getToken().setBooleanValue(Boolean.TRUE);  
     byte[] id = new byte[20];  
     new Random().nextBytes(id);  
     rsaPublicKeyTemplate.getId().setByteArrayValue(id);  
     rsaPrivateKeyTemplate.getSensitive().setBooleanValue(Boolean.TRUE);  
     rsaPrivateKeyTemplate.getToken().setBooleanValue(Boolean.TRUE);  
     rsaPrivateKeyTemplate.getPrivate().setBooleanValue(Boolean.TRUE);  
     rsaPrivateKeyTemplate.getId().setByteArrayValue(id);  
     rsaPrivateKeyTemplate.getSign().setBooleanValue(Boolean.TRUE);  
     rsaPrivateKeyTemplate.getDecrypt().setBooleanValue(Boolean.TRUE);  
     rsaPublicKeyTemplate.getVerify().setBooleanValue(Boolean.TRUE);  
     rsaPublicKeyTemplate.getEncrypt().setBooleanValue(Boolean.TRUE);  
     rsaPublicKeyTemplate.getKeyType().setPresent(false);  
     rsaPublicKeyTemplate.getObjectClass().setPresent(false);  
     rsaPrivateKeyTemplate.getKeyType().setPresent(false);  
     rsaPrivateKeyTemplate.getObjectClass().setPresent(false);  
     KeyPair generatedKeyPair = session.generateKeyPair(keyPairGenerationMechanism, rsaPublicKeyTemplate, rsaPrivateKeyTemplate);  
     return generatedKeyPair;  
   }  

Importing Certificates to your token

This method defined above generates and stores the private key on the card, and returns the keypair, our next move would be generating a CSR (Certificate Signing Request) with the generated public key, and sending it to our certificate authority in order to get a digital certificate, and then store it in our token. However the part about generating and signing the CSR is out of scope, for now we will just create a dummy "getCertificateFromKeyPair' method that returns an X509Certificate, how to implement a real one will be explained in future articles about EJBCA web service API.

 public void storeCertificate(Session session, X509Certificate x509Certificate) throws Exception {  
     PublicKey publicKey = (PublicKey) x509Certificate.getPublicKey();  
     java.security.interfaces.RSAPublicKey rsaPublicKey = (java.security.interfaces.RSAPublicKey) publicKey;  
     RSAPrivateKey rsaPrivateKeySearchTemplate = new RSAPrivateKey();  
     byte[] modulus = iaik.pkcs.pkcs11.Util.unsignedBigIntergerToByteArray(rsaPublicKey.getModulus());  
     rsaPrivateKeySearchTemplate.getModulus().setByteArrayValue(modulus);  
     // We want to find objects on the card having the same properties as our template  
     session.findObjectsInit(rsaPrivateKeySearchTemplate);  
     // There is just 1 private key we stored, so we want just 1 result  
     Object[] foundKeyObjects = session.findObjects(1);  
     byte[] objectID = null;  
     if (foundKeyObjects.length > 0) {  
       //Get the found key  
       Key foundKey = (Key) foundKeyObjects[0];  
       //Get its ID  
       objectID = foundKey.getId().getByteArrayValue();  
     }  
     session.findObjectsFinal();  
     // create certificate object template  
     X509PublicKeyCertificate pkcs11X509PublicKeyCertificate = new X509PublicKeyCertificate();  
     Name subjectName = (Name) x509Certificate.getSubjectDN();  
     Name issuerName = (Name) x509Certificate.getIssuerDN();  
     String subjectCommonName = subjectName.getRDN(objectID.commonName);  
     String issuerCommonName = issuerName.getRDN(ObjectID.commonName);  
     char[] label = (subjectCommonName + "'s "  
         + ((issuerCommonName != null) ? issuerCommonName + " " : "") + "Certificate").toCharArray();  
     byte[] newObjectID;  
     // if we need a new object ID, create one  
     if (objectID == null) {  
       newObjectID = ((java.security.interfaces.RSAPublicKey) publicKey).getModulus().toByteArray();  
       MessageDigest digest = MessageDigest.getInstance("SHA-1");  
       newObjectID = digest.digest(newObjectID);  
     } else {  
       // we already got one from a corresponding private key before  
       newObjectID = objectID;  
     }  
     byte[] encodedSubject = ((Name) currentCertificate.getSubjectDN()).getEncoded();  
     byte[] encodedIssuer = ((Name) currentCertificate.getIssuerDN()).getEncoded();  
     pkcs11X509PublicKeyCertificate.getToken().setBooleanValue(Boolean.TRUE);  
     pkcs11X509PublicKeyCertificate.getPrivate().setBooleanValue(Boolean.FALSE);  
     pkcs11X509PublicKeyCertificate.getLabel().setCharArrayValue(label);  
     pkcs11X509PublicKeyCertificate.getId().setByteArrayValue(newObjectID);  
     pkcs11X509PublicKeyCertificate.getSubject().setByteArrayValue(encodedSubject);  
     pkcs11X509PublicKeyCertificate.getIssuer().setByteArrayValue(encodedIssuer);  
     pkcs11X509PublicKeyCertificate.getSerialNumber().setByteArrayValue(serialNumber);  
     pkcs11X509PublicKeyCertificate.getValue().setByteArrayValue(currentCertificate.getEncoded());  
     //And finaly store the certificate on the token  
     session.createObject(pkcs11X509PublicKeyCertificate);  
   }  

That's pretty much it for today, i hope it was helpful, and will continue shortly with the "smart card Tutorials" series with more advanced topics. Until then, my best regards everyone.

2 comments:

  1. I have ".p12 file " from certificat authority ,i want to import an X509 certificate in to my pkcs#11 token . what are the modifications to do on this code .

    ReplyDelete