# 介绍

还是之前那个 websocket, websocket 这部分是直接借用其他同事的代码,属于上拿来直接用了,等都转测都快结束了,自己过了一遍代码,发现 idea 提示这个版本的 websocket, 有 CVE 漏洞,心凉半截,当时感觉应该不是什么大问题,改个版本就可以,然后事情果然没这么简单

# org.java-websocket

同事给的依赖版本是 1.3.0, 我也在这个基础上,把功能都开发完了
{codecode}

org.java-websocket
Java-WebSocket
1.3.0

{endcodeendcode}
当我直接升级到最新版本 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, 遇到两个问题

  1. api 发生变化
  2. 信任所有证书报错

# 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 记录,发现中间还有一次变动

  1. chatclient.setWebSocketFactory( new SSLWebSocketClientFactory( sslContext ) );
  2. chatclient.setSocket( factory.createSocket() );
  3. 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);
}