摘要:当通过OpenSC在PKCS11上使用JCA时,在提取证书时会请求PIN。
我有一个需要使用智能卡签名的应用程序。 OpenSC支持智能卡,因此我使用Java内置的pkcs11包装提供程序来使用它。出于功能原因,我需要在没有请求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#Login)。但是,即使只提取公共信息(例如证书),我的代码总是在我调用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();
有什么想法吗?有没有办法按我的意愿配置它?任何进一步的想法或测试?
此外:你知道其他任何访问智能卡的方法,例如直接通过JAVA2OpenSC包装器等吗?
谢谢,
答案 0 :(得分:7)
<强>解决强>
我找到了从智能卡获取公共证书的方法。
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种不同类型的智能卡对其进行了测试,并且正在对所有智能卡进行测试
答案 1 :(得分:0)
嗯,我曾经做过的事情就像是
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
答案 2 :(得分:0)
最后,没有使用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)。
答案 3 :(得分:0)
另一种方法是使用IAIK PKCS#11 Wrapper。 JavaDoc here。下面的示例代码。
/**
* 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);
}