在Java客户端中接受服务器的自签名SSL证书

这看起来像一个标准的问题,但我无法在任何地方find明确的方向。

我有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中执行。

基本上有两种select:将自签名证书添加到您的JVM信任库或configuration您的客户端

选项1

从浏览器导出证书并将其导入到JVM信任库(以build立信任链):

 <JAVA_HOME>\bin\keytool -import -v -trustcacerts -alias server-alias -file server.cer -keystore cacerts.jks -keypass changeit -storepass changeit 

选项2

禁用证书validation(来自Example Depot的代码):

 // 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) { } 

请注意, 我不build议使用选项2 。 禁用信任pipe理器会破坏SSL的某些部分,使您在中间人攻击时容易受到攻击。 更喜欢选项#1,或者甚至更好的是让服务器使用由公知的CA签署的“真实”证书。

我将这个问题JDK 8u74不属于默认JVM可信主机的证书提供者。 提供商是www.identrust.com ,但那不是我想要连接的域名。 该域名已从该提供商获得其证书。 请参阅JDK / JRE中的默认列表是否将跨root用户信任信任? – 读几个条目。 另请参阅支持哪些浏览器和操作系统让我们encryption 。

所以,为了连接到我感兴趣的域名,其中有一个从identrust.com发布的证书我做了以下步骤。 基本上,我必须获得JVM信任的identrust.com( DST Root CA X3 )证书。 我能够这样做,使用Apache HttpComponents 4.5像这样:

1:通过证书链下载说明获取不诚信证书 。 点击DST根CA X3链接。

2:将该string保存到名为“DST根CA X3.pem”的文件中。 请务必在文件开头和结尾添加“—– BEGIN CERTIFICATE —–”和“—– END CERTIFICATE —–”行。

3:使用以下命令创build一个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)应用程序的资源目录中。

5:使用下面的代码来加载这个文件并将其附加到Apache 4.5 HttpClient。 这将解决所有具有从indetrust.com颁发的证书的域的问题indetrust.com 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(); 

当构build项目时,cacerts.jks将被复制到类path中并从那里加载。 我没有,在这个时间点,testing对其他ssl网站,但如果上述代码“链”在这个证书,那么他们也会工作,但我不知道。

参考: 自定义SSL上下文以及如何使用Java HttpsURLConnection接受自签名证书?

而不是设置默认的套接字工厂(IMO是一件坏事),而是会影响当前的连接,而不是你试图打开的每一个SSL连接:

 URLConnection connection = url.openConnection(); // JMD - this is a better way to do it that doesn't override the default SSL factory. if (connection instanceof HttpsURLConnection) { HttpsURLConnection conHttps = (HttpsURLConnection) connection; // Set up a Trust all manager TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted( java.security.cert.X509Certificate[] certs, String authType) { } public void checkServerTrusted( java.security.cert.X509Certificate[] certs, String authType) { } } }; // Get a new SSL context SSLContext sc = SSLContext.getInstance("TLSv1.2"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); // Set our connection to use this SSL context, with the "Trust all" manager in place. conHttps.setSSLSocketFactory(sc.getSocketFactory()); // Also force it to trust all hosts HostnameVerifier allHostsValid = new HostnameVerifier() { public boolean verify(String hostname, SSLSession session) { return true; } }; // and set the hostname verifier. conHttps.setHostnameVerifier(allHostsValid); } InputStream stream = connection.getInputStream(); 

信任所有SSL证书: – 如果要在testing服务器上进行testing,则可以绕过SSL。 但不要使用此代码进行生产。

 public static class NukeSSLCerts { protected static final String TAG = "NukeSSLCerts"; public static void nuke() { try { TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { X509Certificate[] myTrustedAnchors = new X509Certificate[0]; return myTrustedAnchors; } @Override public void checkClientTrusted(X509Certificate[] certs, String authType) {} @Override public void checkServerTrusted(X509Certificate[] certs, String authType) {} } }; SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, trustAllCerts, new SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String arg0, SSLSession arg1) { return true; } }); } catch (Exception e) { } } 

}

请在Activity或应用程序类中的onCreate()函数中调用该函数。

 NukeSSLCerts.nuke(); 

这可以在Android中使用Volley。

Apache HttpClient 4.5支持接受自签名证书:

 SSLContext sslContext = SSLContexts.custom() .loadTrustMaterial(new TrustSelfSignedStrategy()) .build(); SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext); Registry<ConnectionSocketFactory> reg = RegistryBuilder.<ConnectionSocketFactory>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); 

这将构build一个SSL套接字工厂,该工厂将使用TrustSelfSignedStrategy ,将其注册到自定义连接pipe理器,然后使用该连接pipe理器进行HTTP GET。

我赞同那些吟唱“不做生产”的人,但是有用例来接受生产以外的自签证书; 我们在自动化集成testing中使用它们,以便我们使用SSL(就像在生产中一样),即使没有在生产硬件上运行。

如果“他们”正在使用自签名证书,则需要他们采取必要步骤使其服务器可用。 具体来说,这意味着以可靠的方式离线向您提供证书。 所以让他们这样做。 然后使用“JSSE参考指南”中所述的keytool将其导入到信任库中。 甚至不要考虑在这里发布的不安全的TrustManager。

编辑为了十七 (!)downvoters和下面的许多评论者的利益,谁显然还没有真正阅读我在这里写了什么,这不是一个反对自我签名证书jeremiad。 正确实施自签证书没有任何问题 但是,实施它们的正确方法是通过脱机过程安全地传递证书而不是通过未经身份validation的渠道进行身份validation。 这当然是显而易见的? 对于我曾经工作过的每一个有安全意识的组织,从拥有数千家分支机构的银行到我自己的公司,都是显而易见的。 信任所有证书的客户端代码库“解决scheme”,其中包括绝对任何人签署的自签名证书,或任何将自己设置为CA的任意仲裁机构,其本身并不安全。 这只是在安全地玩。 这是毫无意义的。 你有一个私人的,防篡改,答复certificate,注射certificate与…有人交谈。 任何人。 一个男人在中间。 模仿者 任何人。 你可能只是使用明文。