使用合格证书签署 PDF 文档 - 智能卡
Signing PDF document with qualified certificate - smartcard
我的小程序使用智能卡签署 PDF 文档时遇到问题。它适用于不合格的证书,但不适用于合格的证书。我正在使用 SunPKCS11 提供程序。这是 CryptoTech 卡。这是代码的一部分,我正在尝试在此提供程序上运行:
String pkcs11config = "name = " + PROVIDER + "\nlibrary = \"" + value + "\"";
byte[] pkcs11configBytes = pkcs11config.getBytes();
final ByteArrayInputStream configStream = new ByteArrayInputStream(pkcs11configBytes);
pkcs11Provider = new sun.security.pkcs11.SunPKCS11(configStream);
Security.addProvider(pkcs11Provider);
这是问题发生时的代码:
final KeyStore keyStore = KeyStore.getInstance(TYPE, pkcs11Provider);
keyStore.load(null, PIN);
常量:
public static final String PROVIDER = "CryptoTech";
private static final String TYPE = "PKCS11";
这是异常堆栈跟踪:
java.io.IOException: load failed
at sun.security.pkcs11.P11KeyStore.engineLoad(P11KeyStore.java:763)
at java.security.KeyStore.load(Unknown Source)
at pl.emsi.sign.card.CardManager.getKey(CardManager.java:165)
at pl.emsi.sign.logic.DocumentLogic.success(DocumentLogic.java:79)
at pl.emsi.sign.card.CardManager.driverSelected(CardManager.java:92)
at pl.emsi.sign.card.CardManager.driverSelected(CardManager.java:121)
at pl.emsi.sign.card.CardManager.actionPerformed(CardManager.java:414)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access0(Unknown Source)
at java.awt.EventQueue.run(Unknown Source)
at java.awt.EventQueue.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.run(Unknown Source)
at java.awt.EventQueue.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.WaitDispatchSupport.run(Unknown Source)
at java.awt.event.InvocationEvent.dispatch(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access0(Unknown Source)
at java.awt.EventQueue.run(Unknown Source)
at java.awt.EventQueue.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)
Caused by: javax.security.auth.login.LoginException
at sun.security.pkcs11.SunPKCS11.login(SunPKCS11.java:1238)
at sun.security.pkcs11.P11KeyStore.login(P11KeyStore.java:849)
at sun.security.pkcs11.P11KeyStore.engineLoad(P11KeyStore.java:753)
... 54 more
Caused by: sun.security.pkcs11.wrapper.PKCS11Exception: CKR_PIN_LOCKED
at sun.security.pkcs11.wrapper.PKCS11.C_Login(Native Method)
at sun.security.pkcs11.SunPKCS11.login(SunPKCS11.java:1222)
... 56 more
我已经检查过 PIN 没有被锁定,因为其他应用程序(无需提及此应用程序的名称)签署 PDF 文档没有任何问题。 PIN 码也 100% 正确。
如果缺少某些信息,请告诉我。
编辑1:
By: "It works fine for not qualified certificates but won't for qualified" 我的意思是不合格的证书放置在与该合格证书不同的智能卡上。
在 keyStore.load 方法中传递 null 代替 PIN 参数 (keyStore.load(null, null)) 将导致来自相应令牌驱动程序的密码提示。您可以通过这种方式验证 PIN...
已解决
好的,我找到了解决问题的方法。
事实证明,提供商试图默认使用 ID 为 0 的卡插槽。出现问题的卡在前三个插槽上有不合格的证书。这些插槽上的令牌未初始化。我尝试使用的合格证书位于第四个插槽。
我用 IAIK PKCS11 Wrapper
获取了这张卡的 Tokens
信息。这是代码示例:
try {
Module module = Module.getInstance(value);
module.initialize(null);
Slot[] slots = module.getSlotList(true);
TokenInfo[] infos = new TokenInfo[slots.length];
for (int i = 0; i < slots.length; i++) {
infos[i] = slots[i].getToken().getTokenInfo();
}
printTokenInfos(infos);
if (slots.length == 0) {
System.err.println("No token available!");
return;
}
} catch (TokenException | IOException e1) {
e1.printStackTrace();
}
[...]
private void printTokenInfos(TokenInfo[] infos) {
int counter = 0;
for (TokenInfo info : infos) {
System.out.println("Token: " + counter++);
System.out.println(info);
}
}
//"value" passed to Module's getInstance method is th path for .dll module
//used for one's type of card.
从这里我可以确定我应该使用哪个插槽。它可以通过将 slotListIndex
参数添加到 Provider 的配置输入流来完成。例如
String pkcs11config = "name = " + PROVIDER + "\nlibrary = \"" + value + "\"\nslotListIndex = " + slotIndex;
对这个问题有帮助的站点:
IAIK JCA/JCE
https://javaczysen.blogspot.com/ - 不幸的是,只有波兰语。
我的小程序使用智能卡签署 PDF 文档时遇到问题。它适用于不合格的证书,但不适用于合格的证书。我正在使用 SunPKCS11 提供程序。这是 CryptoTech 卡。这是代码的一部分,我正在尝试在此提供程序上运行:
String pkcs11config = "name = " + PROVIDER + "\nlibrary = \"" + value + "\"";
byte[] pkcs11configBytes = pkcs11config.getBytes();
final ByteArrayInputStream configStream = new ByteArrayInputStream(pkcs11configBytes);
pkcs11Provider = new sun.security.pkcs11.SunPKCS11(configStream);
Security.addProvider(pkcs11Provider);
这是问题发生时的代码:
final KeyStore keyStore = KeyStore.getInstance(TYPE, pkcs11Provider);
keyStore.load(null, PIN);
常量:
public static final String PROVIDER = "CryptoTech";
private static final String TYPE = "PKCS11";
这是异常堆栈跟踪:
java.io.IOException: load failed
at sun.security.pkcs11.P11KeyStore.engineLoad(P11KeyStore.java:763)
at java.security.KeyStore.load(Unknown Source)
at pl.emsi.sign.card.CardManager.getKey(CardManager.java:165)
at pl.emsi.sign.logic.DocumentLogic.success(DocumentLogic.java:79)
at pl.emsi.sign.card.CardManager.driverSelected(CardManager.java:92)
at pl.emsi.sign.card.CardManager.driverSelected(CardManager.java:121)
at pl.emsi.sign.card.CardManager.actionPerformed(CardManager.java:414)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access0(Unknown Source)
at java.awt.EventQueue.run(Unknown Source)
at java.awt.EventQueue.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.run(Unknown Source)
at java.awt.EventQueue.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.WaitDispatchSupport.run(Unknown Source)
at java.awt.event.InvocationEvent.dispatch(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access0(Unknown Source)
at java.awt.EventQueue.run(Unknown Source)
at java.awt.EventQueue.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)
Caused by: javax.security.auth.login.LoginException
at sun.security.pkcs11.SunPKCS11.login(SunPKCS11.java:1238)
at sun.security.pkcs11.P11KeyStore.login(P11KeyStore.java:849)
at sun.security.pkcs11.P11KeyStore.engineLoad(P11KeyStore.java:753)
... 54 more
Caused by: sun.security.pkcs11.wrapper.PKCS11Exception: CKR_PIN_LOCKED
at sun.security.pkcs11.wrapper.PKCS11.C_Login(Native Method)
at sun.security.pkcs11.SunPKCS11.login(SunPKCS11.java:1222)
... 56 more
我已经检查过 PIN 没有被锁定,因为其他应用程序(无需提及此应用程序的名称)签署 PDF 文档没有任何问题。 PIN 码也 100% 正确。
如果缺少某些信息,请告诉我。
编辑1:
By: "It works fine for not qualified certificates but won't for qualified" 我的意思是不合格的证书放置在与该合格证书不同的智能卡上。
在 keyStore.load 方法中传递 null 代替 PIN 参数 (keyStore.load(null, null)) 将导致来自相应令牌驱动程序的密码提示。您可以通过这种方式验证 PIN...
已解决
好的,我找到了解决问题的方法。
事实证明,提供商试图默认使用 ID 为 0 的卡插槽。出现问题的卡在前三个插槽上有不合格的证书。这些插槽上的令牌未初始化。我尝试使用的合格证书位于第四个插槽。
我用 IAIK PKCS11 Wrapper
获取了这张卡的 Tokens
信息。这是代码示例:
try {
Module module = Module.getInstance(value);
module.initialize(null);
Slot[] slots = module.getSlotList(true);
TokenInfo[] infos = new TokenInfo[slots.length];
for (int i = 0; i < slots.length; i++) {
infos[i] = slots[i].getToken().getTokenInfo();
}
printTokenInfos(infos);
if (slots.length == 0) {
System.err.println("No token available!");
return;
}
} catch (TokenException | IOException e1) {
e1.printStackTrace();
}
[...]
private void printTokenInfos(TokenInfo[] infos) {
int counter = 0;
for (TokenInfo info : infos) {
System.out.println("Token: " + counter++);
System.out.println(info);
}
}
//"value" passed to Module's getInstance method is th path for .dll module
//used for one's type of card.
从这里我可以确定我应该使用哪个插槽。它可以通过将 slotListIndex
参数添加到 Provider 的配置输入流来完成。例如
String pkcs11config = "name = " + PROVIDER + "\nlibrary = \"" + value + "\"\nslotListIndex = " + slotIndex;
对这个问题有帮助的站点:
IAIK JCA/JCE
https://javaczysen.blogspot.com/ - 不幸的是,只有波兰语。