A dive into SoftHSM

Western Electric 229G cryptoprocessor. Pauli Rautakorpi / CC BY

A Hardware Security Module (HSM) is a physical device that attaches directly to a computer or server and is used to securely manage and perform operations on cryptographic keys. The purpose of these devices is, among others, to generate cryptographic keys and sign information without revealing private-key material to the outside world. These are hardened, intrusion-resistant devices that typically provide features like on-board key-generation, key storage and accelerated encryption. HSMs are commonly used in Public Key Cryptography (PKI) deployments to secure Certificate Authority keys.

This blog is about SoftHSM and its usage in Java. SoftHSM isn’t exactly an HSM per se, but a software implementation of a generic PKCS#11 device. This is being developed as part of OpenDNSSEC and allows you to explore PKCS#11 without having a real HSM.

To understand how SoftHSM is used in Java we need to get familiar with the layers involved in interacting with an HSM and related concepts.

PKCS#11 specifies an Application Programming Interface(API) called Cryptoki for devices that hold cryptographic information and perform cryptographic functions. These devices are known as Cryptographic Tokens. PKCS#11 presents a common logical view of tokens to applications.

Layers used for interacting with an HSM from Java code

Java defines a set of programming interfaces for performing cryptographic operations. These are collectively known as Java Cryptography Architecture (JCA) and Java Cryptography Extension (JCE). These interfaces are provider-based. This means that when performing a cryptographic operation in our application, the application talks to the interface, but the actual operation is performed in the configured provider which implements that interface. This architecture supports different types of provider implementations. Some may perform the operations in software; others, on a hardware token.

The Sun PKCS#11 provider does not implement cryptographic algorithms by itself, but acts as a bridge between the JCA, JCE APIs and the native PKCS#11 module. This native module must be in the form of a shared-object library (.so file on Solaris and Linux) or dynamic-link library (.dll on Windows) and is provided by the vendor of the HSM device.

Using SoftHSM

SoftHSM can be installed using already available binary packages, or from source. For example, on a recent version of Ubuntu, a simple sudo apt-get install softhsm will suffice. As part of the installation, the necessary command line tools will be installed along with the shared object file libsofthsm2.so which is the native implementation of PKCS#11 for SoftHSM.

Use the softhsm2-util command line utility to create a new token as follows

softhsm2-util --init-token --slot 0 --label "<token name>"

This prompts you for a Security Officer PIN (used to add/ remove tokens) and a User PIN(used for accessing the token from the application).

The PKCS#11 configuration file must contain the location of the .so file and the slot number of the token.

name = softhsm_pkcs11
library = /usr/lib64/libsofthsm2.so
slot = 0

Configure JCA provider

In this snippet, I’ve programmatically configured the JCA provider at runtime. You can also statically configure it in the Java security properties file $JAVA_HOME/lib/security/java.security

Provider sunPkcsProvider = new SunPKCS11(pkcs11ConfigFilePath);
if (-1 == Security.addProvider(sunPkcsProvider)) {
throw new RuntimeException("Could not configure SunPKCS11 provider");
}

Load keystore

Operations that deal with keys in the token, require you to first login using your User PIN before the operations can proceed. You can supply the PIN in the password input parameter to the load method while accessing the token as a keystore via the java.security.KeyStore class.

String userPinStr = "<user PIN specified while creating token>"
char[] userPin = userPinStr.toCharArray();
KeyStore keyStore = KeyStore.getInstance("PKCS11", sunPkcsProvider);
keyStore.load(null, userPin);

Save Private key to keystore

Here I’m saving an RSA 2048 Private key to the keystore

keyStore.setKeyEntry(
keyAlias, // Alias for this key
privateKey, // of type java.security.PrivateKey
ksPasswd.toCharArray(), // keystore password to protect key
caCertChain); // the cert chain for its public key
keyStore.store(null);

Operations using stored key

We can now use the stored key to perform cryptographic operations. In this case, I’m using the key to decrypt content that was encrypted using its corresponding public key.

PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias, ksPasswd.toCharArray());
Cipher decryptCipher = null;
decryptCipher = Cipher.getInstance("RSA");
decryptCipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] encryptedBytes = decryptCipher.doFinal(dataBytes);
String plaintextString = new String(encryptedBytes);

What if we print this private key on the console?

The HSM will let you perform crypto operations using the stored private key, but won’t let you access the content of the key itself. Here is what we get if we try to print the key to the console via the Java code.

Private key content when printed to console

Concluding notes

Being a software implementation of a generic HSM, SoftHSM allows us to explore the cryptoki standard and understand its usage even if we don’t have a real HSM. In Java application code, we can leverage JCA APIs to interact with the HSM via the Sun PKCS#11 provider, which acts as a bridge between JCA APIs and platform specific native PKCS#11 module.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store