我今天从Java 1.6升级到Java 1.7.从那时起,当我尝试通过SSL建立与Web服务器的连接时发生错误:
javax.net.ssl.SSLProtocolException: handshake alert: unrecognized_name
at sun.security.ssl.ClientHandshaker.handshakeAlert(ClientHandshaker.java:1288)
at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1904)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1027)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1262)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1289)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1273)
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:523)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1296)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254)
at java.net.URL.openStream(URL.java:1035)
Run Code Online (Sandbox Code Playgroud)
这是代码:
SAXBuilder builder = new SAXBuilder();
Document document = null;
try {
url = new URL(https://some url);
document = (Document) builder.build(url.openStream());
} catch (NoSuchAlgorithmException ex) {
Logger.getLogger(DownloadLoadiciousComputer.class.getName()).log(Level.SEVERE, null, ex);
}
Run Code Online (Sandbox Code Playgroud)
它只是一个测试项目,这就是为什么我允许和使用不受信任的证书与代码:
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) {
}
}
};
try {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
Logger.getLogger(DownloadManager.class.getName()).log(Level.SEVERE, null, e);
}
Run Code Online (Sandbox Code Playgroud)
我成功尝试连接到https://google.com.我的错在哪里?
谢谢.
Lek*_*eyn 299
Java 7引入了默认启用的SNI支持.我发现某些配置错误的服务器在SSL握手中发出"无法识别的名称"警告,大多数客户端都会忽略该警告......除了Java.正如@Bob Kerns所说,Oracle工程师拒绝"修复"这个错误/功能.
作为解决方法,他们建议设置jsse.enableSNIExtension
属性.要允许您的程序无需重新编译即可运行,请将您的应用程序运行为:
java -Djsse.enableSNIExtension=false yourClass
Run Code Online (Sandbox Code Playgroud)
该属性也可以在Java代码中设置,但必须在任何SSL操作之前设置.加载SSL库后,您可以更改属性,但不会对SNI状态产生任何影响.要在运行时禁用SNI(具有上述限制),请使用:
System.setProperty("jsse.enableSNIExtension", "false");
Run Code Online (Sandbox Code Playgroud)
设置此标志的缺点是SNI在应用程序中的任何位置都被禁用.为了利用SNI并仍然支持配置错误的服务器:
SSLSocket
使用要连接的主机名创建一个.我们来这个名字吧sslsock
.sslsock.startHandshake()
.这将阻塞直到它完成或在出错时抛出异常.每当发生错误时startHandshake()
,都会收到异常消息.如果它等于handshake alert: unrecognized_name
,那么您找到了配置错误的服务器.unrecognized_name
警告(在Java中致命)时,重试打开a SSLSocket
,但这次没有主机名.这有效地禁用了SNI(毕竟,SNI扩展是关于向ClientHello消息添加主机名).对于Webscarab SSL代理,此提交实现了后备设置.
小智 87
我有我认为同样的问题.我发现我需要调整Apache配置以包含主机的ServerName或ServerAlias.
此代码失败:
public class a {
public static void main(String [] a) throws Exception {
java.net.URLConnection c = new java.net.URL("https://mydomain.com/").openConnection();
c.setDoOutput(true);
c.getOutputStream();
}
}
Run Code Online (Sandbox Code Playgroud)
这段代码有效:
public class a {
public static void main(String [] a) throws Exception {
java.net.URLConnection c = new java.net.URL("https://google.com/").openConnection();
c.setDoOutput(true);
c.getOutputStream();
}
}
Run Code Online (Sandbox Code Playgroud)
Wireshark透露,在TSL/SSL Hello警告警报(级别:警告,描述:无法识别的名称)期间,服务器Hello正从服务器发送到客户端.这只是一个警告,然而,Java 7.1然后立即响应"致命,描述:意外消息",我认为这意味着Java SSL库不喜欢看到无法识别的名称的警告.
来自Wiki on Transport Layer Security(TLS):
112无法识别的名称警告仅限TLS; 客户端的服务器名称指示符指定了服务器不支持的主机名
这让我看看我的Apache配置文件,我发现如果我为客户端/ java端发送的名称添加了ServerName或ServerAlias,它可以正常工作而没有任何错误.
<VirtualHost mydomain.com:443>
ServerName mydomain.com
ServerAlias www.mydomain.com
Run Code Online (Sandbox Code Playgroud)
eck*_*kes 36
您可以使用System属性jsse.enableSNIExtension = false禁用发送SNI记录.
如果您可以更改它有助于使用的代码SSLCocketFactory#createSocket()
(没有主机参数或连接的套接字).在这种情况下,它不会发送server_name指示.
Sco*_*yre 17
我们还在新的Apache服务器版本上遇到了这个错误.
我们的例子中的修复是ServerAlias
在httpd.conf
Java中尝试连接的主机名中定义一个.我们ServerName
设置为内部主机名.我们的SSL证书使用外部主机名,但这不足以避免警告.
要帮助调试,可以使用此ssl命令:
openssl s_client -servername <hostname> -connect <hostname>:443 -state
如果该主机名出现问题,则会在输出顶部附近打印此消息:
SSL3 alert read: warning:unrecognized name
我还应该注意,使用该命令连接到内部主机名时,我们没有收到该错误,即使它与SSL证书不匹配.
小智 15
您可以定义使用任意ServerName和通配符ServerAlias的最后一个catchall虚拟主机,而不是依赖于apache中的默认虚拟主机机制,例如
ServerName catchall.mydomain.com
ServerAlias *.mydomain.com
Run Code Online (Sandbox Code Playgroud)
这样你就可以使用SNI而apache不会发回SSL警告.
当然,这只有在您可以使用通配符语法轻松描述所有域时才有效.
Shc*_*ein 12
它应该是有用的.要重试Apache HttpClient 4.4中的SNI错误 - 我们提出的最简单的方法(请参阅HTTPCLIENT-1522):
public class SniHttpClientConnectionOperator extends DefaultHttpClientConnectionOperator {
public SniHttpClientConnectionOperator(Lookup<ConnectionSocketFactory> socketFactoryRegistry) {
super(socketFactoryRegistry, null, null);
}
@Override
public void connect(
final ManagedHttpClientConnection conn,
final HttpHost host,
final InetSocketAddress localAddress,
final int connectTimeout,
final SocketConfig socketConfig,
final HttpContext context) throws IOException {
try {
super.connect(conn, host, localAddress, connectTimeout, socketConfig, context);
} catch (SSLProtocolException e) {
Boolean enableSniValue = (Boolean) context.getAttribute(SniSSLSocketFactory.ENABLE_SNI);
boolean enableSni = enableSniValue == null || enableSniValue;
if (enableSni && e.getMessage() != null && e.getMessage().equals("handshake alert: unrecognized_name")) {
TimesLoggers.httpworker.warn("Server received saw wrong SNI host, retrying without SNI");
context.setAttribute(SniSSLSocketFactory.ENABLE_SNI, false);
super.connect(conn, host, localAddress, connectTimeout, socketConfig, context);
} else {
throw e;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
和
public class SniSSLSocketFactory extends SSLConnectionSocketFactory {
public static final String ENABLE_SNI = "__enable_sni__";
/*
* Implement any constructor you need for your particular application -
* SSLConnectionSocketFactory has many variants
*/
public SniSSLSocketFactory(final SSLContext sslContext, final HostnameVerifier verifier) {
super(sslContext, verifier);
}
@Override
public Socket createLayeredSocket(
final Socket socket,
final String target,
final int port,
final HttpContext context) throws IOException {
Boolean enableSniValue = (Boolean) context.getAttribute(ENABLE_SNI);
boolean enableSni = enableSniValue == null || enableSniValue;
return super.createLayeredSocket(socket, enableSni ? target : "", port, context);
}
}
Run Code Online (Sandbox Code Playgroud)
和
cm = new PoolingHttpClientConnectionManager(new SniHttpClientConnectionOperator(socketFactoryRegistry), null, -1, TimeUnit.MILLISECONDS);
Run Code Online (Sandbox Code Playgroud)
使用spring boot和jvm 1.7和1.8 进入这个问题.在AWS上,我们没有选择更改ServerName和ServerAlias以匹配(它们是不同的),因此我们执行了以下操作:
在build.gradle中,我们添加了以下内容:
System.setProperty("jsse.enableSNIExtension", "false")
bootRun.systemProperties = System.properties
Run Code Online (Sandbox Code Playgroud)
这使我们能够通过"无法识别的名称"绕过这个问题.
归档时间: |
|
查看次数: |
286720 次 |
最近记录: |