Luna SA HSM Concepts

A Hardware Storage Module (HSM) is a more secure alternative to the keystore file most Java developers are familiar with. The HSM stores cryptographic data such as private keys and certificates in RAM or flash storage. Through software APIs, the Luna HSM participates in cryptographic operations like digital signatures without actually releasing the secured credentials to your applications, where they potentially could be exported or misused.

This was an important point for me to understand. Initially, I thought that I could get a private key and certificate chain from my Luna HSM at application startup, just like a keystore file, and then cache the credentials in memory for when I needed them. But it doesn’t work that way: the Luna API objects only give you pointers to data stored on the HSM; not the data themselves. My ‘cached certificates’ only resulted in NullPointerExceptions because the connections had expired by the time I tried to use them.

Most HSM devices include extra-paranoid security features to ensure that the data they hold does not fall into hostile hands. In addition to requiring authentication to use stored objects, the Luna SA includes tamper-detection features like a chassis opening sensor and an internal thermometer. If an intrusion is detected, the HSM erases its storage to prevent sensitive data from being stolen. You will want to make sure that all of your data center admins are aware of these features to prevent accidents!

The Luna SA also includes dedicated hardware for accelerated cryptographic calculations. Some models have high availability features.

Administration

There are multiple layers of systems on a Luna SA:

  • The appliance operating system
  • The HSM card
  • Logical partitions created within the HSM

The operating system’s root user is named ‘admin’. Admin can manage the device’s base configuration: networking, time, hardware settings. You can give the admin password to to data center staff who maintain the appliance, because the admin user has no authorization to access the HSM contained within.

The HSM itself is installed in a ‘slot’ inside the Luna SA. (I don’t know if ‘slot’ refers to a physical slot for an expansion card or just a logical interface to the HSM hardware. On the Luna SA 5, the built-in HSM is in slot 1 and slots 2 – 4 are available for backup devices to plug into.) The HSM has its own administrator, apart from the OS admin. It is sometimes referred to as the ‘security officer’ or ‘SO’ in documentation. You can manage the HSM by first logging into the Luna as the OS admin and then running the hsm command and supplying the security officer password.

Within the HSM, the SO can configure logical partitions for storage of crypto artifacts. Each partition has its own password. So one Luna SA can be used by multiple groups or applications. At my company, we’ve created one partition for certificates used by developers and another for production. That satisfies the regulatory requirement for separation of duties and controls between development and production personnel.

Some administration functions are done from a command line on the Luna. Others need to be performed remotely, using the command line tools that are provided with the client software.

Luna SA Setup

Turning on the appliance for the first time and configuring networking is pretty straightforward and is explained well by the documentation, so I don’t think it’s necessary for me to cover it. The only questions our network admin had for me related to some HSM-specific terms in the documentation.

Cloning refers the the backup process for the HSM. To permit the contents of the HSM to be backed up to a USB token, you need to enable cloning and assign the same cloning domain name to both the HSM and the backup token.

PIN means ‘password’ – at least in our configuration. If you have configured FIPS 140-2 Level 3 security, then the authentication process is different.

Manufacturer Documentation

Complete documentation, including a Getting Started guide and a full listing of options for all of the commands used below, is included with the Luna SA client software, in the form of a collection of HTML pages which you can unzip into a local directory. In addition, once you have the client software installed, you will find a Java API Guide and example code in the Program Files\LunaSA\JSP directory on your computer.

Installing Client Software

Instructions are provided for installing and configuring on Windows and AIX, since those are the operating systems I have worked with. The process for Linux or other UNIX breeds is probably pretty similar to AIX.

Tips for installing the client software:

Windows

  • The main installer launches several other installs before it completes. If you’re developing in Java or Groovy, you need the Java Security Provider (JSP). If you’re developing in C, you need the C Security Provider (CSP).
  • You may need to add C:\Program Files\LunaSA\JSP\lib to PATH in your Windows environment variables. (This puts LunaAPI.dll on java.library.path. You can run groovy -e “println System.getProperty(‘java.library.path’)” from a command line to verify what’s in java.library.path.)
  • Make sure there is a Windows environment variable named “ChrystokiConfigurationPath” and add it if it is missing. The value should indicate the location of the crystoki.ini file, which should be “C:\Program Files\LunaSA” in the default installation.

