always_a_rookie_to_learn always_a_rookie_to_learn - 20 days ago 12
Java Question

How to finalize SunPKCS11 Provider after it is initialized?

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.

Answer

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