X509TrustManager一定能起作用么?

起因

某日部分请求报"unable to find valid certification path to requested target",从异常堆栈来看是代码里设置的没生效,还是去做证书校验,证书校验失败导致最终抛异常。
image.png

分析

代码里确实在创建sslContext的时候指定了自定义的X509TrustManager,并信任服务端证书,为啥没有生效呢?

从下面的代码看到,在获取新的httpClient实例之前,会调用 Protocol.registerProtocol注册协议和sslContext的关联关系。

   public SSLContext createTrustSSLContext(CommunicationConfig config) throws GeneralSecurityException, IOException {
        String protocol = StringUtils.defaultIfBlank(ConfigUtil.getSSLProtocol(config), PROTOCOL);
        SSLContext sslContext = SSLContext.getInstance(protocol);
        TrustManager[] tm = { getForeverTrusterManager() };
        sslContext.init(null, tm, new SecureRandom());
        return sslContext;
    }

 private TrustManager getForeverTrusterManager() {
        return new X509TrustManager() {

            @Override
            public void checkClientTrusted(X509Certificate[] input, String authType) throws CertificateException {
                // 建立这种永远信任的连接,不需要校验
            }

            @Override
            public void checkServerTrusted(X509Certificate[] input, String arg1) throws CertificateException {
                // 建立这种永远信任的连接,不需要校验
            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[] {};
            }
        };
    }
 private HttpClient getAvailableClient(CommunicationConfig config) {
        try {
            String key = config.getKey();
            HttpClient client = instanceMap.get(key);
            if (client == null) {
                synchronized (instanceMap) {
                    if ((client = instanceMap.get(key)) == null) {
                        // 注册https协议
                        registerSSLProtocol(config);
                        // 创建客户端
                        client = contructClient(config);
                        // 注册协议处理类
                        ClientProtocolHandler handler = ProtocolFactory.getClientProtocolHandler(config.getProtocol()).newInstance();
                        handler.setConfig(config);
                        handlerMap.put(key, handler);
                        instanceMap.put(key, client);
                    }
                }
            }
            return client;
        } catch (Exception e) {
            errorLogger.error("HTTP客户端注册异常{}", e);
            throw new YtgwException(CommunicationErrorCode.NO_CLIENT_FOUND, e);
        }
    }

   private void registerSSLProtocol(CommunicationConfig config) {
        if (config.getProtocol() == TransportProtocol.HTTPS) {
            // 已经注册过就不用再注册了,如果有自定义协议用 HTTPS_SCHEMA
            String scheme = ConfigUtil.getHttpsSchema(config);
            TransportURL url = config.getUri();
            SSLContext context = SSLProtocolHelper.create(config);
            SSLSocketFactory socketFactory = context.getSocketFactory();
            ProtocolSocketFactory factory = new SSLSocketFactoryImpl(socketFactory, config);
            // 注册的协议只能使用全小写
            Protocol myHttps = new Protocol(scheme, factory, url.getPort());
            Protocol.registerProtocol(scheme, myHttps);
            logger.info("注册HTTPS协议,scheme={},url={},port={}", scheme, url.getUrl(), url.getPort());
        }
    }

从下面的代码能看到在打开连接的时候,会从protocol对象获取socketFactory,这个socketFactory就是上面注册上去的。
image.png

如果没有注册对应的协议,protocol获取到的socketFactory又是啥呢?
从下面的代码能看到protocol会调用SSLProtocolSocketFactory.getSocketFactory()创建一个默认的socketFactory,这个默认的socketFactory就是使用最上面异常堆栈里的sun.security.ssl.X509TrustManagerImpl去做证书的校验,如果证书有问题,就会出现校验失败的情况。

到这里就知道产生问题的根本原因了,如果Protocol.registerProtocol时注册的是协议A,访问的是协议B,protocol就会创建默认的socketFactory去做证书校验,最终出现证书校验失败的问题。

  public static Protocol getProtocol(String id) 
        throws IllegalStateException {

        if (id == null) {
            throw new IllegalArgumentException("id is null");
        }

        Protocol protocol = (Protocol) PROTOCOLS.get(id);

        if (protocol == null) {
            protocol = lazyRegisterProtocol(id);
        }

        return protocol;
    } 

private static Protocol lazyRegisterProtocol(String id) 
        throws IllegalStateException {

        if ("http".equals(id)) {
            final Protocol http 
                = new Protocol("http", DefaultProtocolSocketFactory.getSocketFactory(), 80);
            Protocol.registerProtocol("http", http);
            return http;
        }

        if ("https".equals(id)) {
            final Protocol https 
                = new Protocol("https", SSLProtocolSocketFactory.getSocketFactory(), 443);
            Protocol.registerProtocol("https", https);
            return https;
        }

        throw new IllegalStateException("unsupported protocol: '" + id + "'");
    }
  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值