AIX

  • Before starting the installation, ensure that you have a Random Number Generator (RNG) or Entropy Gathering Daemon (EGD) on your system at one of /dev/egd-pool, /etc/egd-pool, /etc/entropy, or /var/run/egd-pool.
  • If you’re developing in Java or Groovy, answer ‘Y’ to install the Java Security Provider (JSP) and ‘Y’ to install the SDK.
  • Following installation, it is necessary to configure any environment where the Luna client is going to be used so that the Java runtime can interface with the native libLunaAPI.so library. Some of these methods for setting java.library.path are redundant. If you don’t have libLunaAPI.so on java.library.path, it will result in
    java.lang.UnsatisfiedLinkError: LunaAPI_64 (Not found in java.library.path)
    • Add /usr/lunasa/jsp/lib/ to the PATH environment variable
    • Add -Djava.library.path=/usr/lunasa/jsp/lib/ to the JAVA_OPTS environment variable
    • Add “/usr/lunasa/jsp/lib/” to the LIBPATH environment variable
    • Include /usr/lunasa/jsp/lib/LunaProvider.jar in your CLASSPATH

Registering a Client with the Luna HSM

The client software communicates with the HSM using SSH. Before you can start working with the crypto features, you will need to set up a trust between your client and the HSM by exchanging keys.

My experience has indicated that when a command requires you to specify a hostname you should provide only the unqualified hostname, without the domain name, for the server or client. The tools create files using the hostnames you enter, and in some cases, extra periods in the filenames seem to cause issues. Also, be consistent with case when you type the hostnames. They are added to configuration files and certificates exactly as you type them.

  • On the client, open a command prompt and cd to the directory where the client software is installed. On Windows that will be C:\Program Files\LunaSA. On AIX that will probably be /usr/lunasa/bin.
  • Download a copy of the server’s public key. On AIX, you can use the built in secure copy (scp) command. For Windows, PuTTY pscp is included with the client software.
    • Windows:
      scp admin@<hsm_hostname>:server.pem ../cert/server
    • AIX:
      pscp admin@<hsm_hostname>:server.pem ./cert/server
  • Configure your client to trust the server. This step uses the vtl client command.
    • Windows:
      vtl addServer -n <hsm_hostname> -c cert/server/server.pem
    • AIX:
      ./vtl addServer -n <hsm_hostname> -c ../cert/server/server.pem
  • Generate a private key for your client. Running this command will cause two files to be created in LunaSA/cert/client.
    • Windows:
      vtl createCert -n <client_hostname>
    • AIX:
      ./vtl createCert -n <client_hostname>
  • Export the client cert you generated and transmit it to the server. Note that there is a colon on the end of the server’s hostname, designating that you want to copy your client certificate to the root directory on the server. If you leave the colon off, it will result in the error message “Local to local copy not supported”.
    • Windows:
      pscp "cert\client\<client_hostname>.pem" admin@<hsm_hostname>:
    • AIX:
      scp ../cert/client/<client_hostname>.pem admin@<hsm_hostname>:
  • On the hsm command line, associate the key file to your client by hostname. This command registers the client by its DNS hostname. If your client isn’t found in DNS, the HSM can’t see it.
    client register -client <client_hostname> -hostname <client_hostname>

  • Assign the client to a partition. (Note that you can get a list of partition names by issuing the partition list command.) You will need to know the partition password.
    • On the hsm command line:
      client assignPartition -client <client_hostname> -partition <partition_name>
    • Confirm the setup. On the client command line:
      vtl verify

