标准Java SSL套接字是否有一种属性可以禁用SSL连接的主机名验证?目前我找到的唯一方法是编写一个主机名验证器,该验证器始终返回true。
Weblogic提供了这种可能性,可以使用以下属性禁用主机名验证:
-Dweblogic.security.SSL.ignoreHostnameVerify
标准Java SSL套接字是否有一种属性可以禁用SSL连接的主机名验证?目前我找到的唯一方法是编写一个主机名验证器,该验证器始终返回true。
Weblogic提供了这种可能性,可以使用以下属性禁用主机名验证:
-Dweblogic.security.SSL.ignoreHostnameVerify
可以创建自定义Java代理程序,覆盖默认的HostnameVerifier
。
import javax.net.ssl.*;
import java.lang.instrument.Instrumentation;
public class LenientHostnameVerifierAgent {
public static void premain(String args, Instrumentation inst) {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
public boolean verify(String s, SSLSession sslSession) {
return true;
}
});
}
}
然后只需将 -javaagent:LenientHostnameVerifierAgent.jar
添加到程序的 Java 启动参数中即可。
SSLSocketFactory.setHostnameVerifier(new AllowAllHostnameVerifier())
? - end-user@Nani的答案在Java 1.8u181中不再适用。你仍然需要使用自己的TrustManager,但它需要是X509ExtendedTrustManager
而不是X509TrustManager
:
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.Socket;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509ExtendedTrustManager;
public class Test {
public static void main (String [] args) throws IOException {
// This URL has a certificate with a wrong name
URL url = new URL ("https://wrong.host.badssl.com/");
try {
// opening a connection will fail
url.openConnection ().connect ();
} catch (SSLHandshakeException e) {
System.out.println ("Couldn't open connection: " + e.getMessage ());
}
// Bypassing the SSL verification to execute our code successfully
disableSSLVerification ();
// now we can open the connection
url.openConnection ().connect ();
System.out.println ("successfully opened connection to " + url + ": " + ((HttpURLConnection) url.openConnection ()).getResponseCode ());
}
// Method used for bypassing SSL verification
public static void disableSSLVerification () {
TrustManager [] trustAllCerts = new TrustManager [] {new X509ExtendedTrustManager () {
@Override
public void checkClientTrusted (X509Certificate [] chain, String authType, Socket socket) {
}
@Override
public void checkServerTrusted (X509Certificate [] chain, String authType, Socket socket) {
}
@Override
public void checkClientTrusted (X509Certificate [] chain, String authType, SSLEngine engine) {
}
@Override
public void checkServerTrusted (X509Certificate [] chain, String authType, SSLEngine engine) {
}
@Override
public java.security.cert.X509Certificate [] getAcceptedIssuers () {
return null;
}
@Override
public void checkClientTrusted (X509Certificate [] certs, String authType) {
}
@Override
public void checkServerTrusted (X509Certificate [] certs, String authType) {
}
}};
SSLContext sc = null;
try {
sc = SSLContext.getInstance ("SSL");
sc.init (null, trustAllCerts, new java.security.SecureRandom ());
} catch (KeyManagementException | NoSuchAlgorithmException e) {
e.printStackTrace ();
}
HttpsURLConnection.setDefaultSSLSocketFactory (sc.getSocketFactory ());
}
}
trustAllCerts
)。 - Maarten Bodewes标准的Java SSL套接字或SSL中没有主机名验证,因此您无法在该级别设置它。主机名验证是HTTPS(RFC 2818)的一部分:这就是为什么它表现为javax.net.ssl.HostnameVerifier,并应用于HttpsURLConnection的原因。
我在访问RESTful网络服务时也遇到了同样的问题。我使用以下代码来解决这个问题:
public class Test {
//Bypassing the SSL verification to execute our code successfully
static {
disableSSLVerification();
}
public static void main(String[] args) {
//Access HTTPS URL and do something
}
//Method used for bypassing SSL verification
public static void disableSSLVerification() {
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
} };
SSLContext sc = null;
try {
sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
}
}
这对我很有帮助。试试看吧!
@user207421是正确的,在标准Java SSL套接字或SSL中确实没有主机名验证。
但X509ExtendedTrustManager实现了主机名检查逻辑(请参见其javadoc)。要禁用此功能,我们可以将SSLParameters.endpointIdentificationAlgorithm设置为null,就像JDK AbstractAsyncSSLConnection所做的那样:
if (!disableHostnameVerification)
sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); // default is null
disableHostnameVerification
属性的值读取自属性:jdk.internal.httpclient.disableHostnameVerification
。
如何修改SSLParameters
对象取决于您使用的具体软件。
例如,Spring WebFlux WebClient:
HttpClient httpClient = HttpClient.create()
.secure(sslContextSpec ->
sslContextSpec
.sslContext(sslContext)
.handlerConfigurator(sslHandler -> {
SSLEngine engine = sslHandler.engine();
SSLParameters newSslParameters = engine.getSSLParameters(); // 返回的是一个新对象
// 参考:https://github.com/AdoptOpenJDK/openjdk-jdk11/blob/master/src/java.net.http/share/classes/jdk/internal/net/http/AbstractAsyncSSLConnection.java#L116
newSslParameters.setEndpointIdentificationAlgorithm(null);
engine.setSSLParameters(newSslParameters);
})
)
WebClient webclient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
最初的回答:
SSLConnectionSocketFactory sslConnectionSocketFactory =
new SSLConnectionSocketFactory(sslContext,
new String[] { "TLSv1.2" }, null, new HostnameVerifier() {
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});