如何使用智能卡对 jar 进行签名

How to sign a jar with a smartcard

我在工作中使用的是 PKCS11 智能卡,我想使用 jarsigner 使用我卡上的证书对 jar 文件进行签名。

我主要在 Linux 上工作。 Coolkey可以看到卡片了。

Oracle's documentation 提及智能卡:

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

但是显然没有那个名字的实际提供者,而且 jarsigner 的 -list 参数似乎不存在。

我终于让 jarsigner 看到了卡片,但它报告说它不是有效的条目并且没有私钥:

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.

其中 smartcard.config 是:

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

我通过在 Java:

中加载卡片,以编程方式获得 "Identity #0" 来自的别名列表
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"

这些智能卡的重点是私钥留在卡上,卡就是签名的地方;有没有办法让 jarsigner 做我想做的事?

编辑:

经过一番摸索,我注意到我似乎无法使用 SHA1withDSA,只能使用 SHA1withRSA:

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

打印此列表:SHA512withRSA、SHA256withRSA、SHA1withRSA、MD5withRSA、RSA/ECB/PKCS1Padding、SHA384withRSA、MD2withRSA、RSA、PKCS11

但我注意到签名的罐子似乎都使用 DSA;这可能是问题所在吗?

我将 -sigalg 添加到可用的算法 [在本例中为 SHA256withRSA],这很有帮助。还有:

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

我偶然混合了来自几个不同 JDK 的二进制文件。

所以,我使用的最终命令行是:

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'

其中 ~/cardpass 是一个只包含 notmyrealpass 的文件,card_linux.config 是

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

与@Gary B 的上述类似,除了我使用的是 OpenSC,这是我的命令:

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'

我用的卡是PIV(也适用于CAC)。 pkcs11.cfg 是

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

在命令行中不包括卡 PIN 会提示输入 PIN(又名密钥库密码)。

以上已通过 CAC 和 Yubikey NEO(在 PIV 配置中)、JDK-1.8.0_102 和 Github mouse07410/OpenSC.[=12 进行了测试=]