在java中打开自签名HTTPS SSL URL的直接方法?

我正在构建一个需要在java中打开自签名HTTPS SSL URL的应用程序。 我已经了解到,您可以通过添加对HttpsURLConnection.setDefaultHostnameVerifier()的调用来绕过SSL问题,您可以在其中说明允许的主机名。

但是,我的第二个问题是我的服务器运行自签名证书。 因此,即使主机名绕过,我也会遇到以下exception:

javax.net.ssl.SSLHandshakeException:sun.security.validator.ValidatorException:PKIX路径构建失败:sun.security.provider.certpath.SunCertPathBuilderException:无法找到所请求目标的有效证书路径

我环顾四周,我发现的唯一解决方案是将证书添加到java密钥库,但这对我来说是一个问题,因为我无法控制他们何时或如何更新java,我已经读过更新之间不保留密钥库,并且我无权访问JVM之外的系统。

我的应用程序只会调用单个服务器,所以如果有办法绕过自签名限制,并且从不咨询密钥库,那么这不是一个安全问题,但有没有办法做到这一点?

我正在构建一个需要在java中打开自签名HTTPS SSL URL的应用程序。 我已经了解到,您可以通过添加对HttpsURLConnection.setDefaultHostnameVerifier()的调用来绕过SSL问题,您可以在其中说明允许的主机名。

你的问题有一些误解:
主机名validation与证书是否自签名无关。
它是一个validation,它将您尝试访问的域与证书信息(CN或主题Alt名称)进行匹配。

如果您需要接受与该URL不匹配的证书,则需要配置validation程序(完全不推荐!!!)

关于自签。
这是无关紧要的。
您可以将应用程序配置为加载自定义信任库,其中包括应用程序将信任的证书。 事实上,这是最好的方法(比使用Java的cacerts)。
您所要做的就是在密钥库(JKS或PKCS12)中导入证书,并将其加载到应用程序中的自定义TrustManagers中。
只是谷歌arround,大量的例子,例如self-signed-ssl

尽管我不愿意这样说,但有时候最好还是顺其自然。

Java正试图通过使用适当的SSLvalidation实践来提高应用程序的安全性。 在这种情况下,它是成功的:如果你能够告诉程序“它没关系,接受不受信任的自签名证书”,你的程序就容易受到马洛里放置他的服务器的中间人攻击 (具有自己的自签名证书,与您的一样有效!)在主机和它正在尝试与之通信的目标之间。 然后他继续阅读你认为好的和安全的所有交通,你甚至没有注意到。

因此,令人遗憾的是,断言告诉Java“在连接到此主机时信任任何自签名证书”是安全的,这是不正确的。

您可以从StartSSL获得免费的,完全有效的SSL证书。 他们是好人。

这就是PKI应该如何工作 – 您必须拥有完整的信任链,从您存储在密钥库中的一些可信证书到您的证书。 所以你可以

  • 将您的证书设置为受信任
  • 问一些已经信任的人(即在密钥库中使用可信证书)来签署证书

试图绕过这不是一个坏主意。 您可以在某些Java安装后挂钩中安装证书,您可能会定期检查一些cron作业 ,或者在exception处理中执行此操作 。 您甚至可以从服务器下载这种方式的证书,并在每次更改时安装它(使用openssl轻松提取证书)。 但是上帝的缘故,如果你决定做这样的事情,至少要把一些关于它的审计日志写到第三台机器上,确保有人读它。

您也可以在门上写“黑客友好”。 🙂

(请注意,当你在谈论你的问题中的“密钥库”时,你实际上是在谈论信任存储(它是一个密钥库)。关于这个不幸令人困惑的Java术语的更多细节都在这个问题中 。)

我的应用程序只会调用单个服务器,所以如果有办法绕过自签名限制,并且从不咨询密钥库,那么这不是一个安全问题,但有没有办法做到这一点?

实际上,这将是一个安全问题。 您的应用程序可能确实只打算调用单个服务器,但信任存储正是为了帮助确保您连接到要连接的计算机。 没有它,您将无法知道您是连接到该服务器还是MITM服务器。

如果您需要SSL / TLS提供的安全性,请不要绕过这些规则。 (特别是,不要使用可接受任何证书的信任管理器。)

我环顾四周,我发现的唯一解决方案是将证书添加到java密钥库,但这对我来说是一个问题,因为我无法控制他们何时或如何更新java,我已经读过更新之间不保留密钥库,并且我无权访问JVM之外的系统。

引用自己的答案 (更具体的问题):

正确的方法是使用keytool将此自签名证书导入客户端的信任存储区,例如:

 keytool -import -file server-cert.pem -alias myserver -keystore mytruststore.jks 

您可以直接在JRE的信任存储(lib / security / cacerts)中执行此操作,这可能缺乏一定的灵活性,或者在您自己的此文件副本中执行此操作,然后将其设置为信任存储(默认密码为changeit或OSX上的changeme )。 您可以使用通常的javax.net.ssl.trustStore*系统属性(例如-Djavax.net.ssl.trustStore=mytruststore系统属性(和-Djavax.net.ssl.trustStorePassword`))为您的应用程序全局配置此信任库。 ..]

您实际上不必使用JRE提供的信任存储(并且可能会定期更新)。 您可以将自签名证书导入新的空密钥库,您将在其中将其用作应用程序中的信任存储区(不要随其导入私钥)。 事实上,在这个问题中讨论了如何讨论信任库管理:没有什么能阻止你专门为你的应用程序或部分应用程序使用不同的信任库(实际上Sun / Oracle不保证默认信任库的适用性) )。

我正在构建一个需要在java中打开自签名HTTPS SSL URL的应用程序。 我已经了解到,您可以通过添加对HttpsURLConnection.setDefaultHostnameVerifier()的调用来绕过SSL问题,您可以在其中说明允许的主机名。

虽然如果您的信任存储区中只有一个自我证书,但确实可能稍微有些问题,但主机名validation也是SSL / TLS提供的安全性的重要组成部分。 请勿绕过它,validation您要连接的证书是否与您尝试连接的名称相匹配。 (打个比方,如果你想查一下某人的身份,你不仅要检查他们的护照是由你信任的护照所发出的,而且你还要检查他们里面是否有正确的名字,否则你可能在该国家的任何人面前。)

使自签名证书的主题可分辨名称的CN= RDN成为服务器的主机名应该足够了,尽管这是传统的做法。 您可能还想添加“使用者替代名称”扩展名。 在这个答案中更多关于这一点 。

一般来说,不要绕过SSL“问题” 这些机制正是为了使SSL / TLS的使用安全。