
本文详解在无法升级 java 版本的前提下,通过自定义 sslcontext 和 socketfactory,在 java 7 环境中强制启用 tlsv1.2 并向下兼容 tlsv1.0/tlsv1.1,特别适配基于 apache commons httpclient + axis 的遗留 soap 调用场景。
本文详解在无法升级 java 版本的前提下,通过自定义 sslcontext 和 socketfactory,在 java 7 环境中强制启用 tlsv1.2 并向下兼容 tlsv1.0/tlsv1.1,特别适配基于 apache commons httpclient + axis 的遗留 soap 调用场景。
Java 7u131+(推荐使用 u151 或更高更新版)原生支持 TLSv1.2,但默认不启用——JVM 启动时仅激活 TLSv1.0。要让基于 org.apache.commons.httpclient 的旧有 Axis 客户端(如 HttpClient + ProtocolSocketFactory)支持 TLSv1.2 握手,关键在于替换底层 SSLSocket 的协议套件,而非仅调用 SSLContext.getInstance("TLSv1.2")(该方式会禁用其他版本,不符合“同时支持低版本”的需求)。
由于你当前使用的是 HttpSecureProtocol 扩展的 FixedProtocolSocketFactory,需在其构造逻辑中注入显式指定协议列表的 SSLSocketFactory。注意:HttpSecureProtocol 内部使用的是 JDK 原生 SSLSocketFactory,我们可通过反射或继承重写其 createSocket() 行为,但更稳妥、可维护的方式是改用 org.apache.httpcomponents:httpclient(4.x)的 SSLConnectionSocketFactory 机制——它与 Commons HttpClient 2.x 不兼容,因此必须坚持原有技术栈。所幸,HttpSecureProtocol 允许设置自定义 SSLSocketFactory。
✅ 正确改造步骤如下:
-
在
FixedProtocolSocketFactory构造函数末尾,创建并注入支持多协议的SSLSocketFactory:
利用SSLContext显式初始化,并通过SSLSocket.setEnabledProtocols(...)控制握手协议范围:
public class FixedProtocolSocketFactory extends HttpSecureProtocol {
public FixedProtocolSocketFactory(final String keystore, final String keystorePassword)
throws GeneralSecurityException, IOException {
super();
// 配置信任链(CA证书)
TrustChain trustChain = TrustMaterial.CACERTS;
super.setTrustMaterial(trustChain);
// 加载客户端密钥库(如有)
File keystoreFile = new File(keystore);
if (keystoreFile.exists() && keystorePassword != null) {
char[] ksPass = keystorePassword.toCharArray();
KeyMaterial km = new KeyMaterial(keystoreFile, ksPass);
super.setKeyMaterial(km);
}
// ✅ 关键:覆盖默认 SSLSocketFactory,强制启用 TLSv1.0/1.1/1.2
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(
getKeyManagers(), // 来自 setKeyMaterial 的 KeyManager[]
getTrustManagers(), // 来自 setTrustMaterial 的 TrustManager[]
new SecureRandom()
);
// 创建包装工厂,确保新建 socket 启用指定协议
final String[] enabledProtocols = {"TLSv1", "TLSv1.1", "TLSv1.2"};
super.setSocketFactory(new SSLSocketFactory(sslContext) {
@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose)
throws IOException {
SSLSocket sslSocket = (SSLSocket) super.createSocket(socket, host, port, autoClose);
sslSocket.setEnabledProtocols(enabledProtocols); // 强制启用
return sslSocket;
}
@Override
public Socket createSocket() throws IOException {
SSLSocket sslSocket = (SSLSocket) super.createSocket();
sslSocket.setEnabledProtocols(enabledProtocols);
return sslSocket;
}
});
}
}-
确保 JVM 启动参数不干扰协议选择(可选但推荐):
添加以下参数,避免旧版安全策略限制 TLSv1.2:-Dhttps.protocols=TLSv1,TLSv1.1,TLSv1.2 -Djdk.tls.client.protocols=TLSv1,TLSv1.1,TLSv1.2
-
验证是否生效:
在 SOAP 调用前,添加简易检测逻辑(用于调试):SSLSocket socket = (SSLSocket) factory.createSocket(); System.out.println("Enabled protocols: " + Arrays.toString(socket.getEnabledProtocols())); // 应输出 [TLSv1, TLSv1.1, TLSv1.2]
⚠️ 注意事项:
立即学习“Java免费学习笔记(深入)”;
- Java 7 对 TLSv1.2 的支持依赖于 JCE Unlimited Strength Policy(Java 7u151+ 已内置,旧版本需手动安装 JCE 补丁);
- 若服务端仅支持 TLSv1.2(已禁用 TLSv1.0/1.1),而客户端未正确设置
setEnabledProtocols,将抛出javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake; -
Protocol.registerProtocol("https", ...)注册多次同名协议(如两次"https")会导致后注册者覆盖前者,请确保只注册一次(删除重复registerProtocol("https", ...)行); -
FixedProtocolSocketFactory中getKeyManagers()和getTrustManagers()是HttpSecureProtocol提供的受保护方法,可直接调用。
总结:在 Java 7 下启用 TLSv1.2 并保持向后兼容,核心不是“切换到 TLSv1.2”,而是主动控制 SSLSocket 的 enabledProtocols 列表。通过继承 HttpSecureProtocol 并注入自定义 SSLSocketFactory,即可在不修改 Axis 调用代码的前提下,安全、稳定地完成现代 TLS 协议升级。


















