Accessing the Luna HSM Keystore

This is a code example for accessing credentials stored on a Luna SA HSM for use with digital signatures. This example is implemented as a Grails service.

package com.geekcredential.crypto

// The EnvironmentContext class reads out environment variables that are set in the Tomcat 
// conf/context.xml of whatever environment this class is running in. This enables us to 
// connect to different HSM partitions for development or production servers.
import com.geekcredential.common.ui.EnvironmentContext
import java.security.cert.Certificate
import java.security.PrivateKey
import java.security.Security
import java.security.KeyStore
import com.safenetinc.luna.LunaSlotManager

/*
 * The KeyStoreService provides a layer of abstraction between e-signing
 * resources and the code that uses them. It manages connections to an
 * external certificate storage device and caches certificates in memory to
 * reduce the number of times it is necessary to retrieve them. Other
 * information also used in the e-signing process is also made available
 * through the KeyStoreService as a convenience.
 */
class KeyStoreService {

    boolean transactional = true

    // Represents a connection to a hardware device. The Luna SA has 4 "slots", but three
    // of those are ports where a USB PED can be plugged in. Slot #1 is a handle to the
    // cryptographic acceleration & storage device inside the Luna SA server.
    private LunaSlotManager hsmConnection

    private String getEnvironmentVariable(String variableName) {
        String value = EnvironmentContext.getEnv(variableName)
        if (!value || value == 'null') {
            throw new Exception("Environment variable '${variableName}' is not set. Please add it to the             environment configuration (Tomcat conf/context.xml if this application is running as a .war) and restart this application.")
        }
        return value
    }

    private String getPartitionName() {
        return getEnvironmentVariable("esignaturePartitionName")
    }

    private String getPartitionPassword() {
        return getEnvironmentVariable("esignaturePartitionPassword")
    }

    private String getPrivateKeyLabel() {
        return getEnvironmentVariable("esignaturePrivateKeyLabel")
    }

    private String[] getCaCertLabels() {
        return getEnvironmentVariable("esignatureCaCertLabels").split(";")
    }

    private String getCertLabel() {
        return getEnvironmentVariable("esignatureCertLabel")
    }

    String getContactEmail() {
        return getEnvironmentVariable("esignatureContactEmail")
    }

    String getTimestampUrl() {
        return getEnvironmentVariable("esignatureTimestampUrl")
    }

    String getOcspUrl() {
        return getEnvironmentVariable("esignatureOcspUrl")
    }

    String getPdfOwnerPassword() {
        return getEnvironmentVariable("esignaturePdfOwnerPassword")
    }

    void refreshHsmConnection() {
        try {
            resolveLunaSlotManagerInstance()
            hsmConnectionLogin()
        } catch (Throwable t) {
            log.fatal("Unable to login to the Hardware Storage Module (HSM). E-signing can't be completed without access to a certificate", t)
            throw new Exception("Unable to login to the Hardware Storage Module (HSM). E-signing can't be completed without access to a certificate", t)
        }
    }

    private void hsmConnectionLogin() {
        synchronized (hsmConnection) {
            if (!hsmConnection.loggedIn) {
                hsmConnection.login(partitionPassword)
            }
        }
    }

    private void resolveLunaSlotManagerInstance() {
        if (!hsmConnection) {
            hsmConnection = LunaSlotManager.getInstance()
        }
        if (!hsmConnection) {
            throw new Exception("LunaSlotManager did not return an instance.")
        }
    }

    Map getCredentials() {
        try {
            if (!Security.getProvider("LunaProvider")) {
                Security.addProvider(new com.safenetinc.luna.provider.LunaProvider())
            }
            refreshHsmConnection()
            KeyStore ks = KeyStore.getInstance("Luna")
            ks.load(null, null)

            Map credentials = [:]
            credentials.put("privateKey", (PrivateKey) ks.getKey(privateKeyLabel, null))

            // We need to assemble the certificate chain manually because the HSM doesn't support the
            // getCertificateChain method. The array of certificates in the chain should be ordered
            // starting with our cert and then proceeding to any intermediate certificate authority certs
            // up to the original issuer.
            List chain = [ks.getCertificate(certLabel)]
            caCertLabels.each {label ->
                chain << ks.getCertificate(label)
            }
            credentials.put("certificateChain", chain.toArray(new Certificate[chain.size()]))

            return credentials
        } catch (Throwable t) {
            log.fatal("Unable to retrieve resources from the Hardware Storage Module (HSM). E-signing can't be completed without a private key and certificate chain", t)
            throw new Exception("Unable to retrieve resources from the Hardware Storage Module (HSM). E-signing can't be completed without a private key and certificate chain", t)
        }
    }

}
Advertisements

#digital-signature, #grails, #hsm, #luna-sa