问题描述
安卓同事称代码调试访问test-material.aaa.tv/xxx/xxxx.png等图片资源的时候报错,报错信息类似如下:
1 | javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. |
问题分析
查看了安卓的开放文档,出现该报错主要由一下三种情况发生:
首先排除第一点和第二点,证书是购买的赛门铁克。
然后去ssllabs网站测试test-material.aaa.tv
域名支持的https建联加密方式
起先怀疑是安卓可能使用了SSL3的加密方式进行https简练握手,后来确认安卓使用版本之后排除了该情况。
接着使用浏览器访问资源查看发现存在中间CA证书,但用域名检测网站的时候提示不含中间CA证书。于是乎又抓了一个安卓端可以显示的图片的域名来测试,在测试网站上也是属于不包含中间CA证书的情况。
两者的区别是,无法建联的域名是阿里云上的,能建联显示图片的域名是腾讯云上的。
当时很纳闷,后来查阅资料得知不同软件或者设备会有不同的行为,有些即便不存在中间CA也会帮忙代理查找。
安卓(其他苹果设备应该也是如此)自身会信任一些根证书,可能安卓去“解析”test-material.aaa.tv这个域名的时候得到的根证书,并不在信任证书里面,从而导致https建联失败。
于是发现安卓的确是存在信任一些列证书的情况:
然后来获取下test-material.aaa.tv的根证书情况,自然,在浏览器端获取到的可能是浏览器帮助代理请求获得的。得在服务器端用命令去查看,命令如下:
1 | openssl s_client -connect test-material.aaa.tv:443 -servername test-material.aaa.tv |
-connect 检测的域名,后面跟随ssl端口号
-servername 指定SNI(Server Name Indication),因为可能存在多个域名对应一张证书的情况,比如买的证书是一级域名和二级域名都可以使用这种情况,那么需要指定具体的域名。
SNI (Server Name Indication)是用来改善服务器与客户端 SSL (Secure Socket Layer)和 TLS (Transport Layer Security) 的一个扩展。主要解决一台服务器只能使用一个证书(一个域名)的缺点,随着服务器对虚拟主机的支持,一个服务器上可以为多个域名提供服务,因此SNI必须得到支持才能满足需求。
上图已经修复问题,修复之前只有条目0,不包含条目1。
- 红框信息:
- 起始证书是C=CN/L=xxxxx(后面一大串),然后C=CN/L=xxxxx(后面一大串)这个证书又是由GeoTrust Inc./CN=GeoTrust SSL CA - G3颁发。
- 修复问题之前只有条目0,没有条目1,然后在安卓信任证书列表里面查不到关于信任GeoTrust Inc./CN=GeoTrust SSL CA - G3这个证书的条目。所以建联无法通过了。
- GeoTrust SSL CA - G3又是由Geo Trust Inc./CN=Geo Trust Global CA颁发。
修复问题之后可以在安卓端信任列表里面查到存在Geo Trust Global CA证书条目,所以建联成功了。
问题解决方法:
中间证书CA没配置导致,上阿里云后台,查看该域名对应的证书,发现的确只有本机CA证书,没有配置中间证书CA,重新配置上后,问题解决,安卓端建联正常。
refer:
https://developer.android.google.cn/training/articles/security-ssl.html#MissingCa
http://blog.csdn.net/makenothing/article/details/53292335
证书检测地址:
安卓文档对于缺失中间证书的描述:
1 | 有趣的是,在大多数桌面浏览器中访问此服务器不会引发完全未知的 CA 或自签署服务器证书所引发的类似错误。这是因为大多数桌面浏览器都会将可信的中间 CA 缓存一段时间。当浏览器从某个网站访问和了解中间 CA 后,下次它就不需要将中间 CA 添加在证书链中。 |
查看证书到期时间:
openssl s_client -connect www.icoinbay.com:443 -servername www.icoinbay.com 2>/dev/null |openssl x509 -noout -dates