Java 从没有 PIN/密码的 PKCS11 智能卡获取证书

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21167927/
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-08-13 07:04:50  来源:igfitidea点击:

Getting certificates from PKCS11 Smartcard without PIN/password

javapkcs#11

提问by Javier Sedano

Abstract: when using JCA over PKCS11 over OpenSC, the PIN is requested when extracting certificates.

摘要:当使用 JCA over PKCS11 over OpenSC 时,在提取证书时需要 PIN。

I have got an application that needs to sign using a smartcard. The smartcard is supported by OpenSC, so I am using the Java-built-in pkcs11 wrapper provider to use it. For functional reasons, I need to obtain the certificates in the card without a PIN requested. If the user finally signs, then, of course, the PIN is needed.

我有一个需要使用智能卡签名的应用程序。OpenSC 支持智能卡,因此我使用 Java 内置的 pkcs11 包装器提供程序来使用它。出于功能原因,我需要在没有请求 PIN 的情况下获取卡中的证书。如果用户最终签名,那么当然需要 PIN。

I see I can do it from command line without providing a PIN:

我看到我可以从命令行执行此操作而无需提供 PIN:

pkcs11-tool --module C:\WINDOWS\system32\opensc-pkcs11.dll -r -a 50-MDS_Signature -y cert -o p.cer
Using slot 1 with a present token (0x1)

So far, so good.

到现在为止还挺好。

