跨运营商netty tls问题排查记录

背景

使用netty开发的tcp文件服务处于电信网络,发现手机端使用北京移动4G时,文件无法下载,但是上传正常,使用电信4G或联通4G,甚至其它地区的移动4G都可以正常下载。

疑点分析

初步认为是跨运营商时做了一些网络限制(内蒙古移动4G也存在这样的问题,有可能是地区政治因素影响)。可能的情况是移动4G入网口做了相关的安全拦截,不信任其它ISP发过来的数据报。因此排查方向暂定在端口以及加密通道。

排查过程

前提

4G手机开热点,使用笔记本连接4G热点运行测试客户端以便运行wireshark抓包。

尝试http

因为是网络问题,首先尝试http协议是否也存在类似问题,因此在排查过程中试验了不同环境下的http文件下载(包括分片),结果显示,不管是否加密http/https,都是能够正常下载的,因此http作为备用方案继续排查tcp问题。

针对端口拦截

考虑拦截规则可能是通过端口拦截或限速,之前文件服务所使用的端口为非常规端口,因此不断尝试其它端口,结论是确实某些特殊端口没有被拦截,比如8080、5222等,这些特殊端口下是可以正常下载,其它端口下表现为服务端正常接收到第一个报文,下行第一片文件数据时客户端接收不到(偶尔又能收到,但是速度非常缓慢1kb都不到),wilreshark抓包客户端存在大量tcp-out-of-order,服务端存在大量tcp-dup-ack、retransmission,即下行存在大量重传。

TLS加密

仅仅某些特殊端口可以使用无法满足需求,作为企业版软件运行在客户自己的机器上,特殊端口很可能被客户其它程序占用,继续排查,猜测既然是跨运营商拦截问题,那么很可能涉及加密问题,因此在netty基础上加了安全套接字,然而问题依旧。期间尝试修改各种netty参数,甚至放弃netty用原生nio以及原生socket加上tls层,同样无法通过。

python版本验证

在java版本服务端这无头绪的情况下,同事写了简易的python进行验证,尴尬的是python版本是可以的,但是也要加tls,非tls的python版本基本上也是不行的,因此明确非特殊端口需要加密。那么,接下来的问题是java版本服务端与python版本存在什么区别,通过交叉验证,结论如下:

服务端客户端结果
javajava×
javapython×
pythonpython
pythonjava基本上可以,有时不行

可以看出,只要有一端是java就存在问题(目前使用jdk1.8),客户端是java的情况会好一些。

抓包对比python与java

怀疑问题还是在tls,因此抓包对比python与java的tls建立过程,发现协商得到的加密套件有差异,python协商出的是TLS_RSA_WITH_AES_256_GCM_SHA384,java协商出的是TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,尝试将java版本的加密套件直接设置为TLS_RSA_WITH_AES_256_GCM_SHA384,但是报错没找到该套件。通过以下代码可以查看当前所有加密套件,低版本的java8可能没有TLS_RSA_WITH_AES_256_GCM_SHA384套件,这时需要下载jce,http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html, 将解压后的jar包覆盖到/jre/lib/security,同时将/jre/lib/security/java.security中的crypto.policy=unlimited注释打开。

SSLServerSocketFactory ssf = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
ssf.getSupportedCipherSuites(); // 支持的加密套件
ssf.getDefaultCipherSuites(); // 默认的加密套件

搞定之后,使用相同的加密套件java版本仍然不行。。。。。。。。

最终解决之道

处理过程中偶然搜索到一篇文章:https://juejin.im/post/5b94d7b96fb9a05ce5767fe2, netty的ssl优化,其中提到jdk自带的某些加密套件算法性能低下,特别是gcm相关算法,由此得到启发,虽然之前的测试python与java使用相同加密套件,但是实现上可能存在差别,因此参照文章所说引入netty-tcnative-boringssl-static包,使用openSSL实现的算法替换jdk自带的,再次测试发现大部分情况下都是可以的,偶尔不行,再过滤掉gcm相关套件,重新测试稳定可行,并且速度也有所提升,到此问题解决!!!

public static SslContext initServer() throws Exception {
        InputStream keyStream = null;
        try {
            keyStream = new FileInputStream("/var/run/security/sChat.jks");
            char[] serverPass = PASS.toCharArray();

            KeyStore keyStore = KeyStore.getInstance("JKS");
            keyStore.load(keyStream, serverPass);

            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
            keyManagerFactory.init(keyStore, serverPass);

            return SslContextBuilder.forServer(keyManagerFactory).sslProvider(SslProvider.OPENSSL)
                    .ciphers(null,
                            (ciphers, defaultCiphers, supportedCiphers) ->
                                    defaultCiphers.stream().filter(s -> !s.contains("GCM")).collect(Collectors.toSet()).toArray(new String[0]))
                    .build();
        } finally {
            try {
                if (keyStream != null) {
                    keyStream.close();
                }
            } catch (Exception e) {

            }
        }
    }

总结

  1. 跨运营商网络限制,按端口限制、加密限制;
  2. jdk实现的某些加密套件性能较低,在正常网络情况下没有问题,但是在本例跨运营商网络环境较不稳定的情况下可能存在因tls层某些步骤耗时较长而导致的超时重传等问题,具体哪些步骤会有影响有待深挖。

参照

https://juejin.im/post/5b94d7b96fb9a05ce5767fe2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值