它看起来像一个标准问题,但我无法在任何地方找到明确的方向.
我有java代码尝试连接到可能具有自签名(或过期)证书的服务器.代码报告以下错误:
[HttpMethodDirector] I/O exception (javax.net.ssl.SSLHandshakeException) caught when processing request: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
据我了解,我必须使用keytool并告诉java允许这种连接是可以的.
解决此问题的所有说明都假设我完全熟练使用keytool,例如
为服务器生成私钥并将其导入密钥库
有没有人可以发布详细说明?
我正在运行unix,所以bash脚本最好.
不确定它是否重要,但代码是在jboss中执行的.
这里基本上有两个选项:将自签名证书添加到JVM信任库或将客户端配置为
从浏览器导出证书并将其导入JVM信任库(以建立信任链):
\bin\keytool -import -v -trustcacerts -alias server-alias -file server.cer -keystore cacerts.jks -keypass changeit -storepass changeit
禁用证书验证:
// Create a trust manager that does not validate certificate chains TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } public void checkClientTrusted( java.security.cert.X509Certificate[] certs, String authType) { } public void checkServerTrusted( java.security.cert.X509Certificate[] certs, String authType) { } } }; // Install the all-trusting trust manager try { SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); } catch (GeneralSecurityException e) { } // Now you can access an https URL without having the certificate in the truststore try { URL url = new URL("https://hostname/index.html"); } catch (MalformedURLException e) { }
请注意,我根本不推荐选项#2.禁用信任管理器会使SSL的某些部分失效,并使您在中间攻击中容易受到攻击.首选选项#1,或者更好的是,让服务器使用由知名CA签名的"真实"证书.
我将此问题追溯到不属于默认JVM可信主机的证书提供程序JDK 8u74
.提供商是www.identrust.com,但这不是我试图连接的域名.该域名已从该提供商处获得其证书.请参阅JDK/JRE中默认列表的交叉根覆盖信任? - 读下几个条目.另请参阅哪些浏览器和操作系统支持Let's Encrypt.
因此,为了连接到我感兴趣的域,其中有identrust.com
我发出的证书,我执行了以下步骤.基本上,我必须得到jVM DST Root CA X3
信任的identrust.com()证书.我能够使用Apache HttpComponents 4.5这样做:
1:在证书链下载说明中获取不受信任的证书.单击DST根CA X3链接.
2:将字符串保存到名为"DST Root CA X3.pem"的文件中.务必在文件的开头和结尾添加"----- BEGIN CERTIFICATE -----"和"----- END CERTIFICATE -----"行.
3:使用以下命令创建一个java密钥库文件cacerts.jks:
keytool -import -v -trustcacerts -alias IdenTrust -keypass yourpassword -file dst_root_ca_x3.pem -keystore cacerts.jks -storepass yourpassword
4:将生成的cacerts.jks密钥库复制到java /(maven)应用程序的resources目录中.
5:使用以下代码加载此文件并将其附加到Apache 4.5 HttpClient.这将解决所有具有从indetrust.com
util oracle 发布的证书的域的问题,该证书将证书包含在JRE默认密钥库中.
SSLContext sslcontext = SSLContexts.custom() .loadTrustMaterial(new File(CalRestClient.class.getResource("/cacerts.jks").getFile()), "yourpasword".toCharArray(), new TrustSelfSignedStrategy()) .build(); // Allow TLSv1 protocol only SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( sslcontext, new String[] { "TLSv1" }, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier()); CloseableHttpClient httpclient = HttpClients.custom() .setSSLSocketFactory(sslsf) .build();
当项目构建时,cacerts.jks将被复制到类路径中并从那里加载.在这个时间点,我没有测试其他ssl网站,但如果上面的代码"链"在这个证书中,那么它们也会起作用,但是我不知道.
参考:自定义SSL上下文以及如何使用Java HttpsURLConnection接受自签名证书?
Apache HttpClient 4.5支持接受自签名证书:
SSLContext sslContext = SSLContexts.custom() .loadTrustMaterial(new TrustSelfSignedStrategy()) .build(); SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext); Registryreg = RegistryBuilder. create() .register("https", socketFactory) .build(); HttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(reg); CloseableHttpClient httpClient = HttpClients.custom() .setConnectionManager(cm) .build(); HttpGet httpGet = new HttpGet(url); CloseableHttpResponse sslResponse = httpClient.execute(httpGet);
这将构建一个SSL套接字工厂,它将使用TrustSelfSignedStrategy
它,将其注册到自定义连接管理器,然后使用该连接管理器执行HTTP GET.
我同意那些吟唱"不要在生产中这样做"的人,但是有一些用例可以接受生产之外的自签证书; 我们在自动化集成测试中使用它们,这样即使不在生产硬件上运行,我们也会使用SSL(就像在生产中一样).