【问题标题】:iText: using java to digitally sign PDF doc and observing java.security.NoSuchAlgorithmException erroriText:使用 java 对 PDF 文档进行数字签名并观察 java.security.NoSuchAlgorithmException 错误
【发布时间】:2014-09-03 01:45:18
【问题描述】:

我有一个带有 PDF 数字签名证书的 SafeNet eToken 5100 USB 令牌。我配置了一个 windows 和 mac 系统来使用它,并且我成功地在两台机器上用它的证书手动签署了 PDF 文件。因此,证书按预期工作。

我将令牌发送到我的网络托管公司,他们将它放在我的 Linux (CentOS) 服务器上。我在服务器上安装了 SafeNet Authentication Client。

现在我正在尝试使用 iText 来应用签名。我正在关注这本优秀书籍中的代码示例 4.2:http://itextpdf.com/book/digitalsignatures。我的代码是:

    String config = "name = eToken5100_20130805\n" +
               "library = /usr/lib64/libeTPkcs11.so\n" +
               "slot = 0";  // create a dynamic conf file
    ByteArrayInputStream bais = new ByteArrayInputStream(config.getBytes());
    Provider providerPKCS11 = new SunPKCS11(bais);
    Security.addProvider(providerPKCS11);
    System.out.println(providerPKCS11.getName());
    BouncyCastleProvider providerBC = new BouncyCastleProvider();
    Security.addProvider(providerBC);

    KeyStore ks = KeyStore.getInstance("PKCS11", providerPKCS11);
    ks.load(null, K.PASS_TOKEN);
    String alias = (String)ks.aliases().nextElement();
    PrivateKey pk = (PrivateKey)ks.getKey(alias, K.PASS_TOKEN);
    Certificate[] chain = ks.getCertificateChain(alias);
    OcspClient ocspClient = new OcspClientBouncyCastle();
    TSAClient tsaClient = null;
    for (int i = 0; i < chain.length; i++) {
        X509Certificate cert = (X509Certificate)chain[i];
        String tsaUrl = CertificateUtil.getTSAURL(cert);
        if (tsaUrl != null) {
            tsaClient = new TSAClientBouncyCastle(tsaUrl);
            break;
        }
    }
    List<CrlClient> crlList = new ArrayList<CrlClient>();
    crlList.add(new CrlClientOnline(chain));
    sign(userFile, userFile_signed, chain, pk, DigestAlgorithms.SHA256, providerPKCS11.getName(),
                 CryptoStandard.CMS, "Test", "Ghent", crlList, ocspClient, tsaClient, 0);

当我运行此代码时,它会生成以下运行时错误:

stack trace: java.security.KeyStoreException: PKCS11 not found
Caused by: java.security.NoSuchAlgorithmException: no such algorithm: 
  PKCS11 for provider SunPKCS11-eToken5100_20130805

This link 表示错误的密钥库类型可能会生成此错误。我检查了我的~glassfish/java/jdk7u25/jdk1.7.0_25/jre/lib/security/java.security 文件,它使用了jks 的密钥库。但是,将其更改为 PKCS11 会导致我的 GlassFish 服务器无法重新启动。所以我把它保留为jks

问题 1:这会导致问题吗?如果有,如何解决?

问题 2:我刚刚在上面的 java 代码中编造了名称eToken5100_20130805...但是这个名称是否需要在某个地方与其他名称匹配? (上面的Java代码是我唯一输入这个名字的地方)。

问题 3:我知道库是正确的,但是如何确定插槽号?我手动输入了插槽 0 到 6(只是猜测),它们每个都产生了上面显示的相同错误。此外,如果我输入插槽号 7 到 10,则每次都会抛出 PKCS11Exception CKR_SLOT_ID_INVALID。

我对这个话题了解不多,但我尝试通过各种方式确定槽号:

(A) 我不确定以下语法是否正确:

# keytool -v -list -keystore NONE -storetype PKCS11 -providername SunPKCS11-eToken5100_20130805 -J-Djava.security.debug=sunpkcs11,pkcs11

