如何在特定连接上使用不同的证书? [英] How can I use different certificates on specific connections?
问题描述
我正在添加到我们的大型Java应用程序的模块必须与另一家公司的SSL安全网站进行交谈。问题是该站点使用自签名证书。我有一份证书副本,以验证我没有遇到中间人攻击,我需要将此证书合并到我们的代码中,以便与服务器的连接成功。
A module I'm adding to our large Java application has to converse with another company's SSL-secured website. The problem is that the site uses a self-signed certificate. I have a copy of the certificate to verify that I'm not encountering a man-in-the-middle attack, and I need to incorporate this certificate into our code in such a way that the connection to the server will be successful.
以下是基本代码:
void sendRequest(String dataPacket) {
String urlStr = "https://host.example.com/";
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setMethod("POST");
conn.setRequestProperty("Content-Length", data.length());
conn.setDoOutput(true);
OutputStreamWriter o = new OutputStreamWriter(conn.getOutputStream());
o.write(data);
o.flush();
}
如果没有为自签名证书进行任何额外处理,这将死于conn.getOutputStream(),但有以下异常:
Without any additional handling in place for the self-signed certificate, this dies at conn.getOutputStream() with the following exception:
Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
....
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
....
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
理想情况下,我的代码需要教Java接受这是一个自签名证书,适用于应用程序中的这一点,而不是其他任何地方。
Ideally, my code needs to teach Java to accept this one self-signed certificate, for this one spot in the application, and nowhere else.
我知道我可以将证书导入JRE的证书颁发机构商店,这将允许Java接受它。如果我能提供帮助,那不是我想采取的方法;对于他们可能不使用的一个模块,我们所有客户的机器上看起来非常具有侵入性;它会影响使用相同JRE的所有其他Java应用程序,我不喜欢它,即使访问此站点的任何其他Java应用程序的几率为零。这也不是一个简单的操作:在UNIX上我必须获得以这种方式修改JRE的访问权限。
I know that I can import the certificate into the JRE's certificate authority store, and that will allow Java to accept it. That's not an approach I want to take if I can help; it seems very invasive to do on all of our customer's machines for one module they may not use; it would affect all other Java applications using the same JRE, and I don't like that even though the odds of any other Java application ever accessing this site are nil. It's also not a trivial operation: on UNIX I have to obtain access rights to modify the JRE in this way.
我还看到我可以创建一个TrustManager实例做一些自定义检查。看起来我甚至可以创建一个TrustManager,在除了这个证书之外的所有实例中委托给真正的TrustManager。但看起来全局安装了TrustManager,我认为会影响我们应用程序的所有其他连接,这对我来说也不是很合适。
I've also seen that I can create a TrustManager instance that does some custom checking. It looks like I might even be able to create a TrustManager that delegates to the real TrustManager in all instances except this one certificate. But it looks like that TrustManager gets installed globally, and I presume would affect all other connections from our application, and that doesn't smell quite right to me, either.
设置Java应用程序以接受自签名证书的首选,标准或最佳方法是什么?我可以完成上面提到的所有目标,还是我必须妥协?是否有涉及文件和目录及配置设置的选项,以及几乎没有代码?
What is the preferred, standard, or best way to set up a Java application to accept a self-signed certificate? Can I accomplish all of the goals I have in mind above, or am I going to have to compromise? Is there an option involving files and directories and configuration settings, and little-to-no code?
推荐答案
创建一个 SSLSocket
自己出厂,并在连接前将其设置在 HttpsURLConnection
上。
Create an SSLSocket
factory yourself, and set it on the HttpsURLConnection
before connecting.
...
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(sslFactory);
conn.setMethod("POST");
...
您需要创建一个 SSLSocketFactory
并保持它。下面是如何初始化它的草图:
You'll want to create one SSLSocketFactory
and keep it around. Here's a sketch of how to initialize it:
/* Load the keyStore that includes self-signed cert as a "trusted" entry. */
KeyStore keyStore = ...
TrustManagerFactory tmf =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, tmf.getTrustManagers(), null);
sslFactory = ctx.getSocketFactory();
如果您在创建密钥库时需要帮助,请发表评论。
If you need help creating the key store, please comment.
以下是加载密钥库的示例:
Here's an example of loading the key store:
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(trustStore, trustStorePassword);
trustStore.close();
要使用PEM格式证书创建密钥存储区,您可以使用<$ c编写自己的代码$ c> CertificateFactory ,或者只从JDK导入 keytool
(keytool 不会为密钥条目,但对于受信任的条目是好的。)
To create the key store with a PEM format certificate, you can write your own code using CertificateFactory
, or just import it with keytool
from the JDK (keytool won't work for a "key entry", but is just fine for a "trusted entry").
keytool -import -file selfsigned.pem -alias server -keystore server.jks
这篇关于如何在特定连接上使用不同的证书?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!