如何将证书身份验证与 HttpsURLConnection 结合使用?
我正在尝试连接到 HTTPS URL,但我需要使用客户端身份验证和第三方软件放置在系统上的证书。
我丝毫不知道我应该如何查找或使用它,我所要做的就是C#示例代码,这与我发现的所有Java答案有很大不同。(例如,密钥库显然需要某种密码?
这是我拥有的 C# 示例代码
System.Security.Cryptography.X509Certificates.X509CertificateCollection SSC_Certs =
new System.Security.Cryptography.X509Certificates.X509CertificateCollection();
Microsoft.Web.Services2.Security.X509.X509CertificateStore WS2_store =
Microsoft.Web.Services2.Security.X509.X509CertificateStore.CurrentUserStore(
Microsoft.Web.Services2.Security.X509.X509CertificateStore.MyStore);
WS2_store.OpenRead();
Microsoft.Web.Services2.Security.X509.X509CertificateCollection WS2_store_Certs = WS2_store.Certificates;
然后,它只是循环访问WS2_store_Certs CertificateCollection 并以这种方式检查它们。再往前走一点,它像这样设置证书:
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(url_string);
httpWebRequest.ClientCertificates = SSC_Certs;
这一切看起来都相当合乎逻辑,即使我不知道它是如何找到证书的,但我仍然无法找到Java的等效物。
更新
我正在建立的连接是依赖于JDK 5的更大应用程序的一部分,但是我已经设法使用sumscapi jar来查找我正在寻找的证书。当我尝试使用Windows密钥库进行连接时,它会出错,所以我认为我通过从Windows存储中获取所需的证书并将其插入默认的java中来解决这个问题。现在我得到一个EOFException,后跟一个SSLHandshakeException,上面写着“握手时远程主机关闭连接”。ssl调试跟踪不会向我揭示直接问题,因为我需要的证书显示在此处的证书链中。
它完成了整个 ClientKeyExchange 的事情,说它已经完成了,然后我从调试日志中得到的最后一条消息是
[write] MD5 and SHA1 hashes: len = 16
0000: 14 00 00 0C D3 E1 E7 3D C2 37 2F 41 F9 38 26 CC .......=.7/A.8&.
Padded plaintext before ENCRYPTION: len = 32
0000: 14 00 00 0C D3 E1 E7 3D C2 37 2F 41 F9 38 26 CC .......=.7/A.8&.
0010: CB 10 05 A1 3D C3 13 1C EC 39 ED 93 79 9E 4D B0 ....=....9..y.M.
AWT-EventQueue-1, WRITE: TLSv1 Handshake, length = 32
[Raw write]: length = 37
0000: 16 03 01 00 20 06 B1 D8 8F 9B 70 92 F4 AD 0D 91 .... .....p.....
0010: 25 9C 7D 3E 65 C1 8C A7 F7 DA 09 C0 84 FF F4 4A %..>e..........J
0020: CE FD 4D 65 8D ..Me.
AWT-EventQueue-1, received EOFException: error
我用来设置连接的代码是
KeyStore jks = KeyStore.getInstance(KeyStore.getDefaultType());
jks.load(null, null);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
jks.setCertificateEntry("alias", cert1); //X509Certificate obtained from windows keystore
kmf.init(jks, new char[0]);
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(kmf.getKeyManagers(), new TrustManager[]{tm}, null);
sslsocketfactory = sslContext.getSocketFactory();
System.setProperty("https.proxyHost", proxyurl);
System.setProperty("https.proxyPort", proxyport);
Authenticator.setDefault(new MyAuthenticator("proxyID", "proxyPassword"));
URL url = new URL(null, urlStr, new sun.net.www.protocol.https.Handler());
HttpsURLConnection uc = (HttpsURLConnection) url.openConnection();
uc.setSSLSocketFactory(sslsocketfactory);
uc.setAllowUserInteraction(true);
uc.setRequestMethod("POST");
uc.connect();
(我还没有尝试过HttpClient,因为我不知道如何找到证书文件,我也不确定这是否在每个客户端系统上都一样。
另一个更新
我已经在WINDOWS-ROOT密钥库中找到了我需要的证书的CA(并使用.verify()进行了检查以查看它们是否都签出),我也将它们添加到java密钥库中,但仍然没有任何更改。我想他们应该进入TrustStore,但我还没有找到一种方法来以编程方式做到这一点。(我宁愿不依赖最终用户来做这种事情,因为我只能向他们保证,由于这个荒谬的长问题开头提到的第三方软件,证书和CA将存在。
还有更多更新
在上一次更新的基础上,我得出的结论是,我的问题一定在于我的CA不在Java的cacerts文件中,因此它从服务器获取受信任的CA列表,但无法识别它们,并且随后不会发送导致连接失败的单个证书。所以问题仍然存在,我如何让Java使用它的密钥库作为信任库,或者以编程方式将证书添加到cacerts(不需要文件路径)?因为如果这些是不可能的,那只会给我留下秘密选项C,巫毒教。我会开始用针刺一个杜克娃娃,以防万一。