但它会返回此输出:

keytool error: java.security.NoSuchProviderException: no such provider: SunPKCS11-eToken5100_20130805
java.security.NoSuchProviderException: no such provider: SunPKCS11-eToken5100_20130805
at sun.security.jca.GetInstance.getService(GetInstance.java:83)
at sun.security.jca.GetInstance.getInstance(GetInstance.java:206)
at java.security.Security.getImpl(Security.java:698)
at java.security.KeyStore.getInstance(KeyStore.java:661)
at sun.security.tools.KeyTool.doCommands(KeyTool.java:765)
at sun.security.tools.KeyTool.run(KeyTool.java:340)
at sun.security.tools.KeyTool.main(KeyTool.java:333) 

(B)我也试过用modutil找槽号:

# modutil -list -dbdir $HOME/.mozilla/firefox/*.default

但我不确定它返回以下内容时的含义:

winscard_clnt.c:420:SCardEstablishContextTH() Your pcscd is too old and does not support CMD_VERSION
winscard_clnt.c:420:SCardEstablishContextTH() Your pcscd is too old and does not support CMD_VERSION

  Listing of PKCS #11 Modules
  -----------------------------------------------------------
  1. NSS Internal PKCS #11 Module
slots: 2 slots attached
status: loaded

 slot: NSS Internal Cryptographic Services
token: NSS Generic Crypto Services

 slot: NSS User Private Key and Certificate Services
token: NSS Certificate DB

2. eToken
library name: libeTPkcs11.so
 slots: 6 slots attached
 status: loaded

 slot: 
 token: 

 slot: 
 token: 

 slot: 
 token: 

 slot: 
 token: 

 slot: 
 token: 

 slot: 
 token: 

(C) 最后,我尝试运行

# pkcs11-tool --module /usr/lib64/libeTPkcs11.so --list-slots 

但这需要先安装 openSC 才能获得 pkcs11-tool。当我尝试使用:# yum install opensc.x86_64 安装时,出现以下错误:

