Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/magento/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java中如何从usb令牌中获取密钥库_Java_Ssl_Https_Keystore_Mscapi - Fatal编程技术网

Java中如何从usb令牌中获取密钥库

Java中如何从usb令牌中获取密钥库,java,ssl,https,keystore,mscapi,Java,Ssl,Https,Keystore,Mscapi,我有一个SafeNet 5100 eToken,里面已经有一个有效的证书,我用它来访问需要它的公司的web应用程序(多因素身份验证) 我正在创建一个桌面应用程序来访问此网站。我已经能够将网站的证书添加到信任库,并将我的证书放入密钥库 到目前为止,我得到的是: System.setProperty("javax.net.ssl.trustStore", "U:\\Certificados\\efau.truestore"); System.setProperty("javax.net.ssl.tr

我有一个SafeNet 5100 eToken,里面已经有一个有效的证书,我用它来访问需要它的公司的web应用程序(多因素身份验证)

我正在创建一个桌面应用程序来访问此网站。我已经能够将网站的证书添加到
信任库
,并将我的证书放入
密钥库

到目前为止,我得到的是:

System.setProperty("javax.net.ssl.trustStore", "U:\\Certificados\\efau.truestore");
System.setProperty("javax.net.ssl.trustStoreType", "jks");
System.setProperty("javax.net.ssl.trustStorePassword", "oiadad");

KeyManagerFactory kFac;
SSLContext sslContext;
SSLSocketFactory sockFactory = null;
KeyStore ks;

try {
    // load keystore present in windows and print aliases found (only one, so nextElement always prints same information (name of certificate inside usb token I want to open))
    ks = KeyStore.getInstance("Windows-MY", "SunMSCAPI");
    ks.load(null, null);
    System.out.println(ks.aliases().nextElement());
    System.out.println(ks.aliases().nextElement());

    // try to load my certificate specifically from all certificates and passes necessary token password to it
    InputStream in = IOUtils.toInputStream(ks.aliases().nextElement(), "UTF-8");
    System.out.println(in);
    ks.load(in, password);

    // print certificate to check if I have it
    System.out.println(ks.getCertificate(ks.aliases().nextElement()));

    // get ssl context and key manager factory
    sslContext = SSLContext.getInstance("SSL", "SunJSSE");
    kFac = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    kFac.init(ks,null);

    sslContext.init(kFac.getKeyManagers(), null, null);
    sockFactory = sslContext.getSocketFactory();

    // start connection with website
    HttpsURLConnection conn = (HttpsURLConnection)new URL(<my-https-url>).openConnection();
    conn.setRequestMethod("GET");
    conn.setDoInput(true);
    conn.setSSLSocketFactory(sockFactory);

    int responseCode = conn.getResponseCode();
    System.out.println("RESPONSE: " + responseCode);

} catch (KeyStoreException e) {
    e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
    e.printStackTrace();
} catch (CertificateException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
} catch (NoSuchProviderException e) {
    e.printStackTrace();
} catch (UnrecoverableKeyException e1) {
    e1.printStackTrace();
} catch (KeyManagementException e1) {
    e1.printStackTrace();
}
当我为令牌输入了正确的密码,当我输入了错误的密码时,我会出现这个错误,所以我认为我从来没有以正确的方式传递密码

为什么我会收到异常

---------更新---------

我创建了一个配置文件,其中包含指向PKCS11.dll库的以下信息:

name = Aladdin
library = C:/WINDOWS/system32/eTPKCS11.dll
在主要功能中,我添加了:

SunPKCS11 newProvider = new SunPKCS11("u:/Certificados/etpkcs11.cfg");
Provider a = newProvider;
Security.addProvider(a);

KeyStore ks;
try {
    ks = KeyStore.getInstance("PKCS11");
    ...
}
现在我发现这是一个错误:

java.security.KeyStoreException: PKCS11 not found
    at java.security.KeyStore.getInstance(Unknown Source)
    at receita.system.monitoring.Ping.main(Ping.java:292)
Caused by: java.security.NoSuchAlgorithmException: PKCS11 KeyStore not available
    at sun.security.jca.GetInstance.getInstance(Unknown Source)
    at java.security.Security.getImpl(Unknown Source)
    ... 2 more
我还尝试将Keystore.getInstance修改为:

ks = KeyStore.getInstance("PKCS11", a);
然后我得到了一个不同的错误:

java.security.KeyStoreException: PKCS11 not found
    at java.security.KeyStore.getInstance(Unknown Source)
    at receita.system.monitoring.Ping.main(Ping.java:292)
Caused by: java.security.NoSuchAlgorithmException: no such algorithm: PKCS11 for provider SunPKCS11-Aladdin
    at sun.security.jca.GetInstance.getService(Unknown Source)
    at sun.security.jca.GetInstance.getInstance(Unknown Source)
    at java.security.Security.getImpl(Unknown Source)
    ... 2 more
---------更新2(工作代码)---------

我的最终工作代码是:

System.setProperty("javax.net.ssl.trustStore", "U:\\Certificados\\efau.truestore");
System.setProperty("javax.net.ssl.trustStoreType", "jks");
System.setProperty("javax.net.ssl.trustStorePassword", "oiadad");

KeyManagerFactory kFac;
SSLContext sslContext;
SSLSocketFactory sockFactory = null;

SunPKCS11 providerMSCAPI = new SunPKCS11("u:/Certificados/etpkcs11.cfg");
Provider a = providerMSCAPI;
Security.addProvider(a);

KeyStore ks;
try {
    ks = KeyStore.getInstance("PKCS11");

    ks.load(null, password);

    InputStream in = IOUtils.toInputStream(ks.aliases().nextElement(), "UTF-8");
    ks.load(in, password);


    sslContext = SSLContext.getInstance("SSL", "SunJSSE");
    kFac = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    kFac.init(ks,null);

    sslContext.init(kFac.getKeyManagers(), null, null);
    sockFactory = sslContext.getSocketFactory();

    HttpsURLConnection conn = (HttpsURLConnection)new URL(/*<my-url>*/).openConnection();
    conn.setRequestMethod("GET");
    conn.setDoInput(true);
    conn.setSSLSocketFactory(sockFactory);

    int responseCode = conn.getResponseCode();

    InputStream inputstream = conn.getInputStream();
    InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
    BufferedReader bufferedreader = new BufferedReader(inputstreamreader);

    String line = null;
    String htmlResponse = "";

    while ((line = bufferedreader.readLine()) != null) {
        htmlResponse += line + "\n";
    }

} catch (KeyStoreException e) {
    e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
    e.printStackTrace();
} catch (CertificateException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
} catch (NoSuchProviderException e) {
    e.printStackTrace();
} catch (UnrecoverableKeyException e1) {
    e1.printStackTrace();
} catch (KeyManagementException e1) {
    e1.printStackTrace();
}
或在.cfg文件中设置插槽:

name=SafeNet
library=C:\Windows\System32\eTPKCS11.dll
slot=4

SunMSCAPI实现并不完美(例如,如果您有具有相同“友好名称”的证书,则有些证书将无法访问,因为它也是用于密钥库别名的唯一密钥)。我不确定它与硬件令牌的配合效果如何

因为,您还可以使用Oracle JRE对的直接支持

本质上,您的令牌驱动程序应该附带一个实现PKCS#11接口的DLL,您需要将Java指向它(如PKCS#11指南中所述)。为了获得更大的灵活性,动态安装提供程序可能更方便(请参阅以“动态安装提供程序[…]”开头的段落)


在您的评论之后,也许您可以使用试用和错误(通过捕获这些异常)来找到正确的插槽。您可以从字符串加载配置,而不是使用配置文件

String password = "xxxxxxxxx";
String storeType = "PKCS11";

String configuration = "name = OpenSC\n"
        + "library = /usr/lib/opensc-pkcs11.so\n";
Provider provider = new sun.security.pkcs11.SunPKCS11(
        new ByteArrayInputStream(configuration.getBytes("UTF-8")));

Security.addProvider(provider);

KeyStore keyStore = KeyStore.getInstance(storeType, provider);
keyStore.load(null, password.toCharArray());
如果您将
“slot=…\n”
添加到配置字符串中,并使用循环尝试各种值,直到它停止抛出异常为止,这可能会起作用。您可能需要删除失败的安全提供程序,或者更改名称。(我不建议这是一种干净的方法。)

顺便说一下,如果您不想硬编码密码(当然!)或从某个配置文件加载密码,您可以使用如下回调处理程序:

KeyStore keyStore = KeyStore.getInstance(storeType, provider);
LoadStoreParameter param = new LoadStoreParameter() {
    @Override
    public ProtectionParameter getProtectionParameter() {
        return new KeyStore.CallbackHandlerProtection(... put your callback handler here...);
    }
};
keyStore.load(param);

您的回调处理程序可以是“
new com.sun.security.auth.callback.DialogCallbackHandler()
”。我一般不建议使用任何
com.sun.*
sun.*
包,因为它们不是公共Java API的一部分,但您在这里使用的是
sun.security.pkcs11.SunPKCS11
,因此您的代码无论如何都会绑定到这个JRE家族。

尝试下面的代码,使用Java从usb令牌获取密钥库

class Test {
    public static void main(String args[]) throws IOException, GeneralSecurityException, DocumentException, CertificateVerificationException{
        
        // Create instance of SunPKCS11 provider
        String pkcs11Config = "name=eToken\nlibrary=C:\\Windows\\System32\\eps2003csp11.dll";
        java.io.ByteArrayInputStream pkcs11ConfigStream = new java.io.ByteArrayInputStream(pkcs11Config.getBytes());
        sun.security.pkcs11.SunPKCS11 providerPKCS11 = new sun.security.pkcs11.SunPKCS11(pkcs11ConfigStream);
        java.security.Security.addProvider(providerPKCS11);

        // Get provider KeyStore and login with PIN
        String pin = "12345678";
        java.security.KeyStore keyStore = java.security.KeyStore.getInstance("PKCS11", providerPKCS11);
        keyStore.load(null, pin.toCharArray());

        // Enumerate items (certificates and private keys) in th KeyStore
        java.util.Enumeration<String> aliases = keyStore.aliases();
        String alias = null;
        while (aliases.hasMoreElements()) {
            alias = aliases.nextElement();
            System.out.println(alias);
        }
    }
}
类测试{
publicstaticvoidmain(字符串args[])抛出IOException、GeneralSecurityException、DocumentException、CertificateVerificationException{
//创建SunPKCS11提供程序的实例
字符串pkcs11Config=“name=eToken\nlibrary=C:\\Windows\\System32\\eps2003csp11.dll”;
java.io.ByteArrayInputStream pkcs11ConfigStream=new java.io.ByteArrayInputStream(pkcs11Config.getBytes());
sun.security.pkcs11.SunPKCS11 providerPKCS11=新的sun.security.pkcs11.SunPKCS11(pkcs11ConfigStream);
java.security.security.addProvider(providerPKCS11);
//获取提供程序密钥库并使用PIN登录
字符串pin=“12345678”;
java.security.KeyStore KeyStore=java.security.KeyStore.getInstance(“PKCS11”,providerPKCS11);
load(null,pin.toCharArray());
//枚举密钥库中的项(证书和私钥)
java.util.Enumeration Alias=keyStore.alias();
字符串别名=null;
while(别名.hasMoreElements()){
别名=别名.nextElement();
System.out.println(别名);
}
}
}

您的令牌是否也带有PKCS 11库,而不是通过Windows密钥库?我不知道,我如何检查?在我看到的:API和标准支持-PKCS 11 V2.20、Microsoft CAPI、PC/SC、X.509 v3证书存储、SSL v3、IPsec/IKE CCID 1.0中,提到了PKCS 11,所以是的。检查类似
加密的内容ki.dll
或类似文件。我在我的计算机中找到了一个PKCS库eTPKCS11.dll。我试图创建指向它的提供程序,但出现了一些新错误。我在问题末尾添加了一些信息,并进行了新的修改,感谢您的帮助@Bruno。听起来您的dll没有正确加载。也许您可以尝试使用另一个软件要检查它是否工作()。也可能您的DLL是32位的,而您使用的是64位JVM(在本例中,如果它也是32位的,FF也可以工作)。使用
-Djava.security.debug=sunpkcs11
也可能会提供更多详细信息。您可能还需要指定一个“插槽”在您的配置中。非常感谢@Bruno,我曾尝试将cfg文件指向slot=0到3,但它不起作用,我的文件位于
slot=4
。当我使用
-Djava.security.debug=sunpkcs11
时,我在第一次尝试中找到了它。现在我必须或保留
-Djava.security.debug=sunpkcs11
或指定
slot=4
。您知道我是谁吗f无论如何都可以自动查找插槽,因此我不必指定插槽,也不需要保留调试标志?(我将用最终的工作代码和.cfg文件更新我的答案),我刚刚注意到,这可能很有用。
KeyStore keyStore = KeyStore.getInstance(storeType, provider);
LoadStoreParameter param = new LoadStoreParameter() {
    @Override
    public ProtectionParameter getProtectionParameter() {
        return new KeyStore.CallbackHandlerProtection(... put your callback handler here...);
    }
};
keyStore.load(param);
class Test {
    public static void main(String args[]) throws IOException, GeneralSecurityException, DocumentException, CertificateVerificationException{
        
        // Create instance of SunPKCS11 provider
        String pkcs11Config = "name=eToken\nlibrary=C:\\Windows\\System32\\eps2003csp11.dll";
        java.io.ByteArrayInputStream pkcs11ConfigStream = new java.io.ByteArrayInputStream(pkcs11Config.getBytes());
        sun.security.pkcs11.SunPKCS11 providerPKCS11 = new sun.security.pkcs11.SunPKCS11(pkcs11ConfigStream);
        java.security.Security.addProvider(providerPKCS11);

        // Get provider KeyStore and login with PIN
        String pin = "12345678";
        java.security.KeyStore keyStore = java.security.KeyStore.getInstance("PKCS11", providerPKCS11);
        keyStore.load(null, pin.toCharArray());

        // Enumerate items (certificates and private keys) in th KeyStore
        java.util.Enumeration<String> aliases = keyStore.aliases();
        String alias = null;
        while (aliases.hasMoreElements()) {
            alias = aliases.nextElement();
            System.out.println(alias);
        }
    }
}