Requesting and Importing Certificates

  • On the client, open a command prompt and cd to the directory where the client software is installed. On Windows that will be C:\Program Files\LunaSA. On AIX that will probably be /usr/lunasa/bin.
  • Generate public and private keys using the certificate management utility, cmu. Once you have objects stored on the HSM, you will reference them by either numeric handle or text label. It’s best to give the keys descriptive labels, such as “developer_private_key” or “widgetco_inc_public_key”. The handles are just numeric ids assigned automatically when you upload objects.
    cmu generatekeypair -modulusbits=2048 -publicexponent=65537 -sign=T -verify=T -labelpublic="<public_key_label>" -labelprivate="<private_key_label>"
  • Create a certificate request. You can use the cmu list command to show a list of objects stored on the HSM, including the handle and label assigned to each. If there is only one keypair on the partition, the following command defaults to it. If there are more than one, you must also specify -publichandle and -privatehandle options.
    cmu requestcertificate -c="<two_letter_country_code>" -o="<organization_name>" -cn="<common_name>" -s="<state>" -l="<city/locality>" -publichandle=<public_handle> -privatehandle=<private_handle> -outputfile=""
  • The next step depends on the procedure that your certificate authority uses. But it will probably go something like this: Go to the certificate authority’s website and follow their procedures to request a new certificate. At the point when you are given a chance to supply a certificate request of your own, open the CSR file you generated and paste the text from it into the certificate request window. Download the certificate files that are generated from it, following the website’s instructions. When you are done, you should have a certificate file and one or more CA certificate files downloaded to your client.
  • Import each certificate into the HSM partition. Assign each file that you import a meaningful, unique label.
    cmu import -inputFile="<filepath_and_filename_of_cert>" -label="<certificate_label>"
  • If, for any reason, you need to change a label or other attribute after import, you can use the cmu setAttribute command.
    cmu list

    cmu setAttribute -handle=<handle> -label=<new_label>
Advertisements

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

Vagrant Up fails with “VBoxManage.exe: error: Code CO_E_SERVER_EXEC_FAILURE (0x80080005)”

I’m trying out Vagrant, and ran into a problem.  The vagrant up command failed: 

Bringing machine 'default' up with 'virtualbox' provider... 
[default] Setting the name of the VM... 
[default] Clearing any previously set forwarded ports... 
There was an error while executing `VBoxManage`, a CLI used by Vagrant for controlling VirtualBox. The command and stderr is shown below.

Command: ["list", "hostonlyifs"]

Stderr: VBoxManage.exe: error: Failed to create the VirtualBox object! 
VBoxManage.exe: error: Code CO_E_SERVER_EXEC_FAILURE (0x80080005) - Server execution failed (extended info not available) 
VBoxManage.exe: error: Most likely, the VirtualBox COM server is not running or failed to start.

A Google search revealed a lot of other people having the same problem, but not too many solutions, so I’ll document how I got past it.

This is using:

  • Vagrant 1.2.7
  • VirtualBox 4.2.16
  • Windows 7 Professional, 64-bit, SP 1

I could also reproduce the same error by running the vboxmanage list hostonlyifs command from C:\Program Files\Oracle\VirtualBox on a my command line — so this is a problem with VirtualBox and Windows, not with Vagrant itself.

Mitchell Hashimoto very helpfully pointed out that this might just be a permissions issue with Vagrant running under a different userid than VirtualBox, and that does seem to be the case.  I had started my command prompt using Run As Administrator.

Simply running from an un-elevated command line didn’t solve my problem, however. The fix was to grant some DCOM permissions on the VirtualBox Application component.

On Windows 7, that’s accomplished as follows:

  • Open Component Services from the Administrative Tools folder.
  • Drill down to Component Services > Computers > My Computer > DCOM Config.
  • Click through a couple of warnings about unregistered classes from other apps installed on my system.
  • Right click on VirtualBox Application.
  • Go to the Security tab.
  • Change Launch and Activation Permissions from Default to Customize

It’s hard to say whether any specific permission was needed or whether simply opening up the permissions for edit and then re-saving them did the trick, because once it started working, I returned my permissions back to the original defaults and it continued to work. Maybe something in my registry was corrupted and the DCOM Config editor fixed it.

#co_e_server_exec_failure, #dcom, #vagrant, #virtualbox, #windows

Bulk Insert With Grails

Importing data from a text or CSV file into a database with Grails seems like it should be a simple thing. As it turns out, however, some approaches perform dramatically better than others.

Simple GORM

Let’s assume that we’ve created a Grails domain class called MyDomain to represent the table we’re importing into, and a parseLine() method to parse one line of file data (CSV or whatever) into values. You might try something like this:

void loadTable(File file) {
file.eachLine() { line ->
MyDomain dom = new MyDomain(parseLine(line))
dom.save()
}
}

Unfortunately, you would find this code to run very slowly and you might even run out of memory. This is because Grails is caching at two different levels, causing locking and a potential memory leak. Read more about it:

So it’s better to try a different approach. There are many possibilities.

Call an External Tool

If your database platform offers a command line bulk import tool, you could simply call and execute it outside of Grails.

String command = """
db2 LOAD CLIENT FROM '/home/me/data.txt' OF ASC 
METHOD L
(1 9, 10 10, 11 19, 20 20, 21 26, 27 35, 36 71, 72 107, 108 127, 128 129, 
130 134, 135 138, 139 141, 142 144, 145 148, 149 149, 150 150, 151 155)
NULL INDICATORS (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
INSERT INTO MY_SCHEMA.MY_TABLE
STATISTICS NO
ALLOW NO ACCESS;
"""
command.execute()

But you may not have such a command line tool, or it may not be available to you in all environments. And it’s probably not a good idea to give your web server the authority to run database commands from the shell.

Groovy SQL

Another approach might be to bypass Grails’ domain classes and the underlying Hibernate persistence layer and instead use Groovy Sql.

void loadTable(File file) {
def db = grailsApplication.config.dataSource
def sql = Sql.newInstance(db.url, db.user, db.password, db.driver)
file.eachLine() { line ->
sql.execute( "insert into my_table (field0, field1, field2, ...) values (?, ?, ?, ...)".toString(), parseLine(line))
}
}

Or for better performance, in Groovy 1.8.1+ (Grails 2.0.1+), you can batch the inserts:

void loadTable(File file) {
def db = grailsApplication.config.dataSource
def sql = Sql.newInstance(db.url, db.user, db.password, db.driver)
sql.withBatch(20, "insert into my_table (field0, field1, field2, ...) values (:val0, :val1, :val2, ...)".toString() { preparedStatement ->
file.eachLine() { line ->
def fields = parseLine(line)
preparedStatement.addBatch(val0:fields[0], val1:fields[1], val2:fields[2], ...)
}
}
}

Hibernate StatelessSession

If you prefer to stick with GORM, you can. You’ll just need to compensate for Grails’ and Hibernate’s caching mechanisms, by pausing to clear the caches after every 20 records or so, as described in Burt Beckwith’s article on validation caching and Hibernate’s documentation on batch processing. Or instead of having to worry about clearing the Hibernate cache, you can open a stateless database session, which does not cache, for the duration of your import.

void loadTable(File file) {
StatelessSession session = sessionFactory.openStatelessSession()
Transaction tx = session.beginTransaction()
file.eachLine() {line ->
MyDomain dom = new MyDomain(parseLine(line))
session.insert(dom)
}
tx.commit()
session.close()
}

I have not benchmarked any of these approaches to see which performs the best. You will probably have your own reasons to prefer one over the other.

#grails, #groovy, #hibernate, #insert, #sql

Document Signing Service With Grails and iText

I’ve posted examples of document signing using iText and Grails previously. Here’s the latest iteration of the service and methods that we’re using in our production digital signature process. It works in conjunction with the KeyStoreService posted in the previous article.

This code is basically just a refactoring of the examples provided in iText in Action, 2nd Edition, Chapter 12, with a few accomodations for Groovy, Grails, and a SafeNet Luna HSM. If you are serious about using iText for digital signatures, I would definitely recommend that you buy the book.

package com.geekcredential.document

import com.geekcredential.common.output.PdfUtil
import com.geekcredential.crypto.KeyStoreService
import com.itextpdf.text.DocumentException
import com.itextpdf.text.pdf.AcroFields
import com.itextpdf.text.pdf.OcspClientBouncyCastle
import com.itextpdf.text.pdf.PdfDate
import com.itextpdf.text.pdf.PdfDictionary
import com.itextpdf.text.pdf.PdfName
import com.itextpdf.text.pdf.PdfPKCS7
import com.itextpdf.text.pdf.PdfReader
import com.itextpdf.text.pdf.PdfSignature
import com.itextpdf.text.pdf.PdfSignatureAppearance
import com.itextpdf.text.pdf.PdfStamper
import com.itextpdf.text.pdf.PdfString
import com.itextpdf.text.pdf.PdfWriter
import com.itextpdf.text.pdf.TextField
import com.itextpdf.text.pdf.TSAClient
import com.itextpdf.text.pdf.TSAClientBouncyCastle
import java.security.MessageDigest

class DocumentService {

    boolean transactional = false

    KeyStoreService keyStoreService

    private static final List<Integer> PDF_PERMISSIONS = [
            PdfWriter.ALLOW_PRINTING,
            PdfWriter.ALLOW_SCREENREADERS
    ]

    def encryptFile(File file) {
        PdfUtil.encryptFile(file, PDF_PERMISSIONS, keyStoreService.pdfOwnerPassword)
    }

    def encryptFile(byte[] content) {
        PdfUtil.encrypt(content, PDF_PERMISSIONS, keyStoreService.pdfOwnerPassword)
    }

    def applySignatures(String documentPath, String signedDocumentPath, Map signatureMap,
                        String reason, String location) {
        PdfReader reader = new PdfReader(documentPath, keyStoreService.pdfOwnerPassword.bytes)
        File signed = new File(signedDocumentPath)
        FileOutputStream fout = new FileOutputStream(signed)
        // Groovy thinks '\0' is a GString, so we have to be explicit and force it to char.
        PdfStamper stamper = PdfStamper.createSignature(reader, fout, '\0'.toCharacter().charValue(), null, true)

        // Write the 'signatures' entered by applicants to the appropriate fields in the document.
        // These aren't digital signature fields, they're just text fields.
        applyNames(stamper, signatureMap)

        // Apply digital signature
        Map credentials = keyStoreService.credentials
        PdfSignatureAppearance appearance = setAppearance(stamper, credentials, reason, location)
        setCryptoDictionary(appearance)
        // Estimate how much room the signed content will take up and reserve some space for it.
        int contentEstimated = 15000
        preClose(appearance, contentEstimated)
        byte[] encodedSig = generateDigitalSignature(appearance, credentials)
        if (contentEstimated + 2 < encodedSig.length)
            throw new DocumentException("Document signature failed: Not enough space reserved for content in the signed file")
        insertSignedContent(contentEstimated, encodedSig, appearance)
    }

    private def applyNames(PdfStamper stamper, Map signatureMap) {
        AcroFields form = stamper.getAcroFields()
        /*
        Understand form field caching: Setting up a cache for field appearances can improve iText's performance when
        filling PDFs that have a lot of fields.
        (See http://api.itextpdf.com/itext/com/itextpdf/text/pdf/AcroFields.html#setFieldCache(java.util.Map))
        */
        form.setFieldCache(new HashMap<String, TextField>())
        signatureMap.each { String field, String signature ->
            form.setField(field, signature)
        }
        // 'Flattening' eliminates the form fields and merges the contents directly into the rest of the PDF stream.
        stamper.setFormFlattening(true)
        stamper.setFullCompression()
    }

    private PdfSignatureAppearance setAppearance(
            PdfStamper stamper,
            Map credentials,
            String reason,
            String location
    ) {
        PdfSignatureAppearance appearance = stamper.getSignatureAppearance()
        appearance.setCrypto(credentials.privateKey, credentials.certificateChain, null, PdfSignatureAppearance.WINCER_SIGNED)
        appearance.setReason(reason)
        appearance.setLocation(location)
        appearance.setContact(keyStoreService.contactEmail)
        appearance.setCertificationLevel(PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED)
        return appearance
    }

    TSAClient getTimeStampAuthorityClient() {
        TSAClient tsc = new TSAClientBouncyCastle(keyStoreService.timestampUrl)
        return tsc
    }

    private byte[] getOcsp() {
        Map credentials = keyStoreService.credentials
        OcspClientBouncyCastle ocspClient = new OcspClientBouncyCastle(
                credentials.certificateChain[0],
                credentials.certificateChain[credentials.certificateChain.length - 1],
                keyStoreService.ocspUrl)
        int attempts = 0
        byte[] result = null
        // OCSP is optional, and a failure to get a response from the certificate status service should not halt the
        // signature from proceeding. Nevertheless, we give our best effort. If we see these failure messages coming
        // up in the logs frequently, then we need to complain to our service provider about their uptime.
        while (!result && attempts < 3) {
            try {
                attempts++
                result = ocspClient.getEncoded()
            } catch (Throwable t) {
                log.warn("The OCSP service at ${keyStoreService.ocspUrl} could not be contacted. Attempt #${attempts}", t)
            }
        }
        if (!result) {
            log.warn("The OCSP service at ${keyStoreService.ocspUrl} could not be contacted after 3 attempts. Document signing will proceed without confirming that our certificate is still valid")
        }
        return result
    }

    private def setCryptoDictionary(PdfSignatureAppearance appearance) {
        PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, new PdfName("adbe.pkcs7.detached"))
        dic.setReason(appearance.reason)
        dic.setLocation(appearance.location)
        dic.setContact(appearance.contact)
        dic.setDate(new PdfDate(appearance.getSignDate()))
        appearance.setCryptoDictionary(dic)
    }

    private byte[] getDigest(PdfSignatureAppearance appearance) {
        InputStream data = appearance.getRangeStream()
        MessageDigest messageDigest = MessageDigest.getInstance("SHA1")
        byte[] buf = new byte[8192]
        int n
        while ((n = data.read(buf)) > 0) {
            messageDigest.update(buf, 0, n)
        }
        byte[] hash = messageDigest.digest()
        return hash
    }

    private void preClose(PdfSignatureAppearance appearance, int contentEstimated) {
        HashMap<PdfName, Integer> exc = new HashMap<PdfName, Integer>()
        exc.put(PdfName.CONTENTS, new Integer(contentEstimated * 2 + 2))
        appearance.preClose(exc)
    }

    private byte[] generateDigitalSignature(
            PdfSignatureAppearance appearance,
            Map credentials
    ) {
        byte[] hash = getDigest(appearance)
        if (!hash) log.error("Digital signature failure: digest could not be obtained from the PDF")
        Calendar cal = Calendar.getInstance()
        // Check certificate revocation status using OCSP
        byte[] ocsp = getOcsp()
        // Create the signature
        PdfPKCS7 sgn = new PdfPKCS7(credentials.privateKey, credentials.certificateChain, null, "SHA1", null, false)
        if (!sgn) log.error("Digital signature failure: unable to obtain credentials from hardware storage")
        byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, cal, ocsp)
        if (!sh) log.error("Digital signature failure: could not generate authenticated attribute bytes")
        sgn.update(sh, 0, sh.length)
        TSAClient tsc = getTimeStampAuthorityClient()
        return sgn.getEncodedPKCS7(hash, cal, tsc, ocsp)
    }

    private void insertSignedContent(int contentEstimated, byte[] encodedSig, PdfSignatureAppearance appearance) {
        byte[] paddedSig = new byte[contentEstimated]
        System.arraycopy(encodedSig, 0, paddedSig, 0, encodedSig.length)
        PdfDictionary signedDictionary = new PdfDictionary()
        signedDictionary.put(PdfName.CONTENTS, new PdfString(paddedSig).setHexWriting(true))
        appearance.close(signedDictionary)
    }

}
package com.geekcredential.common.output

