I have a problem with my applet for signing PDF documents using smartcard. It works fine for not qualified certificates but won't for qualified. I'm using SunPKCS11 provider. It's CryptoTech card. Here's part of code, where i'm trying to operate on this provider:
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);
And here is code, when the problem occured:
final KeyStore keyStore = KeyStore.getInstance(TYPE, pkcs11Provider);
keyStore.load(null, PIN);
And the constants:
public static final String PROVIDER = "CryptoTech";
private static final String TYPE = "PKCS11";
Here's exception stacktrace:
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$1.success(DocumentLogic.java:79)
at pl.emsi.sign.card.CardManager$1.driverSelected(CardManager.java:92)
at pl.emsi.sign.card.CardManager$2.driverSelected(CardManager.java:121)
at pl.emsi.sign.card.CardManager$7.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.access$500(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.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$4.run(Unknown Source)
at java.awt.EventQueue$4.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$2.run(Unknown Source)
at java.awt.event.InvocationEvent.dispatch(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$500(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.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
I've already checked that the PIN isn't locked, because other application (no need to mention the name of this application) signs the PDF document without any problems. The PIN is 100% correct, also.
If there's some information missing, please, let me know.
EDIT1:
By: "It works fine for not qualified certificates but won't for qualified" i meant that the not qualified certificates was placed on different smartcards than this qualified certificate.
SOLVED
Okay, I found a solution for my problem.
It turned out that the provider was trying to use card's slot with id 0 by default. Card, at which there was problem, have non qualified certificates on three first slots. Tokens on these slots isn't initialized. The qualified cert I was trying to use is placed on fourth slot.
I used IAIK PKCS11 Wrapper
to get info about Tokens
of this card. Here's code sample:
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.
From this place I can determine which slot I should use. It can be accomplished by adding slotListIndex
parameter to Provider's configuration input stream. E.g.
String pkcs11config = "name = " + PROVIDER + "\nlibrary = \"" + value + "\"\nslotListIndex = " + slotIndex;
Helpful sites for this issue:
IAIK JCA/JCE
https://javaczysen.blogspot.com/ - unfortunately, only in polish.