服务端接入了一个加速服务,导致Android端请求不了服务器,抛出了该异常:
1 | javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. |
初看,SSL握手失败,原因:在该信任的证书源中未找到该证书。 拿着异常信息搜索,查到该博客 , 如博客所说。
问题
由于服务器使用的使用的证书不是正规CA签发的,而是二级代理商签发的证书,导致验证通过。
方案
- 后台更换正规的证书,或客户端存放一个相应的证书源,具体操作参考BKS的制作
- 忽略https的证书校验,信任所有的证书
当时为了方便快,直接选中了第二种方案,忽略了潜在的风险,短暂解决了问题。
风险:
信任所有证书,将导致任何人都可以做中间人攻击。参考StackOverflow
而今,这个事情并未结束,并不是因为遭遇中间人攻击,而是,发现尽管服务器使用了http 2.0 版本,却发现客户端与服务器的tcp连接个数很多,tcp连接并没有能复用支持多个http请求。 没能复用,故客户端与服务器之间存在保持多个连接,对服务器造成了压力。
http1.0 本身tcp与http是一对一的关系
http1.1 支持connection: keep-alive ,可以复用tcp连接支持多个http请求,但在同一时刻只能处理一个请求,即两个http请求,需要等待第一个 请求完成后才能下一个请求,尽管还有pipelining属性支持支持流水线,可是在浏览器中默认是关闭的。 http2.0支持多路复用,单个tcp必然可以支持多个http请求的
而我们发现,服务器使用了支持了HTTP2.0,为什么tcp没有能支持多个http请求呢? IOS存在2个tcp连接,而android却有6,7个连接。
对此,我怀疑,
因为在C/S双方使用中,http实际使用的版本由两者支持的最低版本决定,是不是因为安卓端使用的是2.0以下的版本, 故导致不能复用连接的,但捉包后却发现android的连接也是http2,判断失误了。
项目中存在了两个OkHttpClient实例,还有一个Glide默认的HttpUrlConnection… 瞎猜,尝试将这三者统一使用一个OkHttpClient之后, 发现还是没有解决问题
后来,第三方加速服务那边说,捉包查看tcp连接时,存在RST包,怀疑是SSL的问题,我回想到原来处理证书的问题, 将忽略验证的方式改成了方案一App提供一个内置的证书,解决问题。
Https 回顾总结
依赖非对称加密
公钥私钥,一者加密的内容需要另一方解密,服务器保存私钥,客户端保存公钥
这样,这样客户端发送的信息只有服务才能解密查看
中间人攻击
正常是C向服务器请求pub_S,然后C使用这个公钥pub_S与服务器通信
可以若是存在一个中间人M在C和S之间,
当请求通信时,中间人自己生成了一对密钥,pub_M,pri_M,这样,客户端使用了pub_m来加密,而中间人转发请求时用了pub_m加密,这样,通信内容全被中间知道了。
证书
服务器的合法性需要证书机构来确定,
服务器提供公钥以及一些信息向CA申请证书,
C请求S后,S会返回其证书,
C根据CA验证服务器的合法性
三次握手
客户端发送一个包,SYN标志位为1,初始化一个序列号seq
服务端针回复一个包,SYN,ACK标志位为1,确认序号ack为收到序号加一,同时也初始化一个序列号seq
客户端回复一包,ACK为收到序列号加一
由此,三次握手完成
简单说,客户端你发送一个syn包,
服务器回复一下SYN+ACK包
客户端再回复一个ACK包
为什么是三次而不是两次?
若是第二次握手之后,服务器就开辟资源,可能会浪费资源 客户端发送第一个包后,会根据估计的往返时间来等待SYN-ACK包,若是时间超出了预期, 客户端就会自己没有发送个第一个包,收到了服务器的包,也不会建立连接, 而服务器若是在第一个包后就建立了连接,就会导致仅有自己建立了连接
四次挥手
双方都处于连接建立状态,established,
假设客户端想要关闭连接,发送一个FIN标志位为1,和一个序列号假设为100,进入fin-wait-1状态,此时不会主动再发新的数据包,但保持接受包的能力
服务端回复一个确认号也为100的包,表示收到对方要关闭连接请求,进入close-wait状态,通知上层不能发新的包了,但会将队列发送的包发完
服务端发完队列中包后,发一个FIN标志位1的包,通知客户端这是我最后一个发的包,进入last-ack状态,只需要确认关闭通知发给了客户端
客户端收到后FIN包后,确认接受完对方所有的包了,表示也关闭接收包的能力,发一个包让知道服务端知道客户端也正常关闭了
第一次挥手, 发送一个fin包 服务端收到后,会回一个ack包,然后继续发送完自己的数据包, 等数据包发送完毕后,发一个fin包给客户端表示自己也可以关闭了 客户端收到fin包后,会回复一个ack包给服务端,并进入一个等待时间,时间到会关闭连接,服务器收到这个确认包后也会直接关闭连接
为什么有4次挥手?
因为被动关闭连接的一方收到关闭请求后,还需要发送完数据包,才能发fin包,就是说,
被动关闭的一方,需要将ack包和fin分开发送,当然没有数据包时,也可以简化为3三次
参考