javax.net.ssl.SSLHandshakeException:java.security.cert.CertificateException: No subject alternative names matching IP address xxxx解决方案
报错原因
一般出现这种问题是因为目标服务器的证书问题, 证书的 Subject Alternative Names 字段中没有包含客户端所使用的连接地址(这里是 IP 地址)
一般建议不要使用IP地址来进行链接,如果实在是要用IP地址来进行连接的话,有两种处理方案:
方法一:可以将IP地址加入到服务器证书的 Subject Alternative Names 字段中;
方法二:在程序中进行处理,绕过目标服务器的安全验证
下面来介绍的就是方法二:在程序中绕过服务器的安全验证
在程序中绕过服务器的安全验证
以下就是绕过服务器验证的具体实现代码
java
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.SecureRandom;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
/**
* @author cbcbcb
*
*/
public class Test {
public static void main(String[] args) {
// 测试绕过主机验证
String pathUrl ="https://IP:PORT/api/test";
String json = "{\"name\":\"ccc\"}";
StringBuffer sbf = new StringBuffer();
try {
// 创建信任所有证书的 TrustManager
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
}
};
// 创建 SSLContext 并初始化
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new SecureRandom());
// 获取 SSLSocketFactory
SSLSocketFactory ssf = sslContext.getSocketFactory();
// 创建绕过主机名验证的 HostnameVerifier
HostnameVerifier allHostsValid = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
URL urlx = new URL(pathUrl);
HttpsURLConnection connection = (HttpsURLConnection) urlx.openConnection();
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.setUseCaches(false);
connection.setInstanceFollowRedirects(true);
connection.addRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
// 设置 SSLSocketFactory 和 HostnameVerifier
connection.setSSLSocketFactory(ssf);
connection.setHostnameVerifier(allHostsValid);
connection.connect();
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
if (!"".equals(json)) {
out.write(json.getBytes());
}
out.flush();
out.close();
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String lines;
while ((lines = reader.readLine()) != null) {
lines = new String(lines.getBytes(), "UTF-8");
sbf.append(lines);
}
// System.out.println("crm返回参数:" + sbf);
reader.close();
// 断开连接
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(decodeUnicode(sbf.toString()));
}
//下面这个方法是对输出的内容进行编译,有些接口返回的数据在输出台可能会是乱码展示
//这个时候在什么方法都无用的情况下,可以通过这个方法将乱码重新编译成正确的展示
public static String decodeUnicode(String str) {
Pattern pattern = Pattern.compile("(\\\\u(\\p{XDigit}{4}))");
Matcher matcher = pattern.matcher(str);
char ch;
while (matcher.find()) {
ch = (char) Integer.parseInt(matcher.group(2), 16);
str = str.replace(matcher.group(1), ch + "");
}
return str;
}
}
注意点
在上面这串代码中,有一些需要注意的一点就是在设置绕过验证的时候一定要注意设置的是对当前实例绕过还是全部都绕过,稍微有点不注意,设置成了全局的话,那么整个项目所有的链接都会收到相应的影响,所以这里一定要注意注意再注意
HttpsURLConnection.setDefaultHostnameVerifier-----全局设置
java
//全局设置
HostnameVerifier allHostsValid = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
connection.setHostnameVerifier(allHostsValid);-------设置对当前实例的
java
// 创建绕过主机名验证的 HostnameVerifier
HostnameVerifier allHostsValid = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
connection.setHostnameVerifier(allHostsValid);
最后多说一句
如果不好把控这个代码究竟有没有用的话,可以先不要更新到正式环境,先在本地或者测试服务器尝试一下在更新