Java开发HTTPS请求SSL不受信任问题解决方法
在进行Java开发时,我们经常会涉及到请求HTTPS接口的情况。但是,在请求HTTPS接口时,我们有时会遇到SSL证书不受信任的问题,这会导致我们无法正确进行HTTPS请求。本文将详细讲解如何解决Java开发中HTTPS请求SSL不受信任的问题。
问题描述
在使用Java进行HTTPS请求时,如果SSL证书不被当前系统或浏览器所信任,我们会遇到SSL不受信任的错误。例如以下的请求代码:
URL url = new URL("https://example.com");
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setRequestMethod("GET");
con.connect();
如果example.com
证书不被当前系统或浏览器所信任,上述代码运行时会抛出类似于以下的异常:
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
这意味着SSL请求没有通过,无法访问服务器。
解决方法
方法一:信任指定证书
如果我们知道我们要访问的服务器的证书,我们可以在Java程序中添加该证书并将其信任。这需要我们手动将证书转换为.pem
格式,并添加到truststore
中。下面是具体的步骤:
-
将证书保存到本地,例如保存为
example.crt
。 -
使用以下命令将
example.crt
转换为.pem
格式:
openssl x509 -in example.crt -out example.pem -outform PEM
-
打开Java
cacerts
文件,该文件通常位于$JAVA_HOME/jre/lib/security/cacerts
。 -
在
cacerts
中添加证书。以下命令可添加证书到cacerts
:
keytool -importcert -file example.pem -alias example -keystore cacerts
其中,example.pem
为上一步转换的.pem
证书,example
为别名,可以根据自己的需要进行修改,cacerts
为Java的truststore
。
- 在Java程序中添加以下代码,启用指定证书的信任:
String cacertsPath = System.getProperty("java.home") + "/lib/security/cacerts";
System.setProperty("javax.net.ssl.trustStore", cacertsPath);
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
其中,cacertsPath
为Java的cacerts
文件路径,changeit
为默认的cacerts
密码。
方法二:信任所有证书
如果我们不想手动添加每个需要信任的证书,我们可以在Java程序中添加以下代码,使其信任所有证书:
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) {
// do nothing
}
public void checkServerTrusted(X509Certificate[] chain, String authType) {
// do nothing
}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}};
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier((hostname, sslSession) -> true);
上述代码会创建一个TrustManager数组,该数组信任所有证书,然后将其设置为默认的SSLSocketFactory。此外,为了避免主机名验证失败的错误,我们还需要设主机名验证器为所有主机通过。
示例说明
下面是两条示例说明:
示例一:信任指定证书
假设我们要请求的HTTPS接口地址为https://example.com/api
,并且我们知道该接口服务器的证书为example.crt
。我们可以使用以下代码将其信任:
String cacertsPath = System.getProperty("java.home") + "/lib/security/cacerts";
System.setProperty("javax.net.ssl.trustStore", cacertsPath);
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
KeyStore keyStore = KeyStore.getInstance("JKS");
InputStream inputStream = new FileInputStream("example.crt");
keyStore.load(inputStream, "password".toCharArray());
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = new ByteArrayInputStream(certificate.getBytes());
Certificate ca;
while ((ca = cf.generateCertificate(caInput)) != null) {
keyStore.setCertificateEntry("example", ca);
}
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier((hostname, sslSession) -> true);
URL url = new URL("https://example.com/api");
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setRequestMethod("GET");
con.connect();
上述代码将example.crt
信任,并将其添加到Java的truststore
中。然后,我们可以使用HttpsURLConnection
请求https://example.com/api
接口,在请求过程中不会遇到SSL不受信任的错误。
示例二:信任所有证书
如果我们不知道需要请求的HTTPS接口的证书,或者需要避免在添加每个证书时产生的大量工作。我们可以使用以下代码信任所有证书:
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) {
// do nothing
}
public void checkServerTrusted(X509Certificate[] chain, String authType) {
// do nothing
}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}};
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier((hostname, sslSession) -> true);
URL url = new URL("https://example.com/api");
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setRequestMethod("GET");
con.connect();
上述代码使用了信任所有证书的方法,如果请求的HTTPS接口证书未被信任,则无需进行任何额外工作即可通过请求。
总结
以上就是Java开发中HTTPS请求SSL不受信任问题的解决方法。我们可以信任指定证书或信任所有证书。由于信任所有证书存在一定的安全风险,因此我们应该谨慎使用。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java开发https请求ssl不受信任问题解决方法 - Python技术站