Configuring YubiHSM 2 for Java Code Signing
The purpose of the scripts in this repository is to generate an RSA keypair and enroll for an X.509 certificate to a YubiHSM 2 using YubiHSM-Shell as the primary software tool. In addition to YubiHSM-Shell, Java KeyTool and OpenSSL are used.
Two scripts are published in the folder Scripts: the Windows PowerShell script YubiHSM_Cert_Enroll.ps1
and the Linux Bash script YubiHSM_Cert_Enroll.sh
.
When the RSA keypair and certificate have been enrolled to the YubiHSM 2, the YubiHSM 2 PKCS #11 library can then be used with the Sun JCE PKCS #11 Provider.
More specifically, the key/certificate can be used for signing Java code, for example using JarSigner
.
The following steps are performed by the scripts:
- Generate an RSA keypair in the YubiHSM 2.
- Export the CSR (Certificate Signing Request).
- Sign the CSR into an X.509 certificate (using OpenSSL CA as an example).
- Import the signed X.509 certificate into the YubiHSM 2.
The scripts are not officially supported and are provided as-is. The scripts are intended as references, and YubiHSM 2 administrators should ensure to read Yubico’s documentation on managing YubiHSMs, see Introduction before making any deployments in production.
Prerequisites
Operating System and SDKs
Use a computer with Windows 10 or a Linux distribution as the operating system.
Attach the YubiHSM 2 device to one of the available USB ports on the computer.
Install the following software SDKs and tools:
- YubiHSM SDK (including YubiHSM-Setup, YubiHSM-Shell and YubiHSM-Connector)
- OpenSSL
- Java JDK (including KeyTool and JarSigner)
Basic Configuration of YubiHSM 2
Start the YubiHSM-Connector, either as a service or from a command prompt.
Launch the YubiHSM-Shell in a different command prompt, and run the following to make sure that the YubiHSM 2 is accessible:
yubihsm-shell
Using default connector URL: http://127.0.0.1:12345
yubihsm> connect
Session keepalive set up to run every 15 seconds
yubihsm> session open 1 password
Created session 0
yubihsm> list objects 0
Found 1 object(s)
id: 0x0001, type: authentication-key, sequence: 0
Configuration File for YubiHSM 2 PKCS #11
Create the configuration file yubihsm_pkcs11.conf
and store it in the same folder as the yubihsm_pkcs11
module (which is typically C:\Program Files\Yubico\YubiHSM Shell\bin\pkcs11\
on Windows and /usr/lib64/pkcs11/
on Linux).
Configure the yubihsm_pkcs11.conf
according to the instructions on the Configuration webpage. If the YubiHSM-Connector is running on the same machine, it is sufficient to copy the Configuration File Sample and paste it into the file yubihsm_pkcs11.conf
.
Configuration File of Sun JCE PKCS #11 Provider with YubiHSM 2
Next, the YubiHSM 2 PKCS #11 module must be configured for use with the Sun JCE PKCS #11 Provider.
Create the configuration file sun_yubihsm2_pkcs11.conf
with the following content:
name = yubihsm-pkcs11
library = C:\Program Files\Yubico\YubiHSM Shell\bin\pkcs11\
yubihsm_pkcs11.dll
attributes(*, CKO_PRIVATE_KEY, CKK_RSA) = {
CKA_SIGN=true
}
Environment Variables
The path to the YubiHSM PKCS #11 configuration file must be set in the environment variables for Windows and Linux:
YUBIHSM_PKCS11_CONF = <YubiHSM PKCS11 folder>/yubihsm_pkcs11.conf
On Windows it is also recommended to add the following folder paths to the environment variable PATH:
'C:\Program Files\Yubico\YubiHSM Shell\bin'
'C:\Program Files\OpenSSL-Win64\bin'
'C:\Program Files\Java\jdk-<version>\bin'
Java Keystore
The Java keystore contains a number of pre-configured trusted CA-certificates. The Java signing certificate in the YubiHSM 2 will be validated against the trusted CA-certificates in the Java keystore.
It is therefore recommended to check that the CA-certificate(s) that have been used to issue the Java signing certificates are present in the Java keystore. This can be checked by running the following command:
keytool -list -cacerts -storepass <password to Java keystore>
If it is not present, add the CA-certificate(s) as trusted certificate(s) to the Java keystore. The Java tool KeyTool can be used for this purpose.
In order to update the Java keystore, start a console in elevated mode (“Run as administrator” on Windows or use “sudo” on Linux), and then run the commands below to import and verify the CA-certificate(s):
keytool -import -noprompt -cacerts -storepass <password to Java keystore>
-alias <alias of the CA-cert> -file <path to the CA-certificate file>
keytool -list -cacerts -storepass <password to Java keystore> -alias
<alias of the CA-cert>
Below are examples of the commands to import and verify the CA-certificate(s) are:
keytool -import -noprompt -cacerts -storepass changeit -alias MyCACert
-file ./rootCACert.pem
keytool -list -cacerts -storepass changeit -alias MyCACert
Signing JAR files
Consider the following minimal Java source file:
cat HelloWorld.java
public class HelloWorld {
public static void main(String [] args) {
System.out.println("Hello, world");
}
}
Compile the java source file and create an (unsigned) .jar file:
javac HelloWorld.java
jar cfe unsigned.jar HelloWorld HelloWorld.class
We can now sign this JAR file with the RSA signing key we have stored in our YubiHSM 2 and create a signed JAR file:
jarsigner -tsa http://timestamp.digicert.com -addProvider SunPKCS11
-providerArg ./sunpkcs11.conf -keystore NONE -storetype PKCS11
-storepass 0001password -signedjar signed.jar ./unsigned.jar
rsaSign
jar signed.
Warning:
The signers certificate is self-signed.
The timestamp will expire on 2031-11-10.
In this case, a self-signed certificate was used, but for others to be able to validate the certificate you should use a public CA to sign your Java code.
Note that we are using a timestamp server to record the current time in the signed JAR file. This way we do not need to resign the JAR file when the signing certificate expires.
Verifying signed JAR files
To verify the signature on the signed JAR, we use the public key certificate stored on the YubiHSM 2.
jarsigner -verify -addProvider SunPKCS11 -providerArg ./sunpkcs11.conf
-keystore NONE -storetype PKCS11 -storepass 0001password ./signed.jar
jar verified.
If we trust the signer and the Certificate Authority that issued the signer’s certificate, we can decide to run the software in the JAR file:
java -jar signed.jar
Hello, world
Note that access to the YubiHSM2 is not required when verifying a signature on a signed JAR file as the certificate is included in the JAR file itself. Verification will fail however unless the certificate was signed by a trusted Certification Authority.
Windows PowerShell script for generating keys and certificates
The PowerShell script YubiHSM_Cert_Enroll.ps1
in the Scripts folder can be executed on Windows to generate an RSA keypair and enroll for an X.509 certificate to a YubiHSM 2.
YubiHSM-Shell is used in command line mode.
OpenSSL is used as a basic CA for test and demo purposes only. For real deployments, however, the OpenSSL CA should be replaced with a proper CA that signs the CSR into an X.509 certificate.
Parameters
The PowerShell script has the following parameters.
Parameter | Purpose |
---|---|
Algorithm | Signature algorithm [Default: RSA2048] |
AuthKeyID | KeyId of the YubiHSM 2 authentication key
Default: 0x0001]
|
AuthPW | Password to the YubiHSM 2 authentication key
[Default: ]
|
CAcertificate | CA certificate used by OpenSSL (for test purposes)
[Default: TestCACert.pem]
|
CAPrivateKey | CA private key used by OpenSSL (for test purposes)
[Default: TestCAKey.pem]
|
CAPrivateKeyPW | Password of the OpenSSL keystore (for test purposes)
[Default: ]
|
CreateCSR | Generate keys and export CSR and then exit |
CSRfile | File to save the CSR request to
[Default: ./YHSM2-Sig.(date and time).csr]
|
Dname | X.500 Distinguished Name to be used as subject fields
[Default: ]
|
Domain | Domain in the YubiHSM 2 [Default: 1]
|
ImportCert | Import signed certificate created with CreateCSR |
KeyID | KeyID where the RSA keypair will be stored
[Default: 0x0002]
|
KeyName | Label of the key/certificate, same as Java alias
[Default: MyKey1]
|
LogFile | Log file path
[Default: WorkDirectory/YubiHSM_PKCS11_Enroll.log]
|
PKCS11Config | Java JCE PKCS #11 configuration file
[Default: ./sun_yubihsm2_pkcs11.conf]
|
Quiet | Suppress output |
SignedCert | Signed certificate file. [Default: ] |
WorkDirectory | Working directory where the script is executed
[Default: $PSScriptRoot]
|
All parameters have default settings in the PowerShell script. The parameters can either be modified in the PowerShell script or be used as input variables when executing the script.
Example of how to execute the PowerShell script:
$ .\YubiHSM_PKCS11_Setup.ps1 -KeyID 0x0003
Linux Bash Script for Generating Keys and Certificates
The Bash script YubiHSM_Cert_Enroll.sh
in the Scripts folder can be executed on Linux to generate an RSA keypair and enroll for an X.509 certificate to a YubiHSM 2.
YubiHSM-Shell is used in command line mode.
OpenSSL is used as a basic CA for test and demo purposes only. For real deployments, however, the OpenSSL CA should be replaced with a proper CA that signs the CSR into an X.509 certificate.
Parameters
The Bash script has the following parameters.
Parameter | Purpose |
---|---|
-a, –algorithm | Signature algorithm [Default: RSA2048] |
-c, –cacertificate | CA certificate used by OpenSSL
(for test purposes) [Default: ./TestCACert.pem]
|
-C, –createcsr | Generate keys and export CSR and then exit |
-d, –domain | Domain in the YubiHSM 2 [Default: 1] |
-f, –pkcs11configfile | Java JCE PKCS #11 configuration file
[Default: ./sun_yubihsm2_pkcs11.conf]
|
-F, –csrfile | File to save the CSR request to
[Default: ./YHSM2-Sig.(date and time).csr]”
|
-k, –keyed | KeyID where the RSA keypair will be stored
[Default: 0x0002]
|
-n, –keyname | Label of the key/certificate, same as Java Alias
[Default: MyKey1]
|
-o, –dname | X.500 Distinguished Name to be used as subject
fields [Default: ]
|
-p, –authpassword | Password to the YubiHSM 2 authentication key
[Default: ]
|
-q, –quiet | Suppress output
|
-r, –caprivatekeypw | Password of the OpenSSL keystore (for test
purposes) [Default: ]
|
-s, –caprivatekey | CA private key used by OpenSSL
(for test purposes) [Default: ./TestCAKey.pem]
|
-S, –signedcert | Signed certificate file. Mandatory when using
–importcert [Default: ]”
|
-t, –logfile | Log file path
[Default: ./YubiHSM_PKCS11_Enroll.log
|
All parameters have default settings in the Bash script. The parameters can either be modified in the Bash script or be used as input variables when executing the script.
Example of How to Execute the Bash Script
$ ./YubiHSM_PKCS11_Setup.sh -k 0x0002 -n MyKey -d 1 -a rsa2048 -i 0x0001
-p password -c ./TestCACert.pem -s ./TestCAKey.pem -f
./sun_yubihsm2_pkcs11.conf
List the Objects on YubiHSM 2
The created RSA keypair and X.509 certificate can now be accessed through YubiHSM 2 PKCS11 and be used with Sun JCE PKCS11 Provider.
It is recommended to check that the RSA keypair and the X.509 certificate have been created on the YubiHSM 2. It is possible to use either YubiHSM-Shell or Java KeyTool to list and check those objects on the YubiHSM 2.
Example: YubiHSM-Shell Command
yubihsm> list objects 0
Found 3 object(s)
id: 0x0001, type: authentication-key, sequence: 0
id: 0x0002, type: opaque, sequence: 1
id: 0x0002, type: asymmetric-key, sequence: 0
yubihsm> get objectinfo 0 0x0002 asymmetric-key
id: 0x0002, type: asymmetric-key, algorithm: rsa2048, label:
"........................................", length: 896, domains: 1,
sequence: 0, origin: generated, capabilities: exportable-under-wrap:
sign-attestation-certificate:sign-pkcs:sign-pss
Example: Java KeyTool Command
keytool -list -keystore NONE -storetype PKCS11 -providerClass
sun.security.pkcs11.SunPKCS11 -providerArg sun_yubihsm2_pkcs11.conf
-storepass 0001password -v
Keystore type: PKCS11
Keystore provider: SunPKCS11-yubihsm-pkcs11
Your keystore contains 1 entry
Alias name: MyKey1
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=YubiHSM Attestation id:0xd353
Issuer: EMAILADDRESS=admin@test.se, CN=TestCA, OU=Test, O=Yubico,
L=Stockholm, ST=Stockholm, C=SE
Serial number: 23161118fc1d59fbab75138b562a4b00c8163c3d
Valid from: Wed Apr 14 10:43:28 CEST 2021 until: Sat Aug 27 10:43:28
CEST 2022
Certificate fingerprints:
SHA1: 38:1E:81:1A:0A:6E:B0:87:E0:B6:5C:8A:B8:C6:EC:91:1D:51:28:1A
SHA256: CC:F7:26:6C:70:12:7E:E3:62:22:71:9B:3C:32:16:C8:C6:34:10:
F:49:22:7A:18:70:09:E3:3E:73:42:38:47
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 1
Using YubiHSM 2 with Java Signing Applications
When the YubiHSM 2 has been configured with an RSA keypair and a X.509 certificate, the YubiHSM 2 PKCS11 can now be used with any Java signing application that utilizes the default Sun JCE PKCS11 Provider.
For example, JarSigner can be used to sign a JAR-file with the YubiHSM 2 and validate the signed JAR-file.
Example: Use JarSigner to sign a JAR-file
jarsigner -keystore NONE -storetype PKCS11 -providerClass
sun.security.pkcs11.SunPKCS11 -providerArg sun_yubihsm2_pkcs11.conf
lib.jar MyKey1 -storepass 0001password -sigalg SHA256withRSA -tsa
http://timestamp.digicert.com -verbose
...
jar signed.
Example: Use JarSigner to Validate a Signed JAR-file
jarsigner -verify lib.jar -verbose -certs
...
jar verified.
Signing XML files using YubiHSM 2
Many applications make use of XML to structure data stored in files, databases, or elsewhere. To establish trust in such data, these documents can be signed using XML Signatures.
In order to sign XML documents you can use a tool called xmlsectool. As xmlsectool
is implemented as a Java application using the JCA en JCE standards, we can use a YubiHSM 2 to store the signing keys we use for generating XML signatures.
A simple example
As an example, generate an RSA key pair and a self-signed certificate stored on the YubiHSM 2:
$ keytool -keystore NONE -storetype PKCS11 -storepass 0001password -addProvider SunPKCS11 -providerArg ./sunpkcs11.conf -genkey -alias rsaSign -keyalg RSA -dname CN=rsaSign
Generating 2,048 bit RSA key pair and self-signed certificate (SHA256withRSA) with a validity of 90 days
for: CN=rsaSign
As before, we are using the SunPKCS11 provider to interface with the YubiHSM2, similar to other examples in this chapter.
Signing XML files
Let’s generate a very simple XML file:
$ echo '<x></x>' > unsigned.xml
Sign the XML file using xmlsectool
:
$ xmlsectool --sign --pkcs11Config ./sunpkcs11.conf --inFile unsigned.xml --keyAlias rsaSign --keyPassword 0001password --outFile signed.xml
INFO XMLSecTool - Reading XML document from file 'unsigned.xml'
INFO XMLSecTool - XML document parsed and is well-formed.
INFO XMLSecTool - XML document successfully signed
INFO XMLSecTool - XML document written to file /home/user/signed.xml
The signed XML document nog contains a Signature
element containing the a SignatureValue
and
a KeyInfo
element containing a copy of the X.509 certificate on the YubiHSM 2:
<x>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<ds:Reference URI="">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>9hy1oK7rXCJu4rTqLZ7cGUH3rPyGm4QllC8VRv6mX60=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>ce5SooQsD...aiUDiOkaBiWI8A4olAuRcIgme0PqeLg==</ds:SignatureValue>
<ds:KeyInfo>
<ds:KeyValue>
<ds:RSAKeyValue>
<ds:Modulus>uSsZh/aAk...MK4yY1LTUqF2HzSO9d4vGdWzwm4Z63ot6w==</ds:Modulus>
<ds:Exponent>AQAB</ds:Exponent>
</ds:RSAKeyValue>
</ds:KeyValue>
<ds:X509Data>
<ds:X509Certificate>MIICxzCCAa+g.../BUkO7i8reQw+6qA==</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature>
</x>
In the above document, we have shortened the Base64 encoded text elements for brevity.
Verifying XML digital signatures
To verify the signed XML file, we can also use xmlsectool
:
$ xmlsectool --verifySignature --inFile signed.xml --pkcs11Config ./sunpkcs11.conf --keyAlias rsaSign --keyPassword 0001password
INFO XMLSecTool - Reading XML document from file 'signed.xml'
INFO XMLSecTool - XML document parsed and is well-formed.
INFO XMLSecTool - XML document signature verified.
Here, we are referring to the signing certificate stored on the YubiHSM 2 to be able to verify signatures when direct access to the YubiHSM 2 is not available, we need to export the signing certificate and distribute it to whoever needs to be able to verify such signatures.
To export the signing certificate stored on a YubiHSM 2 using keytool
:
$ keytool -keystore NONE -storetype PKCS11 -storepass 0001password -addProvider SunPKCS11 -providerArg ./sunpkcs11.conf -exportcert -alias rsaSign -rfc > signing-crt.pem
We can now use xmlsectool
to verify an XML digital signature using the public key in the signing certificate:
xmlsectool --verifySignature --inFile signed.xml --certificate signing-crt.pem
INFO XMLSecTool - Reading XML document from file 'signed.xml'
INFO XMLSecTool - XML document parsed and is well-formed.
INFO XMLSecTool - XML document signature verified.
In case the signature does not verify, xmlsectool
will complain:
$ xmlsectool --verifySignature --inFile signed.xml --certificate signing-crt.pem
INFO XMLSecTool - Reading XML document from file 'signed.xml'
INFO XMLSecTool - XML document parsed and is well-formed.
WARN XMLSignature - Signature verification failed.
ERROR XMLSecTool - XML document signature verification failed
make: *** [verify] Error 7
In this case, either the XML document was changed after its signature was generated, or the public key in the certificate does not match the private key used for signing. Either way, the XML signature cannot be used to establish trust in the XML document’s authenticity.
For more information, see Using PKCS11 Credentials from the xmlsectool
documentation.
A real-world example: SAML metadata signing
One example application of using XML signatures is in identity federation, where users can logon to a web application after authenticating somewhere else. A well-known protocol used for identity federation is SAML 2.0, and this protocol is based on XML.
The parties where users may want to logon (called Service Providers) need to exchange information with the parties where users authenticate (called Identity Providers), and this SAML 2.0 Metadata is typically signed using XML Signatures so it can be automatically verified by SAML peers.
Consider the following SAML 2.0 metadata document for a fictitious Service Provider which specifies its identifier (entity ID), its SAML signing certificate and the URL endpoint an Identity Provider can direct users to in order to process a SAML authentication response:
<md:EntityDescriptor ID="XYZ123456"
xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="https://example.com/saml/sp.xml">
<md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol" >
<md:KeyDescriptor>
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>
MIHnMIGiAgEBMA0GCSqGSIb3DQEBBAUAMA8xDTALBgNVBAMMBHNpZ24wHhcNMjMwMTA1MTI0ODExWhcNMjgwNjI3MTI0ODExWjAPMQ0wCwYDVQQDDARzaWduMEwwDQYJKoZIhvcNAQEBBQADOwAwOAIxAKrBRhYU03MSaU8jBPNUx9wcc6bWhMpinZmINR0JNdh3Sk/Pddh7zskcLGonFsmasQIDAQABMA0GCSqGSIb3DQEBBAUAAzEADng7opb78PNoLZH1QzYqmxV0ZSc3rE0OlTW00W/Xq7+77OhU5vVAVYnXpQLlv6sB
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</md:KeyDescriptor>
<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" index="0" Location="https://example.com/saml/acs"/>
</md:SPSSODescriptor>
</md:EntityDescriptor>
Note that the certificate in the Metadata is intended for validating SAML protocol messages and is typically different from the certificate used for validating SAML 2.0 metadata. Either or both certificates can have their private keys stored on the YubiHSM 2, but be aware that SAML protocol messages are signed much more frequently than SAML metadata documents, so the former may require multiple YubiHSM 2 deployments in order to scale with the load on your SAML IdP or SP.
To sign this SAML metadata document, we again use xmlsectool
with the signing key stored in a YubiHSM 2. We also specify ID
as the name of the XML attribute to use in the XML signature.
xmlsectool --sign --pkcs11Config ./sunpkcs11.conf --inFile
unsigned.xml --keyAlias rsaSign --keyPassword 0001password
--outFile signed.xml --referenceIdAttributeName ID
As before, we will need to export the SAML signing certificate to distribute among our SAML peers so they can validate our signed metadata.
Example Java code using YubiHSM 2
To interface to cryptographic keys stored on a YubiHSM 2 from Java code, we can use the SunPKCS11 provider.
This has the added benefit that we can write code that is independent of the specific HSM used, as long as the HSM has a PKCS#11 module available.
Apart from writing code, we need to configure all components correctly in order for the code to work correctly. This includes the configuration of the YubiHSM 2 connector, Java keytool
, and the SunPKCS11 provider.
To illustrate, we will code a simple RSA signing example below.
Setup
Let’s assume we have a single YubiHSM 2 connected locally via USB. Store the connector configuration in a file named yubihsm.conf
and point to it via the YUBIHSM_PKCS11_CONF
environment variable so that the YubiHSM 2 PKCS#11 module will be able to find it:
$ echo "connector=yhusb://" > yubihsm.conf
export YUBIHSM_PKCS11_CONF=yubihsm.conf
We will be using Java’s keytool
to manage keys and certificates on the YubiHSM 2. For convenience, store the PKCS#11 configuration options in a file named keytool.config
:
$ cat keytool.config
keytool.all = -keystore NONE -storetype PKCS11 -storepass 0001password
-addProvider SunPKCS11 -providerArg ./sunpkcs11.conf
The file sunpkcs11.conf
is used to configure the PKCS#11 module we want to use, and the PKCS#11 attributes we want to define for objects created or imported via the SunPKCS11 provider:
$ cat sunpkcs11.conf
name = YubiHSM2
library = /usr/local/lib/pkcs11/yubihsm_pkcs11.dylib
attributes(*, CKO_PRIVATE_KEY, CKK_RSA) = {
CKA_SIGN=true
}
Finally, we can create an RSA key pair and a self-signed certificate using Java’s keytool
.
$ keytool -conf keytool.config -genkey -alias rsaSign -keyalg RSA
-dname CN=rsaSign
Generating 2,048 bit RSA key pair and self-signed certificate
(SHA256withRSA) with a validity of 90 days
for: CN=rsaSign
Note that when using keytool
, the keys are generated in software and subsequently imported into the YubiHSM 2. To generate keys on the YubiHSM 2 itself, use the yubihsm-shell
tool.
The generated certificate should now be visible from keytool
. For example:
$ keytool -conf keytool.config -list
Keystore type: PKCS11
Keystore provider: SunPKCS11-YubiHSM2
Your keystore contains 1 entry
rsaSign, PrivateKeyEntry,
Certificate fingerprint (SHA-256):
02:50:E4:1B:D8:FB:1B:07:AB:8C:05:85:37:BD:FB:89:6F:57:1F:CC:86:EC:
E5:F2:BE:61:76:68:38:58:F0:39
Code
Now, let’s turn to coding. In this example, we will be signing files using the RSA private key stored on our YubiHSM 2.
The data to be signed is a simple text file, for instance:
$ date > datatosign
The code needs to do some file I/O, and use the JCA and JCE standards to generate and verify signatures. Let’s start with the necessary imports:
import java.nio.file.Path;
import java.nio.file.Files;
import java.io.FileOutputStream;
import java.security.cert.X509Certificate;
import java.security.*;
To keep things simple, we will not handle Exceptions here and define some hardcoded parameters:
public class RsaSignP11 {
public static void main(String... argv) throws Exception {
String pkcs11Conf = "sunpkcs11.conf";
String userPin = "0001password";
String keyAlias = "rsaSign";
String infile = "datatosign";
String outfile = "signature.bin";
// see below ...
}
}
Using hard-coded parameters is only to keep the example concise. Normally these would be command-line parameters or read from a configuration file. Passwords should never be hard-coded and are typically read from a terminal on demand.
To continue with our example code, first load and configure the SunPKCS11 provider:
Provider provider = Security.getProvider("SunPKCS11");
provider = provider.configure(pkcs11Conf);
Security.addProvider(provider);
Load the PKCS11 KeyStore, authenticating with the User PIN:
KeyStore ks = KeyStore.getInstance("PKCS11", provider);
ks.load(null, userPin.toCharArray());
Retrieve the private key, and sign the data read from the datatosign
file using the SHA256withRSA
algorithm:
PrivateKey privateKey = (PrivateKey) (ks.getKey(keyAlias, null));
Signature rsaSig = Signature.getInstance("SHA256withRSA");
rsaSig.initSign(privateKey);
byte[] datatosign = Files.readAllBytes(Path.of(infile));
rsaSig.update(datatosign);
byte[] sigBytes = rsaSig.sign();
Optionally, the signature can be stored in a signature file for others to verify:
new FileOutputStream(outfile).write(sigBytes);
While we are at it, let’s also verify if the signature generated in sigBytes
can be verified using the corresponding public Key.
First we need to extract the public key from the certificate pointed to by our rsaSign
alias:
X509Certificate cert = (X509Certificate)
ks.getCertificate(keyAlias);
PublicKey publicKey = cert.getPublicKey();
Again using the SHA256withRSA
algorithm, verify that the signature in sigBytes
matches the data in datatosign
using our publicKey
:
rsaSig = Signature.getInstance("SHA256withRSA");
rsaSig.initVerify(publicKey);
rsaSig.update(datatosign);
assert rsaSig.verify(sigBytes) == true : "verify failed";
To test, compile the source:
$ javac RsaSignP11.java
Run the program:
$ java RsaSignP11
$
There is no output, meaning the assert
was passed without issues and the signatures verifies.
Troubleshooting
Debugging issues with HSM’s can be difficult. It may help to enable logging using the following JVM system properties:
For PKCS#11 keystore specific debugging info:
-Djava.security.debug=pkcs11keystore
For general SunPKCS11 provider debugging info:
-Djava.security.debug=sunpkcs11
Also, refer to the documentation on PKCS#11 with YubiHSM 2 for generating debug logs from the PKCS#11 module itself.