3

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

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 的。

4

2 回答 2

2

我曾经实现过类似的东西,但不幸的是智能卡驱动程序有问题,因此驱动程序有时会尝试调用在驱动程序本身中实现的本机 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的 s 发送到您的CallbackHandler. 处理回调时,您只需传递缓存的密码。

不用说,密码缓存通常不受欢迎;)

于 2011-07-08T01:47:31.840 回答
1

MS CryptoAPI 根本不提供任何方法来指定 PIN。如果可能,您唯一的选择是从 CryptoAPI 切换到 PKCS#11——PKCS#11 要求您以代码“登录”到设备并在代码中提供 PIN。

更新:一些硬件供应商提供的一些CSP(加密服务提供商)模块允许您调用特殊的 CryptoAPI 函数(CryptSetProvParam,https ://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt -cryptsetprovparam ) 并将 PIN 传递给它。如果您的硬件的 CSP 支持这种设置 PIN 的方法,您需要联系供应商以获取信息,如果支持,确切的参数 ID 是什么等。无论如何,您目前无法从您的设备中以这种方式设置 PIN使用标准 SunMSCAPI 提供程序的 Java 应用程序。

于 2011-06-27T17:56:34.857 回答