Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PKCS#11 instantiation problems

I am trying to sign a pdf file using the smart card and PKCS#11. I link the right .dll and I am making a configuration file dynamically, but I am running into configuration trouble.

String config = "name=zz\n" +
                "library=" + DLL + "\n" +
                "slotListIndex = " + getSlotsWithTokens(DLL)[0];
ByteArrayInputStream pot = new ByteArrayInputStream(config.getBytes());
Provider providerPKCS11 = new SunPKCS11(pot);

and I get the following error:

Exception in thread "main" java.security.ProviderException: Initialization failed
    at sun.security.pkcs11.SunPKCS11.<init>(SunPKCS11.java:376)
    at sun.security.pkcs11.SunPKCS11.<init>(SunPKCS11.java:107)
    at smartCardPKCS11.scPKCS11.main(scPKCS11.java:56)
Caused by: java.security.ProviderException: slotListIndex is 52481 but token only has 10 slots
    at sun.security.pkcs11.SunPKCS11.<init>(SunPKCS11.java:357)
    ... 2 more

bit confused on the whole slots thing. Can someone help me out?

This how my getSlotsWithTokens looks:

public static long[] getSlotsWithTokens(String libraryPath) throws IOException{
        CK_C_INITIALIZE_ARGS initArgs = new CK_C_INITIALIZE_ARGS();
        String functionList = "C_GetFunctionList";

        initArgs.flags = 0;
        PKCS11 tmpPKCS11 = null;
        long[] slotList = null;
        try {
            try {
                tmpPKCS11 = PKCS11.getInstance(libraryPath, functionList, initArgs, false);
            } catch (IOException ex) {
                ex.printStackTrace();
                throw ex;
            }
        } catch (PKCS11Exception e) {
            try {
                initArgs = null;
                tmpPKCS11 = PKCS11.getInstance(libraryPath, functionList, initArgs, true);
            } catch (IOException ex) {
               ex.printStackTrace();
            } catch (PKCS11Exception ex) {
               ex.printStackTrace();
            }
        }

        try {
            slotList = tmpPKCS11.C_GetSlotList(true);

            for (long slot : slotList){
                CK_TOKEN_INFO tokenInfo = tmpPKCS11.C_GetTokenInfo(slot);
                System.out.println("slot: "+slot+"\nmanufacturerID: "
                        + String.valueOf(tokenInfo.manufacturerID) + "\nmodel: "
                        + String.valueOf(tokenInfo.model));
            }
        } catch (PKCS11Exception ex) {
                ex.printStackTrace();
        } catch (Throwable t) {
            t.printStackTrace();
        }

        return slotList;

    }

UPDATED version:

So I made the changes as @albciff suggested: here is the complete code:

import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.Security;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import sun.security.pkcs11.SunPKCS11;
import sun.security.pkcs11.wrapper.CK_C_INITIALIZE_ARGS;
import sun.security.pkcs11.wrapper.CK_TOKEN_INFO;
import sun.security.pkcs11.wrapper.PKCS11;
import sun.security.pkcs11.wrapper.PKCS11Exception;

import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.log.LoggerFactory;
import com.itextpdf.text.log.SysoLogger;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfSignatureAppearance;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.security.BouncyCastleDigest;
import com.itextpdf.text.pdf.security.CrlClient;
import com.itextpdf.text.pdf.security.CrlClientOnline;
import com.itextpdf.text.pdf.security.DigestAlgorithms;
import com.itextpdf.text.pdf.security.ExternalDigest;
import com.itextpdf.text.pdf.security.ExternalSignature;
import com.itextpdf.text.pdf.security.MakeSignature;
import com.itextpdf.text.pdf.security.PrivateKeySignature;
import com.itextpdf.text.pdf.security.TSAClient;
import com.itextpdf.text.pdf.security.MakeSignature.CryptoStandard;
import com.itextpdf.text.pdf.security.OcspClient;
import com.itextpdf.text.pdf.security.OcspClientBouncyCastle;

public class sPKCS11  {
    public static final String SRC = "src/Test.pdf";
    public static final String DEST = "src/scTest.pdf";
    public static final String DLL = "c:/windows/system32/aetpkss1.dll";

    public static void main(String[] args) throws IOException, GeneralSecurityException, DocumentException {
        LoggerFactory.getInstance().setLogger(new SysoLogger());
        String pkcs11ConfigSettings = "name=aet\n"+"library="+DLL;
        byte[] pkcs11ConfigBytes = pkcs11ConfigSettings.getBytes();
        ByteArrayInputStream confStream = new ByteArrayInputStream(pkcs11ConfigBytes);

        SunPKCS11 pkcs11 = new SunPKCS11(confStream);
        Security.addProvider(pkcs11);

        BouncyCastleProvider providerBC = new BouncyCastleProvider();
        Security.addProvider(providerBC);

        KeyStore ks = KeyStore.getInstance("PKCS11");
        ks.load(null, null);
        Enumeration<String> aliases = ks.aliases();
        while (aliases.hasMoreElements()) {
            System.out.println(aliases.nextElement());
        }
        // alias is here just for the sake of keeping things private
        smartcardsign(pkcs11.getName(), ks, "alias");
    }

    public static void smartcardsign(String provider, KeyStore ks, String alias) throws GeneralSecurityException, IOException, DocumentException {
        PrivateKey pk = (PrivateKey)ks.getKey(alias, null);
        Certificate[] chain = ks.getCertificateChain(alias);
        OcspClient ocspClient = new OcspClientBouncyCastle();
        List<CrlClient> crlList = new ArrayList<CrlClient>();
        crlList.add(new CrlClientOnline(chain));
        scPKCS11 app = new scPKCS11();
        app.sign(SRC, String.format(DEST, alias), chain, pk, DigestAlgorithms.SHA256, provider, CryptoStandard.CMS,
                "Test", "B", crlList, ocspClient, null, 0);
    }

    public void sign(String src, String dest,
            Certificate[] chain, PrivateKey pk,
            String digestAlgorithm, String provider, CryptoStandard subfilter,
            String reason, String location,
            Collection<CrlClient> crlList,
            OcspClient ocspClient,
            TSAClient tsaClient,
            int estimatedSize)
                    throws GeneralSecurityException, IOException, DocumentException {
        // Creating the reader and the stamper
        PdfReader reader = new PdfReader(src);
        FileOutputStream os = new FileOutputStream(dest);
        PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0', null, true);
        // Creating the appearance
        PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
        appearance.setReason(reason);
        appearance.setLocation(location);
        appearance.setVisibleSignature(new Rectangle(36, 748, 144, 780), 1, "sig");
        appearance.setCertificationLevel(PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED);
        // Creating the signature
        ExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider);
        ExternalDigest digest = new BouncyCastleDigest();
        MakeSignature.signDetached(appearance, digest, pks, chain, crlList, ocspClient, tsaClient, estimatedSize, subfilter);
    }
}

And this is the new error msg:

Exception in thread "main" java.security.KeyStoreException: PKCS11 not found
    at java.security.KeyStore.getInstance(Unknown Source)
    at smartCardPKCS11.sPKCS11.main(sPKCS11.java:65)
Caused by: java.security.NoSuchAlgorithmException: PKCS11 KeyStore not available
    at sun.security.jca.GetInstance.getInstance(Unknown Source)
    at java.security.Security.getImpl(Unknown Source)
    ... 2 more

I am aware that it is something really stupid, help is welcomed.

like image 230
caniaskyouaquestion Avatar asked Aug 14 '14 10:08

caniaskyouaquestion


People also ask

What is PKCS for?

The Public-Key Cryptography Standards (PKCS) comprise a group of cryptographic standards that provide guidelines and application programming interfaces (APIs) for the usage of cryptographic methods. As the name PKCS suggests, these standards put an emphasis on the usage of public key (that is, asymmetric) cryptography.

