如何在特定连接上使用不同的证书? [英] How can I use different certificates on specific connections?

查看:27
本文介绍了如何在特定连接上使用不同的证书?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我添加到大型 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 格式的证书创建密钥库,您可以使用 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