JBoss AS5部署Java应用遇SSLHandshakeException握手失败求助
我在JBoss AS5上部署Java应用时,遇到了一个棘手的问题:尝试通过HTTPS URL下载图片时,抛出了javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure异常。我已经配置了全信任的TrustManager和跳过主机名验证的HostnameVerifier,但问题依然没有解决。
先贴一下我的配置代码:
// 全信任信任管理器配置 TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { @Override public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } @Override public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) { } @Override public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) { } } }; // 安装全信任信任管理器 try { SSLContext sc = SSLContext.getInstance("TLSv1.2"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); } catch (Exception e) { } HostnameVerifier hv = new HostnameVerifier() { @Override public boolean verify(String urlHostName, SSLSession session) { return true; } }; HttpsURLConnection.setDefaultHostnameVerifier(hv);
测试代码很简单:
URL url = new URL("https://cdn.hotelbeds.com/giata/62/623081/623081a_hb_ro_007.jpg"); url.openConnection().connect();
我开启了SSL调试模式(-Djavax.net.debug=ssl),得到的日志开头是:
18:20:23,030 INFO [STDOUT] Allow unsafe renegotiation: false Allow legacy hello messages: true Is initial handshake: true Is secure renegotiation: false
问题分析与解决方案
结合JBoss AS5的特性和SSL握手失败的常见原因,我整理了几个可能的解决方向:
1. 检查JDK版本对TLSv1.2的支持
JBoss AS5通常搭配的是Java 6或Java 7。Java 6默认不支持TLSv1.2,只有在update 111及以上版本才添加了对TLSv1.2的支持,而且需要手动启用;Java 7默认支持TLSv1.2,但可能不是默认协商的协议。
如果你的JDK是Java 6,要么升级到Java 7/8,要么尝试将SSLContext的协议改为通用的"TLS",让JVM自动协商最高支持的协议版本:
SSLContext sc = SSLContext.getInstance("TLS"); // 替代"TLSv1.2"
2. 避免全局SSL设置被JBoss覆盖
JBoss AS5自身会使用JSSE进行SSL通信,你设置的HttpsURLConnection.setDefaultSSLSocketFactory全局配置可能被JBoss的内部配置覆盖。建议为单个连接单独配置SSL参数,而不是修改全局默认值:
URL url = new URL("https://cdn.hotelbeds.com/giata/62/623081/623081a_hb_ro_007.jpg"); HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); // 为当前连接单独初始化SSLContext TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { @Override public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } @Override public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) { } @Override public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) { } } }; SSLContext sc = SSLContext.getInstance("TLS"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); conn.setSSLSocketFactory(sc.getSocketFactory()); // 单独设置hostname verifier conn.setHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String urlHostName, SSLSession session) { return true; } }); conn.connect();
3. 分析完整的SSL调试日志
你只提供了日志开头,建议查看完整的握手日志,重点关注:
ClientHello部分:客户端发送的协议版本和支持的密码套件(cipher suites)ServerHello部分:服务器选择的协议和密码套件
如果服务器没有返回任何可用的密码套件,说明客户端和服务器的密码套件没有交集。这种情况下,你需要:
- 升级JDK以获得更多的密码套件支持
- 手动添加服务器支持的密码套件到客户端的SSL参数中
4. 排查JBoss类加载冲突
JBoss AS5自带了一些JSSE相关的类,可能和应用中的类发生冲突。可以在WEB-INF/jboss-web.xml中配置类加载优先级,让应用优先使用JDK自带的SSL类:
<jboss-web> <class-loading> <loader-repository> com.yourcompany:archive=your-app-name <loader-repository-config>java2ParentDelegation=false</loader-repository-config> </loader-repository> </class-loading> </jboss-web>
注意替换com.yourcompany和your-app-name为你自己的应用信息。
总结
先从检查JDK版本和修改连接级别的SSL配置入手,这两个是JBoss AS5环境下最常见的解决方法。如果还是不行,务必分析完整的SSL调试日志,定位到握手失败的具体环节(比如协议不兼容、密码套件不匹配)。
内容的提问来源于stack exchange,提问作者Oleg Krylov

