最近遇到一个奇怪的问题。
使用Android的webView显示从服务器取得的URL地址的内容时,发现在有一些手机上居然显示的是空白页面。通过程序的log发现再有问题的手机上抛出了如下的异常:
W/System.err(7009): javax.net.ssl.SSLPeerUnverifiedException: No peer certificate
W/System.err(7009): at com.android.org.conscrypt.SSLNullSession.getPeerCertificates(SSLNullSession.java:104)
W/System.err(7009): at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:98)
W/System.err(7009): at org.apache.http.conn.ssl.SSLSocketFactory.createSocket(SSLSocketFactory.java:393)
W/System.err(7009): at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:170)
W/System.err(7009): at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:169)
W/System.err(7009): at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:124)
W/System.err(7009): at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:365)
问了一下Google老师javax.net.ssl.SSLPeerUnverifiedException: No peer certificate 错误意思是说发生了证明书错误;
于是开始探究导致该错误的原因。
有2点奇怪的想象;
1.同样的程序访问同一个服务器有的手机有此问题,有的手机居然正常,而且用不正常显示的手机的浏览器直接访问URL是可以正常显示信息。
2.将程序里面的URL替换成其他服务器的URL显示正常。
通过2个现象分析判断多半是服务器的问题,但是是什么原因导致了和服务器之间产生了问题呢?不弄清楚很是不甘心,所以先从客户端(app)开始入手进行分析
客户端是使用了Apache HttpClient 与服务器进行通信,Apache HttpClient和SSL之间又有什么联系呢?
通过httpClient请求一个重定向https的地址其实是有一个SSL认证的过程。
HTTPS:超文本安全传输协议,和HTTP相比,多了一个SSL/TSL的认证过程,端口为443
在网上找到了一张https的时序图(此图解释的很清晰)
1.peer终端发送一个request,https服务端把支持的加密算法等以证书的形式返回一个身份信息(包含ca颁发机构和加密公钥等)。
2.获取证书之后,验证证书合法性。
3.随机产生一个密钥,并以证书当中的公钥加密。
4.request https服务端,把用公钥加密过的密钥传送给https服务端。
5.https服务端用自己的密钥解密,获取随机值。
之后双方传送数据都用此密钥加密后通信。
此次的问题发生在获取证书时候没有得到合法的证书导致了异常发生。
到此对于发生异常的原因弄清楚了,但是针对于2点奇怪的现象还是理解,为何有的手机有问题有的手机没问题呢,并且通过浏览器访问是正常的。
继续调查发现此次访问的服务器因为对应了SNI,而所使用的Apache HttpClient是不对应SNI,
并且Google也是将该方法作为非推荐方法,推出了新的访问方法HttpURLConnection
Apache HttpClient
SNI没对应
HttpURLConnection
2.3以上的版本对应SNI (使用了HttpsURLConnection类)
Android 3.0 (Honeycomb)以上的OS,标准浏览器的话是对应SNI
至此弄清楚了问题的原因。
参考URL:http://knowledge.sakura.ad.jp/tech/1706/