Gary B
Gary B

Reputation: 533

How to sign a jar with a smartcard

I'm using a PKCS11 smartcard at work, and would like to use jarsigner to sign jar files using the certificate on my card.

I'm mostly working on Linux. Coolkey can see the card.

Oracle's documentation mentions smartcards:

jarsigner -keystore NONE -storetype PKCS11 \
    -providerName SunPKCS11-SmartCard \
    -list

But there's apparently no actual provider by that name, and the -list argument to jarsigner doesn't appear to exist.

I finally got jarsigner to see the card, but it reports that it isn't a valid entry and doesn't have a private key:

jarsigner -keystore NONE -storetype PKCS11 \
  -providerClass sun.security.pkcs11.SunPKCS11 \
  -providerArg smartcard.config \
  -storepass notmyrealpass \
  myjarfile.jar 'Identity #0'

jarsigner: Certificate chain not found for: Identity #0.
  Identity #0 must reference a valid KeyStore key entry
  containing a private key and corresponding public key
  certificate chain.

Where smartcard.config is:

name=Kittens
library=/usr/lib/pkcs11/libcoolkeypk11.so

I got the list of aliases [from which "Identity #0" comes] programmatically by loading the card in Java:

String conf = "name=Kittens\nlibrary=/usr/lib/pkcs11/libcoolkeypk11.so";
InputStream s = new ByteArrayInputStream(conf.getBytes());
Provider p = new sun.security.pkcs11.SunPKCS11(s);
KeyStore keyStore = KeyStore.getInstance("PKCS11", p);
keyStore.load(null, "notmyrealpass".toCharArray());
Enumeration<String> aliases = keyStore.aliases();
// Aliases contains "Identity #0", "Identity #1", "Identity #2"

The whole point of these smartcards is that the private key stays on the card, and the card is what does the signing; is there no way to make jarsigner do what I want?

EDIT:

After some more noodling, I noticed that I don't seem to be able to use SHA1withDSA, only SHA1withRSA:

Set<Provider.Service> services = p.getServices();
for(Provider.Service service : services) {
    System.out.println(service.getAlgorithm());
}

prints this list: SHA512withRSA, SHA256withRSA, SHA1withRSA, MD5withRSA, RSA/ECB/PKCS1Padding, SHA384withRSA, MD2withRSA, RSA, PKCS11

But I notice that signed jars all seem to use DSA; is this likely to be the issue?

Upvotes: 2

Views: 3746

Answers (2)

Mouse
Mouse

Reputation: 592

Similar to the above by @Gary B, except that I'm using OpenSC, and here's my command:

jarsigner -tsa http://timestamp.digicert.com \
    -keystore NONE -storetype PKCS11 \
    -providerClass sun.security.pkcs11.SunPKCS11 \
    -providerArg /Library/Java/Extensions/pkcs11.cfg 
    -sigalg SHA256withRSA \
    testjni_signed.jar \
    'Certificate for Digital Signature'

The card I use is PIV (works for CAC too). pkcs11.cfg is

name = OpenSC
library = /Library/OpenSC/lib/opensc-pkcs11.dylib
description = OpenSC PKCS#11 interface for SmartCard
#showInfo = true
slot = 0

Not including the card PIN in the command line makes it prompt for the PIN (aka Passphrase for Keystore).

The above has been tested with CAC and Yubikey NEO (in PIV configuration), JDK-1.8.0_102, and Github mouse07410/OpenSC.

Upvotes: 1

Gary B
Gary B

Reputation: 533

I added -sigalg to an algortihm that was available [SHA256withRSA, in this case], and that helped. Also:

jarsigner: This jar contains entries whose certificate chain is not validated

I was mixing binaries from a couple different JDKs by accident.

So, the final command line I'm using is:

jarsigner \
    -tsa http://timestamp.digicert.com \
    -keystore NONE \
    -storetype PKCS11 \
    -providerClass sun.security.pkcs11.SunPKCS11 \
    -providerArg card_linux.config \
    -storepass `cat ~/cardpass` \
    -sigalg SHA256withRSA \
    dist/sup2rtam.jar \
    'Identity #0'

Where ~/cardpass is a file containing nothing but notmyrealpass, and card_linux.config is

name=CAC
library=/usr/lib/pkcs11/libcoolkeypk11.so

Upvotes: 3

Related Questions