What is PKCS format?

PKCS#12 (also known as PKCS12 or PFX) is a binary format for storing a certificate chain and private key in a single, encryptable file. PKCS#12 files are commonly used to import and export certificates and private keys on Windows and macOS computers, and usually have the filename extensions . p12 or .

Is PKCS12 safe?

PKCS12 (aka PFX) files, on the other hand, are language-neutral and is more secure and has been around long enough that it's supported just about everywhere.

What is pkcs11 driver?

Proxy Server supports Public Key Cryptography Standard (PKCS) #11, which defines the interface used for communication between SSL and PKCS #11 modules. PKCS #11 modules are used for standards-based connectivity to SSL hardware accelerators.


2 Answers

I had the same problem.

Try passing -Djava.security.debug=sunpkcs11 to jvm. I did this and it worked.

If you are using jarsigner or keytool pass -J-Djava.security.debug=sunpkcs11 instead.

See OpenJDK bug. This issue is solved in OpenJDK, but maybe it is still unresolved in Oracle JDK.

like image 125
Rui Baeta Avatar answered Sep 30 '22 20:09

Rui Baeta


Inform the slotListIndex in the config it's optional (however the method getSlotsWithTokens() is not returning the value you expect). You can see the slotListIndex parameter description in PKCS11 Reference:

This is the slot index that this provider instance is to be associated with. It is the index into the list of all slots returned by the PKCS#11 function C_GetSlotList. For example, 0 indicates the first slot in the list. At most one of slot or slotListIndex may be specified. If neither is specified, the default is a slotListIndex of 0.

So config only name and library parameter to configure your PKCS11 provider to avoid your exception:

// Configure the Sun PKCS#11 provider. It requires a stream 
// containing the configuration parameters - "name" and "library".
String pkcs11ConfigSettings = "name = " + pkcs11ID + "\n" + "library = " + libraryPath;
byte[] pkcs11ConfigBytes = pkcs11ConfigSettings.getBytes();
ByteArrayInputStream confStream = new ByteArrayInputStream(pkcs11ConfigBytes);

// instantiate the provider
SunPKCS11 pkcs11 = new SunPKCS11(confStream);
Security.addProvider(pkcs11);   
...

EDIT:

In your code now you're loading the provider correctly so the java.security.KeyStoreException: PKCS11 not found is thrown in the follow invocation KeyStore ks = KeyStore.getInstance("PKCS11"); because your smartcard is not plugged or if it's plugged maybe there is some problem with the provider (both problems thrown the same exception), so try to pass the provider explicitly to the instance using getInstance(String,Provider):

Use:

SunPKCS11 pkcs11 = new SunPKCS11(confStream);
KeyStore ks = KeyStore.getInstance("PKCS11", pkcs11);

Instead of:

KeyStore ks = KeyStore.getInstance("PKCS11");

Furthermore there is an error in your code: you have to provide the PKCS11 password not null when you try to load the keystore.

So use:

ks.load(null, "yourPassword".toCharArray());

Instead of:

ks.load(null, null);

Note alternatively its very common to use a password callback handler to access PKCS11 keystores.

Putting all together, the code could be:

// Configure the Sun PKCS#11 provider. It requires a stream 
// containing the configuration parameters - "name" and "library".
String pkcs11ConfigSettings = "name = " + pkcs11ID + "\n" + "library = " + libraryPath;
byte[] pkcs11ConfigBytes = pkcs11ConfigSettings.getBytes();
ByteArrayInputStream confStream = new ByteArrayInputStream(pkcs11ConfigBytes);

// instantiate the provider
SunPKCS11 pkcs11 = new SunPKCS11(confStream);
Security.addProvider(pkcs11);   
KeyStore ks = KeyStore.getInstance("PKCS11", pkcs11);
ks.load(null, "yourPassword".toCharArray());
like image 30
albciff Avatar answered Sep 30 '22 21:09

albciff