The documentation from Oracle clearly says "The builder will prompt for a password as needed using the previously configured callback handler" (http://docs.oracle.com/javase/6/docs/technotes/guides/security/p11guide.html#Login). However, my code does always request the pin as son as I call KeyStore ks0 = ksbuilder0.getKeyStore();even while only extracting public info (such as certificates).

Oracle 的文档明确指出“构建器将根据需要使用先前配置的回调处理程序提示输入密码”(http://docs.oracle.com/javase/6/docs/technotes/guides/security/p11guide.html#登录)。但是,KeyStore ks0 = ksbuilder0.getKeyStore();即使仅提取公共信息(例如证书),我的代码也总是在我调用时请求 pin 作为儿子。

Follows an extract of the code:

以下是代码的摘录:

private static final String PKCS11_LIB = "C:\WINDOWS\system32\opensc-pkcs11.dll";
private static final String NAME = "OpenSCpkcs11";
private static final String SLOT = "1";
private static final String PIN = "11111111";
private static final String ALIAS = "myCert";

[...]

private static CallbackHandler myCallbackHandler = new CallbackHandler() {
    @Override
    public void handle(Callback[] callbacks) throws IOException,
            UnsupportedCallbackException {
        for (Callback callback : callbacks) {
            if (callback instanceof PasswordCallback) {
                PasswordCallback passwordCallback = (PasswordCallback) callback;
                System.out.println(passwordCallback.getPrompt() + PIN);
                passwordCallback.setPassword(PIN.toCharArray());
            }
        }
    }
};

[...]

String configString = "name = "
  + NAME.replace(' ', '_')
  + "\n"
  + "library = "
  + PKCS11_LIB
  + "\n slot = "
  + SLOT
  + " "
  + "\n attributes = compatibility \n"
  + "attributes(*,*,*)=\n{\nCKA_TOKEN=true\nCKA_LOCAL=true\n}";
ByteArrayInputStream configStream = new ByteArrayInputStream(
    configString.getBytes());
SunPKCS11 pkcs11Provider0 = new SunPKCS11(configStream);
pkcs11Provider0.login(null, myCallbackHandler);
Security.addProvider(pkcs11Provider0);
KeyStore.CallbackHandlerProtection chp = new KeyStore.CallbackHandlerProtection(
    myCallbackHandler);
KeyStore.Builder ksbuilder0 = KeyStore.Builder.newInstance(
    "PKCS11", pkcs11Provider0, chp);
KeyStore ks0 = ksbuilder0.getKeyStore();
X509Certificate cert0 = (X509Certificate) ks0.getCertificate(ALIAS);
// System.out.println("Cert " + cert0.toString());
Principal p = cert0.getSubjectDN();
System.out.println("I am: " + cert0.getSubjectDN().getName());

It results on:

结果是:

Contrase?a de la tarjeta de claves PKCS11 [SunPKCS11-OpenSCpkcs11]: 11111111
2014-01-16 17:48:11.275 cannot lock memory, sensitive data may be paged to disk
I am: CN=pepe perez, SURNAME=pepe, L=qwerty

As you can see, the password is requested before the certificate is got. By means of debugging I can see that the password is requested in the line KeyStore ks0 = ksbuilder0.getKeyStore();

如您所见,在获得证书之前要求输入密码。通过调试可以看到在行中要求输入密码KeyStore ks0 = ksbuilder0.getKeyStore();

Any idea? Is there no way to configure it as I want? Any further idea or test?

任何的想法?有没有办法按照我的意愿配置它?任何进一步的想法或测试?

Furthermore: do you know of any other way to access smartcards, for example directly through a JAVA2OpenSC wrapper or the like?

此外:您是否知道访问智能卡的任何其他方式,例如直接通过 JAVA2OpenSC 包装器等?

Thanks,

谢谢,

采纳答案by Javier Sedano

Finally, there was no solution using JCA. The final solution was to directly attack the PKCS11 driver. I have used Hymannji11 (https://github.com/joelhockey/Hymannji11) and the PKCS11 spec (http://www.emc.com/emc-plus/rsa-labs/standards-initiatives/pkcs-11-cryptographic-token-interface-standard.htm).

最后,没有使用 JCA 的解决方案。最终的解决方案是直接攻击PKCS11驱动程序。我使用过 Hymannji11 ( https://github.com/joelhockey/Hymannji11) 和 PKCS11 规范 ( http://www.emc.com/emc-plus/rsa-labs/standards-initiatives/pkcs-11-cryptographic-令牌接口标准.htm)。

回答by BlueMoon93

Well, what i've once done was something like

嗯,我曾经做过的事情就像

    Provider prov = new sun.security.pkcs11.SunPKCS11("pkcs.cfg");
    Security.addProvider(prov);
    KeyStore cc = null;
    try {
        cc = KeyStore.getInstance("PKCS11");
        cc.load(null, null);
        cc.getCertificate("CITIZEN AUTHENTICATION CERTIFICATE")
    } catch (Exception ex) {
        ex.printStackTrace();
    }

The pkcs.cfg is a file pointing to the "libpteidpkcs11.so" library, and you should be able to adapt it to your code. Mine reads:

pkcs.cfg 是一个指向“libpteidpkcs11.so”库的文件,您应该能够使其适应您的代码。我的写着:

name = SmartCard
library = /usr/local/lib/libpteidpkcs11.so

回答by Plamen Vasilev

SOLVED

解决了

I have found a way to get the public certificate from the smart card.

我找到了一种从智能卡获取公共证书的方法。

    String pkcs11Config = "name = SmartCard\nlibrary = /path/to/libraby.so";
    ByteArrayInputStream confStream = new ByteArrayInputStream(pkcs11Config.getBytes());
    Provider prov = new sun.security.pkcs11.SunPKCS11(confStream);
    Security.addProvider(prov);
    KeyStore cc = null;
    String pin = "";
    try {
        cc = KeyStore.getInstance("PKCS11",prov);
        KeyStore.PasswordProtection pp = new KeyStore.PasswordProtection(pin.toCharArray());
        cc.load(null ,  pp.getPassword() );
        Enumeration aliases = cc.aliases();
        while (aliases.hasMoreElements()) {
            Object alias = aliases.nextElement();
            try {
                X509Certificate cert0 = (X509Certificate) cc.getCertificate(alias.toString());
                System.out.println("I am: " + cert0.getSubjectDN().getName());
            } catch (Exception e) {
                continue;
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }

The KeyStore.load() should be provided with PaswordProtection object with empty pin. This allows me to read the public certificate and extract the data from it.

KeyStore.load() 应该与带有空引脚的 PaswordProtection 对象一起提供。这允许我读取公共证书并从中提取数据。

I have tested this with 3 different types of smart cards and it is working on all of them

我已经用 3 种不同类型的智能卡对此进行了测试,并且对所有类型的智能卡都有效

回答by marioosh

Another way is use IAIK PKCS#11 Wrapper. JavaDoc here. Example code below.

另一种方法是使用IAIK PKCS#11 Wrapper。JavaDoc在这里。下面的示例代码。

/**
 * list certificates
 * 
 * @param module - PKCS#11 (.dll or .so) module path
 * for example: "C:\Program Files (x86)\ENCARD\enigmap11.dll"
 * 
 * @throws Exception
 */
public void listCertificates(String module) throws Exception {
    Module pkcs11Module = Module.getInstance(module);
    pkcs11Module.initialize(null);

    Slot[] slotsWithToken = pkcs11Module.getSlotList(Module.SlotRequirement.TOKEN_PRESENT);
    CertificateFactory certFactory = CertificateFactory.getInstance("X.509");

    for(Slot s: slotsWithToken) {
        Session session = s.getToken().openSession(Token.SessionType.SERIAL_SESSION, Token.SessionReadWriteBehavior.RO_SESSION, null, null);
        session.findObjectsInit(new X509PublicKeyCertificate());
        Object[] objects = null;
        while((objects = session.findObjects(1)).length > 0) {
            for(Object c: objects) {
                X509PublicKeyCertificate cert = (X509PublicKeyCertificate) c;
                byte[] certValue = cert.getValue().getByteArrayValue();
                Certificate cc = certFactory.generateCertificate(new ByteArrayInputStream(certValue));
                if(cc instanceof X509Certificate) {
                    X509Certificate x509 = (X509Certificate) cc;
                    log.info(x509.getNotBefore() + " - " + x509.getNotAfter());
                }                       
            }
        }
        session.findObjectsFinal();
    }           
    pkcs11Module.finalize(null);
}