Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting certificates from PKCS11 Smartcard without PIN/password

Tags:

java

pkcs#11

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

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.

I see I can do it from command line without providing a 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).

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

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?

Thanks,

like image 315
Javier Sedano Avatar asked Jan 16 '14 16:01

Javier Sedano


1 Answers

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.

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

like image 169
Plamen Vasilev Avatar answered Oct 02 '22 23:10

Plamen Vasilev