通过 HTTPS/SSL 的 Java 客户端证书
我正在使用 Java 6,并尝试使用客户端证书针对远程服务器创建 HttpsURLConnection
。
服务器使用自签名根证书,并要求提供受密码保护的客户端证书。 我已将服务器根证书和客户端证书添加到我在 /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home/lib/security/cacerts(OSX 10.5)。 密钥库文件的名称似乎表明客户端证书不应该放在那里?
无论如何,将根证书添加到此存储解决了臭名昭著的 javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX 路径构建失败的问题。
但是,我现在陷入困境如何使用客户端证书。 我尝试了两种方法,但都没有成功。
首先,也是首选,尝试:
SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
URL url = new URL("https://somehost.dk:3049");
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(sslsocketfactory);
InputStream inputstream = conn.getInputStream();
// The last line fails, and gives:
// javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
我尝试跳过 HttpsURLConnection 类(不理想,因为我想与服务器进行 HTTP 通信),然后执行此操作:
SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket("somehost.dk", 3049);
InputStream inputstream = sslsocket.getInputStream();
// do anything with the inputstream results in:
// java.net.SocketTimeoutException: Read timed out
我什至不确定客户端证书是这里的问题。
I am using Java 6 and am trying to create an HttpsURLConnection
against a remote server, using a client certificate.
The server is using an selfsigned root certificate, and requires that a password-protected client certificate is presented. I've added the server root certificate and the client certificate to a default java keystore which I found in /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home/lib/security/cacerts
(OSX 10.5).
The name of the keystore file seems to suggest that the client certificate is not supposed to go in there?
Anyway, adding the root certificate to this store solved the infamous javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed' problem.
However, I'm now stuck on how to use the client certificate. I've tried two approaches and neither gets me anywhere.
First, and preferred, try:
SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
URL url = new URL("https://somehost.dk:3049");
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(sslsocketfactory);
InputStream inputstream = conn.getInputStream();
// The last line fails, and gives:
// javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
I've tried skipping the HttpsURLConnection class (not ideal since I want to talk HTTP with the server), and do this instead:
SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket("somehost.dk", 3049);
InputStream inputstream = sslsocket.getInputStream();
// do anything with the inputstream results in:
// java.net.SocketTimeoutException: Read timed out
I am not even sure that the client certificate is the problem here.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
终于解决了;)。 这里得到了强烈的提示(甘道夫的回答也触及了一点)。 缺少的链接(大部分)是下面的第一个参数,在某种程度上,我忽略了密钥库和信任库之间的区别。
自签名服务器证书必须导入到信任库中:
需要设置这些属性(在命令行上或在代码中):
工作示例代码:
Finally solved it ;). Got a strong hint here (Gandalfs answer touched a bit on it as well). The missing links was (mostly) the first of the parameters below, and to some extent that I overlooked the difference between keystores and truststores.
The self-signed server certificate must be imported into a truststore:
These properties need to be set (either on the commandline, or in code):
Working example code:
虽然不建议,您也可以使用来自 Java 开发人员年鉴:
While not recommended, you can also disable SSL cert validation altogether, using the following code that came from The Java Developers Almanac:
您是否设置了 KeyStore 和/或 TrustStore 系统属性?
或来自代码
与 javax.net.ssl.trustStore 相同
Have you set the KeyStore and/or TrustStore System properties?
or from with the code
Same with javax.net.ssl.trustStore
如果您正在使用 Axis 框架处理 Web 服务调用,则有一个更简单的答案。 如果您希望客户端能够调用 SSL Web 服务并忽略 SSL 证书错误,只需在调用任何 Web 服务之前放置此语句即可:
System.setProperty("axis.socketSecureFactory",
"org.apache.axis.components.net.SunFakeTrustSocketFactory");
关于在生产环境中这样做是一件非常糟糕的事情的常见免责声明适用。
我在 Axis wiki 中找到了此内容。
If you are dealing with a web service call using the Axis framework, there is a much simpler answer. If all want is for your client to be able to call the SSL web service and ignore SSL certificate errors, just put this statement before you invoke any web services:
System.setProperty("axis.socketSecureFactory",
"org.apache.axis.components.net.SunFakeTrustSocketFactory");
The usual disclaimers about this being a Very Bad Thing to do in a production environment apply.
I found this at the Axis wiki.
对我来说,这就是使用 Apache HttpComponents ~ HttpClient 4.x 的工作原理:
P12 文件包含使用 BouncyCastle 创建的客户端证书和客户端私钥:
For me, this is what worked using Apache HttpComponents ~ HttpClient 4.x:
The P12 file contains the client certificate and client private key, created with BouncyCastle:
我使用 Apache commons HTTP 客户端包在我当前的项目中执行此操作,它可以与 SSL 和自签名证书配合使用(在将其安装到您提到的 cacerts 中之后)。 请在这里查看:
http://hc.apache.org/ httpclient-3.x/tutorial.html
http://hc .apache.org/httpclient-3.x/sslguide.html
I use the Apache commons HTTP Client package to do this in my current project and it works fine with SSL and a self-signed cert (after installing it into cacerts like you mentioned). Please take a look at it here:
http://hc.apache.org/httpclient-3.x/tutorial.html
http://hc.apache.org/httpclient-3.x/sslguide.html
我认为您的服务器证书有问题,不是有效的证书(我认为这就是“handshake_failure”在这种情况下的含义):
将您的服务器证书导入客户端 JRE 上的 trustcacerts 密钥库。 使用 keytool 可以轻松完成此操作:
I think you have an issue with your server certificate, is not a valid certificate (I think this is what "handshake_failure" means in this case):
Import your server certificate into your trustcacerts keystore on client's JRE. This is easily done with keytool:
使用下面的代码
或者
根本不需要。 此外,无需创建您自己的自定义 SSL 工厂。
我也遇到了同样的问题,就我而言,存在完整证书链未导入信任库的问题。 使用 keytool 实用程序从根证书导入证书,您也可以在记事本中打开 cacerts 文件,查看是否导入了完整的证书链。 检查导入证书时提供的别名,打开证书并查看它包含多少个,cacerts 文件中应该有相同数量的证书。
此外,还应在运行应用程序的服务器中配置 cacerts 文件,两个服务器将使用公钥/私钥相互验证。
Using below code
or
is not at all required. Also there is no need to create your own custom SSL factory.
I also encountered the same issue, in my case there was a issue that complete certificate chain was not imported into truststores. Import certificates using keytool utility right fom root certificate, also you can open cacerts file in notepad and see if the complete certificate chain is imported or not. Check against the alias name you have provided while importing certificates, open the certificates and see how many does it contains, same number of certificates should be there in cacerts file.
Also cacerts file should be configured in the server you are running your application, the two servers will authenticate each other with public/private keys.
虽然这个问题已有 12 年多了,并且有很多好的答案,但我想提供一个替代方案。 以下是加载密钥库和信任库并获取 sslsocketfactory 或 sslcontext 的小片段:
此示例代码片段来自库: GitHub - SSLContext Kickstart 您可以使用以下代码片段添加它:
Although this question is more than 12 years old and has a-lot of good answers I want to provide an alternative. Here is a small snippet of loading the keystore and truststore and getting the sslsocketfactory or sslcontext:
This example code snippet is from the library: GitHub - SSLContext Kickstart You can add it with the following snippet: