SSL 握手警报:升级到 Java 1.7.0 后出现 unrecognized_name 错误
我今天从 Java 1.6 升级到 Java 1.7。 从那时起,当我尝试通过 SSL 建立与我的网络服务器的连接时,会发生错误:
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)
这是代码:
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);
}
它只是一个测试项目,这就是我允许并在代码中使用不受信任的证书的原因:
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);
}
我成功地尝试连接到 https://google.com。 我的错在哪里?
谢谢。
I upgraded from Java 1.6 to Java 1.7 today.
Since then an error occur when I try to establish a connection to my webserver over SSL:
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)
Here is the code:
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);
}
Its only a test project thats why I allow and use untrusted certificates with the code:
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);
}
I sucessfully tried to connect to https://google.com.
where is my fault?
Thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(17)
Java 7 引入了默认启用的 SNI 支持。我发现某些配置错误的服务器在 SSL 握手中发送“无法识别的名称”警告,大多数客户端都会忽略该警告......除了 Java。正如 @Bob Kerns 提到的,Oracle 工程师拒绝“修复”此错误/功能。
作为解决方法,他们建议设置 jsse.enableSNIExtension 属性。要让您的程序无需重新编译即可运行,请按以下方式运行您的应用程序:
该属性也可以在 Java 代码中设置,但必须在任何 SSL 操作之前设置。加载 SSL 库后,您可以更改该属性,但它 不会对 SNI 状态产生任何影响。要在运行时禁用 SNI(具有上述限制),请使用:
设置此标志的缺点是 SNI 在应用程序中的所有位置都被禁用。为了利用 SNI 并仍然支持错误配置的服务器:
SSLSocket
。我们将其命名为sslsock
。sslsock.startHandshake()
。这将阻塞,直到完成或出错时抛出异常。每当startHandshake()
发生错误时,都会获取异常消息。如果它等于握手警报:unrecognized_name
,那么您发现了配置错误的服务器。unrecognized_name
警告(在 Java 中是致命的)时,请重试打开SSLSocket
,但这次没有主机名。这有效地禁用了 SNI(毕竟,SNI 扩展是向 ClientHello 消息添加主机名)。对于 Webscarab SSL 代理,此提交 实现后备设置。
Java 7 introduced SNI support which is enabled by default. I have found out that certain misconfigured servers send an "Unrecognized Name" warning in the SSL handshake which is ignored by most clients... except for Java. As @Bob Kerns mentioned, the Oracle engineers refuse to "fix" this bug/feature.
As workaround, they suggest to set the
jsse.enableSNIExtension
property. To allow your programs to work without re-compiling, run your app as:The property can also be set in the Java code, but it must be set before any SSL actions. Once the SSL library has loaded, you can change the property, but it won't have any effect on the SNI status. To disable SNI on runtime (with the aforementioned limitations), use:
The disadvantage of setting this flag is that SNI is disabled everywhere in the application. In order to make use of SNI and still support misconfigured servers:
SSLSocket
with the host name you want to connect to. Let's name thissslsock
.sslsock.startHandshake()
. This will block until it is done or throw an exception on error. Whenever an error occurred instartHandshake()
, get the exception message. If it equals tohandshake alert: unrecognized_name
, then you have found a misconfigured server.unrecognized_name
warning (fatal in Java), retry opening aSSLSocket
, but this time without a host name. This effectively disables SNI (after all, the SNI extension is about adding a host name to the ClientHello message).For the Webscarab SSL proxy, this commit implements the fall-back setup.
我相信有同样的问题。
我发现我需要调整 Apache 配置以包含主机的 ServerName 或 ServerAlias。
此代码失败:
此代码有效:
Wireshark 显示在 TSL/SSL Hello 期间出现警告
警报(级别:警告,描述:无法识别的名称),服务器问候
正在从服务器发送到客户端。
然而,这只是一个警告,Java 7.1 随后立即回复“致命,描述:意外消息”,我认为这意味着 Java SSL 库不喜欢看到无法识别名称的警告。
来自传输层安全 (TLS) 的 Wiki:
112 无法识别的名称警告 仅限 TLS;客户端的服务器名称指示器指定了服务器不支持的主机名
这让我查看了我的 Apache 配置文件,我发现如果我为从客户端/java 端发送的名称添加 ServerName 或 ServerAlias,它可以正常工作,没有任何错误。
I had what I believe the same issue is.
I found that I needed to adjust the Apache configuration to include a ServerName or ServerAlias for the host.
This code failed:
And this code worked:
Wireshark revealed that during the TSL/SSL Hello the warning
Alert (Level: Warning, Description: Unrecognized Name), Server Hello
Was being sent from the server to the client.
It was only a warning, however, Java 7.1 then responded immediately back with a "Fatal, Description: Unexpected Message", which I assume means the Java SSL libraries don't like to see the warning of unrecognized name.
From the Wiki on Transport Layer Security (TLS):
112 Unrecognized name warning TLS only; client's Server Name Indicator specified a hostname not supported by the server
This led me to look at my Apache config files and I found that if I added a ServerName or ServerAlias for the name sent from the client/java side, it worked correctly without any errors.
您可以使用系统属性 jsse.enableSNIExtension=false 禁用发送 SNI 记录。
如果您可以更改代码,那么使用
SSLCocketFactory#createSocket()
(不带主机参数或带连接的套接字)会有所帮助。在这种情况下,它不会发送 server_name 指示。You can disable sending SNI records with the System property jsse.enableSNIExtension=false.
If you can change the code it helps to use
SSLCocketFactory#createSocket()
(with no host parameter or with a connected socket). In this case it will not send a server_name indication.我们还在新的 Apache 服务器构建中遇到了此错误。
我们案例中的修复方法是在
httpd.conf
中定义一个与 Java 尝试连接的主机名相对应的ServerAlias
。我们的ServerName
设置为内部主机名。我们的 SSL 证书使用外部主机名,但这不足以避免警告。为了帮助调试,您可以使用以下 ssl 命令:
openssl s_client -servername; -connect:443 -state
如果该主机名有问题,那么它将在输出顶部附近打印此消息:
SSL3alert read: warning:unrecognized name
我还应该注意到,当使用该命令连接到内部主机名时,我们没有收到该错误,即使它与 SSL 证书不匹配。
We also ran into this error on a new Apache server build.
The fix in our case was to define a
ServerAlias
in thehttpd.conf
that corresponded to the host name that Java was trying to connect to. OurServerName
was set to the internal host name. Our SSL cert was using the external host name, but that was not sufficient to avoid the warning.To help debug, you can use this ssl command:
openssl s_client -servername <hostname> -connect <hostname>:443 -state
If there is a problem with that hostname, then it will print this message near the top of the output:
SSL3 alert read: warning:unrecognized name
I should also note that we did not get that error when using that command to connect to the internal host name, even though it did not match the SSL cert.
您可以定义一个使用任意 ServerName 和通配符 ServerAlias 的最后一个包罗万象的虚拟主机,而不是依赖 apache 中的默认虚拟主机机制,例如
,这样您就可以使用 SNI,并且 apache 将不会发回 SSL 警告。
当然,只有当您可以使用通配符语法轻松描述所有域时,这才有效。
Instead of relying on the default virtual host mechanism in apache, you can define one last catchall virtualhost that uses an arbitrary ServerName and a wildcard ServerAlias, e.g.
In that way you can use SNI and apache will not send back the SSL warning.
Of course, this only works if you can describe all of your domains easily using a wildcard syntax.
它应该是有用的。要重试 Apache HttpClient 4.4 中的 SNI 错误 - 我们想出的最简单方法(请参阅 HTTPCLIENT -1522)
和
:
It should be useful. To retry on a SNI error in Apache HttpClient 4.4 - the easiest way we came up with (see HTTPCLIENT-1522):
and
and
使用:
Use:
在 spring boot 和 jvm 1.7 和 1.8 中遇到了这个问题。在 AWS 上,我们无法更改 ServerName 和 ServerAlias 来匹配(它们是不同的),因此我们执行了以下操作:
在 build.gradle 中,我们添加了以下内容:
这使我们能够绕过“无法识别的名称”问题。
Ran into this issue with spring boot and jvm 1.7 and 1.8. On AWS, we did not have the option to change the ServerName and ServerAlias to match (they are different) so we did the following:
In build.gradle we added the following:
That allowed us to bypass the issue with the "Unrecognized Name".
遗憾的是,您无法向 jarsigner.exe 工具提供系统属性。
我已提交缺陷 7177232,引用 @eckes 的缺陷 7127374 并解释为什么它被错误关闭。
我的缺陷具体是关于对 jarsigner 工具的影响,但也许这会导致他们重新打开其他缺陷并正确解决问题。
更新:实际上,事实证明您可以向 Jarsigner 工具提供系统属性,只是帮助消息中没有。使用 jarsigner -J-Djsse.enableSNIExtension=false
You cannot supply system properties to the jarsigner.exe tool, unfortunately.
I have submitted defect 7177232, referencing @eckes' defect 7127374 and explaining why it was closed in error.
My defect is specifically about the impact on the jarsigner tool, but perhaps it will lead them to reopening the other defect and addressing the issue properly.
UPDATE: Actually, it turns out that you CAN supply system properties to the Jarsigner tool, it's just not in the help message. Use
jarsigner -J-Djsse.enableSNIExtension=false
我遇到了同样的问题,结果发现反向 dns 设置不正确,它指向 IP 的错误主机名。在我更正反向 dns 并重新启动 httpd 后,警告就消失了。
(如果我不更正反向 dns,添加 ServerName 也对我有用)
I hit the same problem and it turned out that reverse dns was not setup correct, it pointed to wrong hostname for the IP. After I correct reverse dns and restart httpd, the warning is gone.
(if I don't correct reverse dns, adding ServerName did the trick for me as well)
默认情况下,我的
VirtualHost
的ServerName
已被注释掉。取消注释后有效。My
VirtualHost
'sServerName
was commented out by default. It worked after uncommenting.如果您使用 Resttemplate 构建客户端,则只能像这样设置端点: https://IP/path_to_service 和设置请求工厂。
使用此解决方案,您无需重新启动 TOMCAT 或 Apache:
If you are building a client with Resttemplate, you can only set the endpoint like this: https://IP/path_to_service and set the requestFactory.
With this solution you don't need to RESTART your TOMCAT or Apache:
我在从 Java 1.6_29 升级到 1.7 时也遇到了这个问题。
令人震惊的是,我的客户在 Java 控制面板中发现了一个可以解决此问题的设置。
在“高级”选项卡中,您可以选中“使用 SSL 2.0 兼容的 ClientHello 格式”。
这似乎解决了这个问题。
我们在 Internet Explorer 浏览器中使用 Java 小程序。
希望这有帮助。
I have also come across this issue whilst upgrading from Java 1.6_29 to 1.7.
Alarmingly, my customer has discovered a setting in the Java control panel which resolves this.
In the Advanced Tab you can check 'Use SSL 2.0 compatible ClientHello format'.
This seems to resolve the issue.
We are using Java applets in an Internet Explorer browser.
Hope this helps.
这是 Apache HttpClient 4.5.11 的解决方案。我遇到了主题通配符
*.hostname.com
的证书问题。它返回了相同的异常,但我不能使用属性System.setProperty("jsse.enableSNIExtension", "false");
禁用,因为它在 Google 位置客户端中出错。我找到了简单的解决方案(仅修改套接字):
Here is solution for Apache HttpClient 4.5.11. I had problem with cert which has subject wildcarded
*.hostname.com
. It returned me same exception, but I musn't use disabling by propertySystem.setProperty("jsse.enableSNIExtension", "false");
because it made error in Google location client.I found simple solution (only modifying socket):
当通过 Eclipse 访问运行 subversion 的 Ubuntu Linux 服务器时,我遇到了同样的问题。
它表明问题与 Apache(重新)启动时的警告有关:
这是由于
ports.conf
中的一个新条目造成的,其中另一个NameVirtualHost
指令与sites-enabled/000-default
中的指令一起输入。删除 ports.conf 中的指令后,问题就消失了(自然是在重新启动 Apache 后)
I had the same problem with an Ubuntu Linux server running subversion when accessed via Eclipse.
It has shown that the problem had to do with a warning when Apache (re)started:
This has been due to a new entry in
ports.conf
, where anotherNameVirtualHost
directive was entered alongside the directive insites-enabled/000-default
.After removing the directive in
ports.conf
, the problem had vanished (after restarting Apache, naturally)只是在这里添加一个解决方案。这可能对 LAMP 用户有所帮助
虚拟主机配置中的上述行是罪魁祸首。
错误时的虚拟主机配置
工作配置
Just to add a solution here. This might help for LAMP users
The above mentioned line in the virtual host configuration was the culprit.
Virtual Host Configuration when error
Working Configuration
有一个更简单的方法您可以在其中使用自己的 HostnameVerifier 来隐式信任某些连接。该问题出现在 Java 1.7 中,其中添加了 SNI 扩展,并且您的错误是由于服务器配置错误造成的。
您可以使用“-Djsse.enableSNIExtension=false”在整个 JVM 中禁用 SNI,也可以阅读我的博客,其中我解释了如何在 URL 连接之上实现自定义验证器。
There is an easier way where you can just use your own HostnameVerifier to implicitly trust certain connections. The issue comes with Java 1.7 where SNI extensions have been added and your error is due to a server misconfiguration.
You can either use "-Djsse.enableSNIExtension=false" to disable SNI across the whole JVM or read my blog where I explain how to implement a custom verifier on top of a URL connection.