We recently upgraded our Jenkins CI/CD server to sign jar files during the build process. Obviously we wanted to follow the “Use what you sell” principle and attached a SmartCard-HSM to store the code signing key and certificate. However, integrating jarsigner with a SmartCard-HSM turned out to be a little more complex.

We ended up with a tool development to solve the problem. Welcome sc-hsm-jarsigner !

The sc-hsm-jarsigner is a replacement for the jarsigner command line tool provided by the JDK.

asc@caprese:~/tmp/sc-hsm-jarsigner-example$ java -jar /usr/local/bin/sc-hsm-jarsigner.jar -?
Usage sc-hsm-jarsigner <options> jarfiles*

        --reader,-r <reader>    Define card reader or token to use (default first found)
        --alias,-a <alias>      Define code signing key alias (default first found)
        --pin,-p <alias>        Define PIN for login (default prompt)
        --tsa,-t <url>          Define URL of time stamp service (e.g. http://time.certum.pl)
        --list,-l               Enumerate aliases
        --verbose,-v            Show whats going on

You need to prepare a SmartCard-HSM to hold the code signer. In our setup we create the code signer in our own CardContact CA and place the signer certificate on the device.

To verify the setup, you can enumerate the aliases of keys stored on the HSM:

asc@caprese:~/tmp/sc-hsm-jarsigner-example$ java -jar /usr/local/bin/sc-hsm-jarsigner.jar -l
codesigner

So the code signer uses the alias “codesigner”. You can select the proper alias with the -a option, otherwise the tool takes the first key on the list.

So lets sign a jar. We have an unsigned ocf-cli-2.0.0.jar:

asc@caprese:~/tmp/sc-hsm-jarsigner-example$ ls -la
total 560
drwxrwxr-x  2 asc asc   4096 13. Jun 16:42 .
drwxr-xr-x 71 asc asc  12288 13. Jun 16:33 ..
-rw-rw-r--  1 asc asc 556167 13. Jun 16:41 ocf-cli-2.0.0.jar
asc@caprese:~/tmp/sc-hsm-jarsigner-example$ jarsigner -verify ocf-cli-2.0.0.jar

jar is unsigned.

When using the default setting, the sc-hsm-jarsigner will use the first SmartCard-HSM found and the first signing key found. The user is prompted in a window to enter the PIN.

asc@caprese:~/tmp/sc-hsm-jarsigner-example$ java -jar /usr/local/bin/sc-hsm-jarsigner.jar ocf-cli-2.0.0.jar
codesigner

Signing is complete, which we can verify using the jarsigner command:

asc@caprese:~/tmp/sc-hsm-jarsigner-example$ jarsigner -verify ocf-cli-2.0.0.jar

jar verified.

Warning:
This jar contains entries whose certificate chain is invalid. Reason: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
This jar contains signatures that do not include a timestamp. Without a timestamp, users may not be able to validate this jar after any of the signer certificates expire (as early as 2026-06-12).
POSIX file permission and/or symlink attributes detected. These attributes are ignored when signing and are not protected by the signature.

Re-run with the -verbose and -certs options for more details.

The verification shows, that no timestamp was provided with the signature. That can be fixed by adding the URL of a timestamp server. We also add the PIN as a command line parameter to allow running in headless mode:

asc@caprese:~/tmp/sc-hsm-jarsigner-example$ java -jar /usr/local/bin/sc-hsm-jarsigner.jar --pin 648219 --tsa http://time.certum.pl ocf-cli-2.0.0.jar
codesigner

The signing step can be integrated in a Gradle build with

task signJar(type:Exec) {
	if (project.hasProperty('codeSignerReader') && project.hasProperty('codeSignerPIN')) {
		commandLine "java", "-jar", "/usr/local/bin/sc-hsm-jarsigner.jar", "-r", codeSignerReader, "-p", codeSignerPIN, "-t", "http://time.certum.pl", "-v", jar.archivePath
		println commandLine
	}
}

The jarsigner is also not very suitable for verifying signed jar, which is why we use the verify_jar tool from Axeos. We added the -trusted-keystore-password option to supply a password for P12 based Java truststores.

First you need to create your own truststore to store your own root CA certificate and the root CA certificate of the timestamp server:

keytool -import -keystore truststore.p12 -noprompt -file cacert.pem -alias cardcontact-rootca -storepass password
keytool -import -keystore truststore.p12 -noprompt -file /etc/ssl/certs/Certum_Trusted_Network_CA.pem -alias certum-trusted-network-ca -storepass password

With that you can verify your jar:

verify_jar -trusted-keystore truststore.p12 -trusted-keystore-password password ocf-cli-2.0.0.jar
valid

The sc-hsm-jarsigner is available in the Starterkit and the source is in the CDN.