简体   繁体   中英

Signing PDF document with qualified certificate - smartcard


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.

Pass null in place of PIN parameter in keyStore.load method(keyStore.load(null, null)) will result in password prompt from respective token driver. You can validate the PIN this way...

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. Eg

    String pkcs11config = "name = " + PROVIDER + "\nlibrary = \"" + value + "\"\nslotListIndex = " + slotIndex;

Helpful sites for this issue:
IAIK JCA/JCE
https://javaczysen.blogspot.com/ - unfortunately, only in polish.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM