12

I have initialized the SunPKCS11 provider by:

Provider provider = new sun.security.pkcs11.SunPKCS11("path_to_pkcs11.cfg");
Security.addProvider(provider);

And then I'm using this provider to initialize a KeyStore to use a key for cipher operations.

KeyStore ks = KeyStore.getInstance("PKCS11", provider);
ks.load(null, "password".toCharArray());

Once I'm done with the cipher operations, how should I finalize the session with the PKCS11 token?

I have tried removing the Provider, but it didn't work.

Security.removeProvider("sunPCKS11ProviderName");

The next time I try to communicate with the Token, I get this exception thrown from the token CKR_CRYPTOKI_ALREADY_INITIALIZED

UPDATE:

I have tried

sun.security.pkcs11.SunPKCS11.logout();

but it didn't work either.

I have a use case where I have to use both the PKCS#11 Wrapper and Provider. To be able to use the wrapper, I have to finalize the provider, or else the token throws CKR_CRYPTOKI_ALREADY_INITIALIZED error when the wrapper is trying to communicate with the token.

UPDATE WITH CODE:

I'm using Sun's PKCS#11 Provider and IAIK's PKCS#11 Wrapper.

public static void providerAndWrapperIssue() throws Exception
{
    final String name = "ANY_NAME";
    final String library = "LOCATION OF THE TOKENS DLL/SO";
    final String slot = "SLOT NUMBER";

    // SUN PKCS#11 Provider -------------------------------------------

    StringBuilder builder = new StringBuilder();
    builder.append("name=" + name);
    builder.append(System.getProperty("line.separator"));
    builder.append("library=\"" + library + "\"");
    builder.append(System.getProperty("line.separator"));
    builder.append("slot=" + slot);

    ByteArrayInputStream bais = new ByteArrayInputStream(builder.toString().getBytes());
    Provider provider = new sun.security.pkcs11.SunPKCS11(bais);
    Security.addProvider(provider);

    KeyStore ks = KeyStore.getInstance("PKCS11");
    ks.load(null, null);

    Enumeration<String> aliases = ks.aliases();
    while (aliases.hasMoreElements())
        System.out.println(aliases.nextElement());

    // IAIK PKCS#11 Wrapper -------------------------------------------

    Module pkcs11Module = Module.getInstance(library, false);
    pkcs11Module.initialize(null); <-- Exception here.

    Slot[] slots = pkcs11Module.getSlotList(true);

    Session session = slots[0].getToken().openSession(true, true, null, null);
    session.login(Session.UserType.USER, "".toCharArray());

    session.logout();
    session.closeSession();

    slots[0].getToken().closeAllSessions();

    pkcs11Module.finalize(null);
}

Since the Sun's provider is not logging out and closing sessions, IAIK is not able to access the token. And the Java's Keystore api doesn't have a method to logout.

always_a_rookie
  • 4,515
  • 1
  • 25
  • 46
  • Why would you initialize the token several times? Create a singleton within your project, which will work with the token, and initialize the provider on singleton creation. – Eugene Mayevski 'Callback Jun 17 '16 at 12:40
  • I am initializing the token (using provider) on a singleton. But I have a use case where I have to communicate with the token using PKCS#11 Wrapper. During this initialization the token is throwing already initialized exception. – always_a_rookie Jun 17 '16 at 13:18

3 Answers3

6

Finally was able to find a solution. The Sun's Provider uses the Wrapper underneath. So the trick is to use the Sun's PKCS#11 Wrapper to get the current instance, and finalize it. Obviously this finalizing of the session feature is not exposed in the Provider. But there is a workaround, and it looks like this:

public static void providerAndWrapperIssue() throws Exception
{
    final String name = "ANY_NAME";
    final String library = "LOCATION OF THE TOKENS DLL/SO";
    final String slot = "SLOT NUMBER";

    // SUN PKCS#11 Provider -------------------------------------------

    StringBuilder builder = new StringBuilder();
    builder.append("name=" + name);
    builder.append(System.getProperty("line.separator"));
    builder.append("library=\"" + library + "\"");
    builder.append(System.getProperty("line.separator"));
    builder.append("slot=" + slot);

    ByteArrayInputStream bais = new ByteArrayInputStream(builder.toString().getBytes());
    Provider provider = new sun.security.pkcs11.SunPKCS11(bais);
    provider.setProperty("pkcs11LibraryPath", library);
    Security.addProvider(provider);

    KeyStore ks = KeyStore.getInstance("PKCS11");
    ks.load(null, null);

    Enumeration<String> aliases = ks.aliases();
    while (aliases.hasMoreElements())
        System.out.println(aliases.nextElement());

    // ====================================
    // Solved it using the SUN PKCS#11 Wrapper

    PKCS11 pkcs11 = PKCS11.getInstance(((sun.security.pkcs11.SunPKCS11) provider).getProperty("pkcs11LibraryPath"), null, null, true);
    pkcs11.C_Finalize(PKCS11Constants.NULL_PTR);

    // ====================================

    // IAIK PKCS#11 Wrapper -------------------------------------------

    Module pkcs11Module = Module.getInstance(library, false);
    pkcs11Module.initialize(null);

    Slot[] slots = pkcs11Module.getSlotList(true);

    Session session = slots[0].getToken().openSession(true, true, null, null);
    session.login(Session.UserType.USER, "".toCharArray());

    session.logout();
    session.closeSession();

    slots[0].getToken().closeAllSessions();

    pkcs11Module.finalize(null);
}
always_a_rookie
  • 4,515
  • 1
  • 25
  • 46
3

Import class

import sun.security.pkcs11.SunPKCS11;
import sun.security.pkcs11.wrapper.PKCS11

Use this method to close PKCSS Wrapper

    private void finalizePKCS11Wrapper(PKCS11 pkcs11) throws IOException{
    try {
        Field f = PKCS11.class.getDeclaredField("moduleMap"); 
        f.setAccessible(true);
        Map moduleMap = (Map) f.get(pkcs11);
        moduleMap.clear();
        pkcs11.C_Finalize(PKCS11Constants.NULL_PTR);
    } catch (IllegalAccessException | IllegalArgumentException | NoSuchFieldException | SecurityException | PKCS11Exception e) {
        throw new IOException("No se pudo cerrar la sessión con el token",e);
    }
}

And close sunPKCS11 connection

    public void logout() throws Exception {
    if(sunPKCS11==null)
        return;
    try {
        // Obtenemos el Wrapper del sunPKCS11
        Field f = SunPKCS11.class.getDeclaredField("p11"); 
        f.setAccessible(true);
        PKCS11 objectPKCS11 = (PKCS11)f.get(sunPKCS11);
        finalizePKCS11Wrapper(objectPKCS11);
    } catch (Exception e) {
        e.printStackTrace();
    }
    sunPKCS11.clear();
    sunPKCS11.setCallbackHandler(null);
    Security.removeProvider(sunPKCS11.getName());
    sunPKCS11 = null;
    keyStore = null;
    System.gc();
}
Ronald Coarite
  • 4,460
  • 27
  • 31
0

Your are getting this exception because once the program get executed for the first time the values get retained with the process id in java.So to resolve this make it as a jar file and run as batch file

ARAVIND
  • 51
  • 1
  • 8
  • That's not what caused issue in my case, it was the finalizing operation with token that wasn't available through the java KeyStore/Provider api. And this is in my business logic, so I can't use it in a batch file. – always_a_rookie Nov 24 '16 at 13:32