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

摘要:当通过OpenSC在PKCS11上使用JCA时,在提取证书时会请求PIN。

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

到现在为止还挺好。

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

遵循代码的摘录:

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

结果如下:

 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 

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

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

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

谢谢,

解决了

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

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

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

我用3种不同类型的智能卡对它进行了测试,它正在对所有这些智能卡进行测试

嗯,我曾经做过的事情是这样的

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

pkcs.cfg是一个指向“libpteidpkcs11.so”库的文件,您应该能够将其调整到您的代码中。 我读到:

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

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

另一种方法是使用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); }