java PKCS#11 实例化问题

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25306191/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 07:49:11  来源:igfitidea点击:

PKCS#11 instantiation problems

javacryptographydigital-signaturepkcs#11

提问by caniaskyouaquestion

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.

我正在尝试使用智能卡和 PKCS#11 对 pdf 文件进行签名。我链接了正确的 .dll 并且我正在动态制作一个配置文件,但我遇到了配置问题。

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:

这是我的 getSlotsWithTokens 的样子:

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:

所以我按照@albciff 的建议进行了更改:这是完整的代码:

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, '
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
', 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:

这是新的错误消息:

// 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);   
...

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

我知道这是非常愚蠢的事情,欢迎提供帮助。

回答by Rui Baeta

I had the same problem.

我有同样的问题。

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

尝试传递-Djava.security.debug=sunpkcs11给 jvm。我这样做了,并且奏效了。

If you are using jarsigneror keytoolpass -J-Djava.security.debug=sunpkcs11instead.

如果您正在使用jarsignerkeytool通过-J-Djava.security.debug=sunpkcs11

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

请参阅OpenJDK 错误。这个问题在 OpenJDK 中已解决,但在 Oracle JDK 中可能仍未解决。

回答by albciff

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

slotListIndex在配置中通知它是可选的(但是该方法getSlotsWithTokens()没有返回您期望的值)。您可以在 中看到slotListIndex参数说明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.

这是此提供程序实例要关联的插槽索引。它是 PKCS#11 函数 C_GetSlotList 返回的所有插槽列表的索引。例如,0 表示列表中的第一个槽。最多可以指定 slot 或 slotListIndex 之一。如果两者均未指定,则默认值为 0 的 slotListIndex。

So config only nameand libraryparameter to configure your PKCS11provider to avoid your exception:

所以只配置namelibrary参数来配置你的PKCS11提供者以避免你的异常:

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

EDIT:

编辑:

In your code now you're loading the provider correctly so the java.security.KeyStoreException: PKCS11 not foundis 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):

在您的代码中,您现在正确加载了提供程序,因此java.security.KeyStoreException: PKCS11 not found在后续调用中抛出了 ,KeyStore ks = KeyStore.getInstance("PKCS11");因为您的智能卡未插入,或者如果插入,则提供程序可能存在一些问题(两个问题都引发了相同的异常),因此请尝试通过提供者显式地使用getInstance(String,Provider)

Use:

利用:

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

Instead of:

代替:

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

Furthermore there is an error in your code: you have to provide the PKCS11password not nullwhen you try to load the keystore.

此外,您的代码中存在错误:您必须在尝试加载密钥库时提供PKCS11密码而不是null

So use:

所以使用:

ks.load(null, null);

Instead of:

代替:

// 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());

Notealternatively its very common to use a password callback handler to access PKCS11keystores.

请注意,使用密码回调处理程序访问PKCS11密钥库是非常常见的。

Putting all together, the code could be:

放在一起,代码可能是:

##代码##