import com.itextpdf.text.pdf.PdfEncryptor
import com.itextpdf.text.pdf.PdfReader
import com.itextpdf.text.pdf.PdfWriter

class PdfUtil {

    static int sumPermissions(List<Integer> permissions){
      // Get bitwise sum of permissions
      int sum = (Integer) permissions[0]
      permissions.each {p ->
          sum = sum | p
      }
      return sum
    }

    /**
     * Encrypts a PDF and then returns it as a byte array for writing to http response.
     * @param byte[] content (PDF contents)
     * @param int[] permissions (an array of permission constants (see PdfWriter))
     * @param ownerPassword password to be applied
     */
    static byte[] encrypt(byte[] content, List<Integer> permissions, String ownerPassword) {
        ByteArrayOutputStream bout = new ByteArrayOutputStream()
        /* The userPassword and the ownerPassword can be null or have
           zero length. In this case the ownerPassword is replaced by
           a random string. */
        PdfEncryptor.encrypt(new PdfReader(content),
                             bout,
                             PdfWriter.STANDARD_ENCRYPTION_128,
                             null,
                             ownerPassword,
                             sumPermissions(permissions))
        return bout.toByteArray()
    }

    /**
     * Encrypts a PDF and then saves it.
     * @param File file
     * @param int[] permissions (an array of permission constants (see PdfWriter))
     * @param ownerPassword password to be applied
     */
    static void encryptFile(File file, List<Integer> permissions, String ownerPassword) {
        file.setBytes(encrypt(file.readBytes(), permissions, ownerPassword))
    }

}

#digital-signature, #electronic-signature, #grails, #hsm, #itext, #pdf

com.safenetinc.luna.LunaException: Cannot access sensitive attributes…

I have learned a few lessons about working with the Luna JSP.

#1: Even though you use the same KeyStore interface for accessing a file-based Java keystore (*.jks) and an HSM, don’t make the mistake of thinking that the objects that you get out of the keystore are the same. The HSM does not return actual credentials to you; it only gives you a reference to an object stored on the HSM. You cannot read or use the object, you can only pass the reference back to methods implemented by the HSM security provider.

More specifically, do not try to assign objects that you get from a keystore to a variable name. You are likely to encounter problems later, when you try to access that stored object and it’s not really there.

#2: This should have been self-evident, but I made the mistake of simply copying code from the iText examples without completely understanding it. You must use the Luna security provider when accessing stored credentials. IText’s default provider, Bouncy Castle, knows nothing about pointers to keys stored on an HSM, and you are likely to get a “Cannot access sensitive attributes…” error indicating that an attempt to get a key stored on the HSM failed.

Bad:

KeyStore keystore = KeyStore.getInstance('Luna')
keystore.load(null, null)
PrivateKey privateKey = keystore.getKey('private_key_label')
...
PdfPKCS7 sgn = new PdfPKCS7(
                    (PrivateKey) privateKey,
                    (Certificate[]) certificateChain,
                    null,
                    'SHA1',
                    null,
                    false
)

Good:

KeyStore keystore = KeyStore.getInstance('Luna')
keystore.load(null, null)
...
PdfPKCS7 sgn = new PdfPKCS7(
                    (PrivateKey) keystore.getKey('private_key_label'),
                    (Certificate[]) keystore.getCertificateChain('ca_chain_label'),
                    null,
                    'SHA1',
                    'LunaProvider',
                    false
)

#itext, #jca, #luna-sa

How to configure Notepad++ to always open in a new window

I tend to work with multiple documents open in front of me at once, so I can compare or copy code snippets from different sources. I generally use Notepad++ as my text editor, but the default behavior for Notepad++ is to open multiple documents in tabs within a single instance. There are ways to change that behavior, and every time I change computers or reinstall Notepad++, I find myself having to go look them up again. So here they are for my own convenience and maybe yours.

If you’re launching Notepad++ from a command line or a shortcut, you can simply add -multiInst as a command line argument.

Notepad++ Command Line Switches

However, that doesn’t alter the behavior when you open a document from the Windows Explorer context menu by clicking “Edit with Notepad++”. A more global way of making the application open in a new instance every time is to place an empty file named asNotepad.xml in the Notepad++ Program Files directory.

Notepad++ Configuration Files

#notepad

Reproducing Javascript’s escape() function in Groovy

From time to time, I’ve seen people looking for a way to reproduce Javascript’s escape() function in this or that language. It’s not a complex function – escape() simply replaces characters that aren’t alphanumeric (and a few symbols) with their Unicode values in hexadecimal. Anyway, I recently had a need to escape something using Groovy. Here’s the method.


/* A method to mimic Javascript's escape() function.
* It replaces 'special characters' with their Unicode hexadecimal
* equivalent. This simple method is not designed to handle
* double byte character sets. Feel free to improve it.
*/
String escapeLikeJavascript(String sourceText) {
def specialCharsRegex = /[^\w@*-+.\/]/
return sourceText.replaceAll(specialCharsRegex, {
"%${Integer.toHexString(it.codePointAt(0)).toUpperCase().padLeft(2, '0')}"
})
}