December 10, 2007

Token knowledge

Standards are great. But lack of documentation is bad. Documentation needs to exist at various levels. You should have very technical documentation, for people interested in all the knitty gritty stuff. You should also have superficial overviews for people who aren't interested in the details. And then you should have documentation like this. Enough info for you to follow and do something, but not too much that your head spins. I had to search around for half a day to pick up enough technical information to make it possible. Then with very basic understanding of how things 'should' work, I put together the system.

The thing is, with security applications, a little knowledge is a very bad thing. If you design a security system without a proper understanding of the entire architecture, you may just be creating a very hackable system, will multiple holes just waiting to be exploited. So I don't claim that this system is unhackable or even secure. So since you've read this far without knowing what I'm talking about - here it is. The little project intends to utilize your PKCS11 compliant security token to extract a pin-locked secret for whatever you decide to do with it.

The idea is that a security token is a hardware device which stores some sensitive information, sort of like a physical key contains ridges and valleys. Let's go thru some terminology to get you started when reading other docs about PKCS11.

PKCS11 is the standard API for hardware security tokens. This can be smart cards, USB smart tokens, or Hardware Security modules. All these devices have one thing in common. They store keys. Some also store certificates. That's another PKCS standard - I think its PKCS12 but you have to check that.

Token refers to the device. To access your token you need a PIN. PIN can mean text as well - not just numbers. Inside the token there is a keystore. The keystore stores keys. Think of it like a key chain. To access your keystore you may need a password. Keys are stored in a way that you can easily access a key by an alias - or a name.

So our basic process is thus - Access the key, then unlock it, open the keystore and get the key we want based on its alias. Simple? Yes. By the way, the language we will use is Java - so if you're looking for a way to do this using another language - look elsewhere.

Setup - First install your token drivers. Make sure your OS can recognize the token and that the token has PKCS11 interfaces. There will be a library which provides this interface. On Windows this will be a DLL, on Linux, a .so file. You will need to find this file, or guess what it is, if there's no documentation to help you. For Safenet tokens and smartcards its dkck201.dll. Once you found this file, you need to create a config file - which is a text file. It's possible to go without the text file but lets just make the file.
Here is what the file contains (pkcs11.cfg).


name = SmartCard
library = c:\windows\system32\dkck201.dll
slot=14

Friendly name of SmartCard, location of library and slot. Oh yea... another definition. Slot refers to the the smart card reader, in case you plugged in several readers. You can find out the slot from your smart card reader driver, or you can iterate all the slots until you find the card you want. I won't bother with this.

Once you have your config file, its time to code

String pkcs11ConfigFile = "d:\\pkcs11.cfg";
Provider pkcs11Provider =
new sun.security.pkcs11.SunPKCS11(pkcs11ConfigFile);
Security.addProvider(pkcs11Provider);

Here you tell the Java Security system to configure itself based on the config file. You're adding a provider - which is your smartcard, token or HSM.

Work - Now you do your stuff. A little guide to useful string conversion functions. Call toCharArray() to convert a String to a char[]. Call getBytes() to convert a String to a byte[]. Call new String(byte[]) to convert byte[] into a string.


KeyStore ks = KeyStore.getInstance("PKCS11");
ks.load(null, defaultPIN.toCharArray() );


Here we open our token and unlock they keystore using the default pin. Note the keystore isn't pin protected.


SecretKeySpec key = new SecretKeySpec(secret.getBytes(),"DES");

Here we're creating a secret key. Note that secret is a string and we're creating a SecretKey object from our string. I used DES mainly because it worked for our 8 char secret. Your results may vary. If you're using a specific encryption algorithm, you should probably create a key for that algorithm. I'm not going to show you how here.


ks.deleteEntry(keyalias);
ks.setKeyEntry(keyalias,key,sPIN.toCharArray(),null);

We delete any keys at our keyalias, then store our new key in the keystore, using the keyalias. That's it!

But wait. How do you get the key out, after you've put it in? Here's how

KeyStore.SecretKeyEntry skEntry = (KeyStore.SecretKeyEntry)
ks.getEntry(keyalias, null);
byte data[] = skEntry.getSecretKey().getEncoded();
String secret = new String(data);

Get the key store entry under the keyalias. Get its encoded value - this will not work for all key types - but it works for this one. Then convert the byte array to a string. There's your secret. Now you can do what you want to it. Notice you have to unlock and load the keystore before you do this - so the PIN is necessary. And we're done. A quick 5 minute project for learning enough about PKCS11 to impress other people who don't know anything about PKCS11. Impressed? Thankful? You're welcome.