Transaction Check Error: 
file /usr/lib64/libpcsclite.so.1.0.0 from install of pcsc-lite-libs-1.5.2-13.el6_4.x86_64
conflicts with file from package libpcsclite1-1.4.0-9.el6.x86_64`

我不知道如何克服这个错误。

我可以使用一些帮助来导航这些不熟悉的错误,了解问题所在,然后如何解决它。看起来槽号确实不正确(槽号可以很大,比如 31310?到目前为止,我假设它们是个位数...),还是别的什么?

--------- 更新 ----------

我的 linux 机器为 libpcsclite1 安装了一个软件包,导致上述错误。我卸载了 SafeNet Auth Client (SAC),删除了这个包,安装了 pcsc-lite(使用 yum),然后重新安装了标准的 SAC。我还使用 yum 安装了 opensc,所以我可以使用 pkcs11-tool。我现在可以使用以下方式查看插槽:

# pkcs11-tool --module /usr/lib64/libeToken.so -L
Available slots:
Slot 0 (0x0): AKS xxxx 00 00
 token label:   my label
 token manuf:   SafeNet, Inc.
 token model:   eToken
 token flags:   rng, login required, PIN initialized, token initialized, other flags=0x200
 serial num  :  xxxxxxx
Slot 1 (0x1): 
  (empty)
Slot 2 (0x2): 
  (empty)
Slot 3 (0x3): 
  (empty)
Slot 4 (0x4): 
  (empty)
Slot 5 (0x5): 
  (empty)

现在我可以运行所有内容而不会产生运行时错误。但是,生成的 PDF 文件显示“至少一个签名无效”。 [更新:我的错误,我正在查看错误的文件。签名的 PDF 文件显示有效签名。]

【问题讨论】:

  • 您在哪里找到该设备的 SAC(SafeNet 身份验证客户端)软件?我的理解是,您必须从销售人员那里登录到门户网站.. 我们的 IT 部门拥有.. 但在给我设备并前往周末之前似乎忘记与我分享:-(是否有其他(安全)的地方可以下载它?
  • 我没有收到他们门户的登录信息。我刚刚联系了他们的技术支持,他们通过电子邮件向我发送了一个下载链接。 safenet-inc.com/contact-us

标签: java pdf itext keystore pkcs#11


【解决方案1】:

您的 JVM 使用的密钥库与您的情况无关。您的密钥存储在您的 eToken 上。当您使用这些行时,您尝试加载的正是该密钥库:

KeyStore ks = KeyStore.getInstance("PKCS11", providerPKCS11);
ks.load(null, K.PASS_TOKEN);

因此,您的问题 1 无关紧要。顺便说一句:当我创建KeyStore 的实例时,我没有通过提供程序。如果删除 providerPKCS11 会发生什么?当您将其添加到 Security 实例时,它应该会找到提供程序。

在您的配置文件中,您选择了名称eToken5100_20130805,这很好。您可以使用任何您想要的名称。这回答了您的问题 2。

至于您的问题 3,如果您参考的书(如果我是作者),则在第 4.1.2 节中有一个关于如何找到您需要的插槽的示例,但是如果您需要一个更简单的示例,请在 StackOverflow 上阅读此问题的答案:java keytool with opensc pkcs#11 provider only works with debug option enabled

请注意,您已确定提供程序有效。您的配置文件被正确读取,否则您将看不到您在异常中选择的名称 (SunPKCS11-eToken5100_20130805)。问题是您无法加载密钥库。我找到了this post 关于这个问题。我不确定它是否有帮助。

在任何情况下:您的尝试 (A) 永远不会成功,因为 keytool 不知道配置文件,因此不知道您选择的名称。由于 Linux 上的工具之间存在冲突,您的尝试 (C) 失败。我不知道您在 (B) 中提到的 modutil 工具,但是如果我看到输出,我会假设访问令牌的库已正确加载,并且服务器中有 6 个 USB 插槽,但是这些插槽中没有 USB 令牌。也许就这么简单:也许您的 USB 令牌没有插入服务器的 USB 插槽,或者它已插入,但由于硬件驱动程序问题而看不到。

我会先与托管公司核实。

【讨论】:

  • 感谢详细的 cmets(和书!)@BrunoLowagie。我设法消除了我遇到的错误(请参阅上面的更新)。但是,生成的 PDF 显示至少一个签名无效。我尝试从getInstance() 中删除providerPKCS11,我也尝试使用slotListIndex 而不是slot,但没有任何变化。另外,我尝试了getSlotsWithTokens() 的方法,该方法可以很好地获取插槽 0。没有生成运行时错误,知道现在要检查什么吗?
  • 好的,我想你可以关闭这个问题,因为最初的问题已经解决了。要回答您的后续问题,我们需要了解您如何使用PdfStamper,并且我们需要知道您是在唱一份已经签名的文件,还是您添加了多个签名。我们还需要知道您申请的是哪种类型的签名。这真的是一个不同的问题。
  • 听起来只有一个签名。我们需要了解更多关于 PDF 的信息,以找出它无效的原因。它在 Adob​​e Reader 中显示一个红十字,是吗?
  • 非常感谢@BrunoLowagie,原来我在上面发布的更新后看到的问题是操作员错误——我查看的是未签名的 PDF,而不是签名的 PDF。现在我可以看到一切都很好。非常感谢您的帮助。
  • 感谢您的更新!很高兴看到它对你有用 ;-)
【解决方案2】:

SlotID(在您的情况下由 slot 变量指定)是无符号长整数,由 PKCS#11 库创建者选择将使用的确切值,因此可能很难猜测正确的值.我不是 Java 专家,但通过查看 Sun PKCS#11 Provider 的文档,我会说你应该使用 slotListIndex 变量,它指定 PKCS#11 库返回的可用插槽/令牌列表中的位置。

【讨论】:

  • 一旦我开始工作,我验证了slot=0slotListIndex=0 的工作方式相似。感谢您的意见。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-06-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-15
  • 1970-01-01
  • 2013-07-01
相关资源
最近更新 更多