如何使用MSCAPI提供程序进行客户端SSL身份验证

har*_*mic 5 java security authentication ssl

我正在编写一个程序,需要与Web服务器建立HTTPS连接,需要使用SSL客户端身份验证.

该程序的用户将使用来自Windows环境的证书来验证自己.

我发现了大量示例说明如何设置客户端身份验证,如果我首先将证书导出为pkcs12格式,它可以正常工作,但我不想强迫我的用户这样做.但是,当我尝试使用MSCAPI时,它总是会发生异常:

javax.net.ssl.SSLHandshakeException: Error signing certificate verify
        at sun.security.ssl.Alerts.getSSLException(Unknown Source)
        at sun.security.ssl.SSLSocketImpl.fatal(Unknown Source)
        at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
        at sun.security.ssl.ClientHandshaker.serverHelloDone(Unknown Source)
        at sun.security.ssl.ClientHandshaker.processMessage(Unknown Source)
        at sun.security.ssl.Handshaker.processLoop(Unknown Source)
        at sun.security.ssl.Handshaker.process_record(Unknown Source)
        at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
        at sun.security.ssl.SSLSocketImpl.readDataRecord(Unknown Source)
        at sun.security.ssl.AppInputStream.read(Unknown Source)
        at java.io.BufferedInputStream.fill(Unknown Source)
        at java.io.BufferedInputStream.read1(Unknown Source)
        at java.io.BufferedInputStream.read(Unknown Source)
        at sun.net.www.http.HttpClient.parseHTTPHeader(Unknown Source)
        at sun.net.www.http.HttpClient.parseHTTP(Unknown Source)
        at sun.net.www.http.HttpClient.parseHTTP(Unknown Source)
        at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
        at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(Unknown Source)
        at com.example.Win2.main(Win2.java:62)
Caused by: java.security.SignatureException: Bad Key.

        at sun.security.mscapi.RSASignature.signHash(Native Method)
        at sun.security.mscapi.RSASignature.engineSign(RSASignature.java:390)
        at java.security.Signature$Delegate.engineSign(Unknown Source)
        at java.security.Signature.sign(Unknown Source)
        at sun.security.ssl.RSASignature.engineSign(Unknown Source)
        at java.security.Signature$Delegate.engineSign(Unknown Source)
        at java.security.Signature.sign(Unknown Source)
        at sun.security.ssl.HandshakeMessage$CertificateVerify.<init>(Unknown Source)
        ... 16 more
Run Code Online (Sandbox Code Playgroud)

我无法确定该异常中的密钥可能出现的问题.

我做了一个很小的测试程序来重现我遇到的问题:

String passwd = .....";
URL url = new URL("https://.........");

KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
KeyStore keyStore = KeyStore.getInstance("Windows-MY");
keyStore.load(null, passwd.toCharArray());
keyManagerFactory.init(keyStore, passwd.toCharArray());

SSLContext context = SSLContext.getInstance("TLS");
context.init(keyManagerFactory.getKeyManagers(), null, null);
SSLSocketFactory socketFactory = context.getSocketFactory();

HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(socketFactory);
Run Code Online (Sandbox Code Playgroud)

很明显我在这里不了解的API.它如何知道我想要使用的密钥库中的哪些密钥?我期待Windows提示我,就像其他需要进行身份验证的应用程序一样,但我怀疑它只是选择它找到的第一个.

我是否需要实现自己的密钥管理器,以便选择使用哪个密钥?

如果我遍历密钥库,我可以看到其中的密钥,并可以通过调用getKey来提取密钥.更复杂的是,商店中有多个具有相同别名的键(但有效性不同).其他(非Java)应用程序,例如.Chrome,似乎能够以某种方式确定使用哪些密钥.

编辑:我忘了提,我使用的是Java 1.7.

Eri*_*rik 0

您可能想尝试使用不同的上下文。为您的 Windows 商店提供的安全措施可能无法识别“TLS”。也许改为“SSL_TLS”?