# 介绍
还是之前那个 websocket, websocket 这部分是直接借用其他同事的代码,属于上拿来直接用了,等都转测都快结束了,自己过了一遍代码,发现 idea 提示这个版本的 websocket, 有 CVE 漏洞,心凉半截,当时感觉应该不是什么大问题,改个版本就可以,然后事情果然没这么简单
# org.java-websocket
同事给的依赖版本是 1.3.0, 我也在这个基础上,把功能都开发完了
{}
{}
当我直接升级到最新版本 1.5.6 的时候,有一个函数报错了
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
sslSocketFactory = sc.getSocketFactory();
# 直入主题
引用的三方开源件: https://github.com/TooTallNate/Java-WebSocket
之前没写完,今天来写,不想写了,直接讲两点
升级开源三方件 1.3.0 -> 1.5.6, 遇到两个问题
- api 发生变化
- 信任所有证书报错
# api 发生变化
原代码
// 升级前
MyWebsocketClient client = new MyWebsocketClient(new URI(uri), new Draft_76(), new HashMap<>(), 2000);
SSLContext sc = SSLContext.getInstance( "TLS" );
sc.init( kmf.getKeyManagers(), tmf.getTrustManagers(), null );
client.setWebSocketFactory(new DefaultSSLWebSocketServerFactory(sc));
// 升级后
MyWebsocketClient client = new MyWebsocketClient(new URI(uri), new Draft_6455(), new HashMap<>(), 2000);
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
client.setSocketFactory(sc.getSocketFactory());
setWebSocketFactory () 变成了 setSocketFactory ()
这是官方的样例写法
https://github.com/TooTallNate/Java-WebSocket/blob/master/src/main/example/SSLClientExample.java
看了一下 git 记录,发现中间还有一次变动
- chatclient.setWebSocketFactory( new SSLWebSocketClientFactory( sslContext ) );
- chatclient.setSocket( factory.createSocket() );
- chatclient.setSocketFactory(factory);
初版的时候,还是自己写了 factory, 第二版开始,变成使用了 jdk 给定 factory 了,就是这里导致了我还得继续改信任所有证书的代码,请看下一节
# 信任所有证书报错
本来以为也就改个 api 罢了,结果出包验证发现,日志中出现校验证书的报错,继续定位呗
这次也是代码走读,翻看源码
先给大家看下原本的代码
MyWebsocketClient client = new MyWebsocketClient(new URI(uri), new Draft_6455(), new HashMap<>(), 2000);
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[]{};
}
}
};
try {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
client.setSocketFactory(sc.getSocketFactory());
} catch (NoSuchAlgorithmException | KeyManagementException e) {
e.printStackTrace();
}
boolean connected = false;
try {
connected = client.connectBlocking();
} catch (InterruptedException e) {
log.error("connectBlocking fail", e);
}
if (!connected) {
client.close();
return Optional.empty();
}
return Optional.of(client);
首先就是 sc.init (null, trustAllCerts, new java.security.SecureRandom ());
然后往下跳进两步 发现有 trustManager = chooseTrustManager™;
private X509TrustManager chooseTrustManager(TrustManager[] tm) {
// We only use the first instance of X509TrustManager passed to us.
for (int i = 0; tm != null && i < tm.length; i++) {
if (tm[i] instanceof X509TrustManager) {
if (tm[i] instanceof X509ExtendedTrustManager) {
return (X509TrustManager)tm[i];
} else {
return new AbstractTrustManagerWrapper(
(X509TrustManager)tm[i]);
}
}
}
// nothing found, return a dummy X509TrustManager.
return DummyX509TrustManager.INSTANCE;
}
分析这里,发现最终会返回 new AbstractTrustManagerWrapper () 这个默认实现里面,是含有校验的逻辑的,并没有使用我们自己代码 new 出来的 X509TrustManager
改起来也比较简单,我们实现一个 X509ExtendedTrustManager 这个就可以了,这样他就会使用我们自己的 X509ExtendedTrustManager
最后贴一下前后对比
public Optional<MyWebsocketClient> generateWebsocketClient(String uri) throws URISyntaxException {
MyWebsocketClient client = new MyWebsocketClient(new URI(uri), new Draft_76(), new HashMap<>(), 2000);
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[]{};
}
}
};
try {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
client.setWebSocketFactory(new DefaultSSLWebSocketClientFactory(sc);
} catch (NoSuchAlgorithmException | KeyManagementException e) {
log.error(e.getMessage(), e);
}
boolean connected = false;
try {
connected = client.connectBlocking();
} catch (InterruptedException e) {
log.error("connectBlocking fail", e);
}
if (!connected) {
client.close();
return Optional.empty();
}
return Optional.of(client);
}
public Optional<MyWebsocketClient> generateWebsocketClient(String uri) throws URISyntaxException {
MyWebsocketClient client = new MyWebsocketClient(new URI(uri), new Draft_6455(), new HashMap<>(), 2000);
TrustManager[] trustAllCerts = new TrustManager[]{
new X509ExtendedTrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { }
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { }
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { }
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { }
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { }
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { }
}
};
try {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
client.setSocketFactory(sc.getSocketFactory());
} catch (NoSuchAlgorithmException | KeyManagementException e) {
log.error(e.getMessage(), e);
}
boolean connected = false;
try {
connected = client.connectBlocking();
} catch (InterruptedException e) {
log.error("connectBlocking fail", e);
}
if (!connected) {
client.close();
return Optional.empty();
}
return Optional.of(client);
}