SunPKCS11 Provider初始化后如何完成?
How to finalize SunPKCS11 Provider after it is initialized?
我已经通过以下方式初始化了 SunPKCS11 提供程序:
Provider provider = new sun.security.pkcs11.SunPKCS11("path_to_pkcs11.cfg");
Security.addProvider(provider);
然后我使用此提供程序初始化 KeyStore 以使用密钥进行密码操作。
KeyStore ks = KeyStore.getInstance("PKCS11", provider);
ks.load(null, "password".toCharArray());
完成密码操作后,我应该如何使用 PKCS11 令牌完成会话?
我试过删除 Provider,但没用。
Security.removeProvider("sunPCKS11ProviderName");
下次我尝试与令牌通信时,我从令牌中抛出了这个异常 CKR_CRYPTOKI_ALREADY_INITIALIZED
更新:
我试过了
sun.security.pkcs11.SunPKCS11.logout();
但是也没用。
我有一个用例,我必须同时使用 PKCS#11 Wrapper 和 Provider。为了能够使用包装器,我必须最终确定提供者,否则当包装器尝试与令牌通信时令牌会抛出 CKR_CRYPTOKI_ALREADY_INITIALIZED
错误。
更新代码:
我正在使用 Sun 的 PKCS#11 Provider 和 IAIK 的 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);
}
由于 Sun 的提供商没有注销和关闭会话,IAIK 无法访问令牌。 Java 的 Keystore
api 没有注销方法。
终于找到解决办法了。 Sun 的 Provider 在下面使用 Wrapper。所以诀窍是使用 Sun 的 PKCS#11 Wrapper 来获取当前实例,并最终确定它。显然,会话功能的最终确定不会在 Provider 中公开。但是有一个解决方法,它看起来像这样:
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);
}
你会得到这个异常,因为一旦程序第一次执行,值就会与进程 ID 一起保留在 java.So 中以解决这个问题,将其作为 jar 文件并 运行 作为批处理文件
导入class
import sun.security.pkcs11.SunPKCS11;
import sun.security.pkcs11.wrapper.PKCS11
使用此方法关闭 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);
}
}
并关闭sunPKCS11连接
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();
}
我已经通过以下方式初始化了 SunPKCS11 提供程序:
Provider provider = new sun.security.pkcs11.SunPKCS11("path_to_pkcs11.cfg");
Security.addProvider(provider);
然后我使用此提供程序初始化 KeyStore 以使用密钥进行密码操作。
KeyStore ks = KeyStore.getInstance("PKCS11", provider);
ks.load(null, "password".toCharArray());
完成密码操作后,我应该如何使用 PKCS11 令牌完成会话?
我试过删除 Provider,但没用。
Security.removeProvider("sunPCKS11ProviderName");
下次我尝试与令牌通信时,我从令牌中抛出了这个异常 CKR_CRYPTOKI_ALREADY_INITIALIZED
更新:
我试过了
sun.security.pkcs11.SunPKCS11.logout();
但是也没用。
我有一个用例,我必须同时使用 PKCS#11 Wrapper 和 Provider。为了能够使用包装器,我必须最终确定提供者,否则当包装器尝试与令牌通信时令牌会抛出 CKR_CRYPTOKI_ALREADY_INITIALIZED
错误。
更新代码:
我正在使用 Sun 的 PKCS#11 Provider 和 IAIK 的 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);
}
由于 Sun 的提供商没有注销和关闭会话,IAIK 无法访问令牌。 Java 的 Keystore
api 没有注销方法。
终于找到解决办法了。 Sun 的 Provider 在下面使用 Wrapper。所以诀窍是使用 Sun 的 PKCS#11 Wrapper 来获取当前实例,并最终确定它。显然,会话功能的最终确定不会在 Provider 中公开。但是有一个解决方法,它看起来像这样:
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);
}
你会得到这个异常,因为一旦程序第一次执行,值就会与进程 ID 一起保留在 java.So 中以解决这个问题,将其作为 jar 文件并 运行 作为批处理文件
导入class
import sun.security.pkcs11.SunPKCS11;
import sun.security.pkcs11.wrapper.PKCS11
使用此方法关闭 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);
}
}
并关闭sunPKCS11连接
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();
}