使用SunMSCAPI签署文档并禁止“输入PIN”对话框

时间:2011-06-27 17:27:08

标签: java digital-signature digital-certificate

我正在开发一个使用证书令牌签署文档的java代码。到目前为止,一切都很好,但我想抑制“输入引脚”对话框,因为我存储用户的引脚,所以他/她不需要每次都输入它。这里的真正问题是此代码将以批处理模式运行(无用户交互)。我知道一旦键入,键可能在内存中,因此不需要在短时间内再次键入。但我不能依赖它,我需要提供密码。这是我到目前为止的代码(它只是一个样本,它可能不完整也不起作用):

protected KeyStore loadKeyStoreFromSmartCard()  {
  keyStore = KeyStore.getInstance("Windows-MY", "SunMSCAPI");
  keyStore.load(null, null);
  return keyStore;
}

public void signDocument(byte[] conteudoParaAssinar, String certAlias) {
    char[] pass = (char[]) null;
    PrivateKey key = (PrivateKey) loadKeyStoreFromSmartCard.getKey(certAlias, pass);
    Certificate[] chain = loadKeyStoreFromSmartCard(true).getCertificateChain(certAlias);
    CertStore certsAndCRLs = CertStore.getInstance("Collection", new CollectionCertStoreParameters(Arrays.asList(chain)), "BC");
    X509Certificate cert = (X509Certificate) chain[0];
    CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
    gen.addSigner(key, cert, CMSSignedDataGenerator.DIGEST_SHA1);
    gen.addCertificatesAndCRLs(certsAndCRLs);
    CMSProcessable data = new CMSProcessableByteArray(conteudoParaAssinar);
    CMSSignedData signed = gen.generate(data, true, "SunMSCAPI");
    byte[] envHex = signed.getEncoded();
}

修改

我听说CryptSetProvParam PP_KEYEXCHANGE_PIN女巫可能是解决方案,但我不知道如何从java中调用它。我找到的所有例子都是针对.net。

2 个答案:

答案 0 :(得分:3)

我实现了类似于此的一次,但不幸的是智能卡驱动程序有问题,因此驱动程序试图提出驱动程序本身有时实现的原生PIN回调。但是我们假设你的司机在那方面做得更好。

首先,您需要实现CallbackHandler,文档概述了该概念。在你的情况下,这是PasswordCallback案件有趣处理。

接下来,按如下所示创建KeyStore(省略异常处理)

Provider provider = Security.getProvider("SunMSCAPI");
CallbackHandler cbh = // your implementation
KeyStore.ProtectionParameter protection = new KeyStore.CallbackHandlerProtection(cbh);
//get a handle of the CAPI KeyStore as before
KeyStore.Builder keystoreBuilder = KeyStore.Builder.newInstance("Windows-MY",
                                                                provider, 
                                                                protection);
KeyStore store = keystoreBuilder.getKeyStore();

然后,要访问私钥,请执行以下操作:

KeyStore.Entry ke = store.getEntry(alias, null);
if (!(ke instanceof KeyStore.PrivateKeyEntry))
    throw new RuntimeException("The entry is not a private key.");
PrivateKey key = ((KeyStore.PrivateKeyEntry) ke).getPrivateKey();

提供商会自动生成相应的PasswordCallback以发送给您的CallbackHandler。处理回调时,您只需传递缓存的密码即可。

毋庸置疑,密码缓存通常不受欢迎;)

答案 1 :(得分:0)

MS CryptoAPI未提供指定PIN的统一方式。您唯一的选择是在可能的情况下从CyrptoAPI切换到PKCS#11 - PKCS#11要求您在代码中“登录”设备并在代码中提供PIN。

更新:某些硬件供应商提供的某些 CSP(加密服务提供商)模块允许您调用特殊的CryptoAPI函数(CSPSetProvParam,http://msdn.microsoft.com/en-us/library/aa379858%28v=VS.85%29.aspx)并将PIN传递给它。如果硬件的CSP支持这种设置PIN的方法,那么您需要联系供应商以获取信息,如果确实如此,确切的参数ID是什么等。