看完了理论Android HTTPS基础,HTTPS实际在Android中的是怎样应用的呢?
通过查看官方文档,通过 HTTPS 和 SSL 确保安全, 我们看到,访问HTTPS非常简单,系统已经完成了全部的工作,代码如下:
URL url = new URL("https://wikipedia.org");
URLConnection urlConnection = url.openConnection();
InputStream in = urlConnection.getInputStream();
copyInputStreamToOutputStream(in, System.out);
没错,就这么简单。url.openConnection()
根据请求URL区分返回的是HttpURLConnection
还是HttpsURLConnection
。只要你的服务器证书是由知名 CA 颁发的,Android系统内部会自动去验证服务器证书的有效性,建立起安全的连接。
如何从CA申请证书,可以参看阿里云证书申请。证书有免费的和收费的, 分为单域名,多域名,通配符域名等,可以从不同的知名CA中申请,例如Symantec赛门铁克, CFCA中国金融认证中心,GlobalSign, GeoTrust等等。申请完成后,部署在服务器就可以了。
如果我们只需要简单跑通代码,不需要往下看了,看看官网文档即可。下面是简单了解一下Android是怎样验证服务器证书的,以及自签名证书如何使用。
Android如何验证服务器证书?从前面HTTPS基础我们知道
如果某受信CA为"Sam的签名商店"签发了一个证书,而Sam的签名商店也签发了一个站点证书,浏览器可能会将其作为从有效CA路径导出的证书接受。
所以Android系统内置了很多知名CA的根证书,只要是该CA签发的证书(中间CA),以及中间CA签发的证书,形成了证书链,该证书链上的所有证书都是可信任的。
更详细Android源代码可参考Android 根证书管理与证书验证
除了看源代码,还可以如何观察验证证书过程呢?
HttpsURLConnection
位于javax.net.ssl包下,提供了两个方法,可以传HostnameVerifier,SSLSocketFactory的类实例进去,HttpsURLConnection实例每次发起HTTPS请求都会调用这两个对象的方法。
public void setHostnameVerifier(HostnameVerifier v) {
}
public void setSSLSocketFactory(SSLSocketFactory sf) {
}
public interface HostnameVerifier {
/**
* Verify that the host name is an acceptable match with
* the server's authentication scheme.
*
* @param hostname the host name
* @param session SSLSession used on the connection to host
* @return true if the host name is acceptable
*/
public boolean verify(String hostname, SSLSession session);
}
HostnameVerifier是用来查看要访问的域名是否正确,verify方法返回true则继续访问, 返回false则阻止访问。这里跟服务器证书没啥关系,仅仅是用来检查客户端的请求URL域名部分。
SSLSocketFactory是一个抽象类,继承SocketFactory。javax.net.SocketFactory就是一个创建普通Socket的抽象工厂类,看看源码就明白,没啥好说的。而SSLSocketFactory顾名思义,是创建基于SSL的安全Socket的抽象工厂类,它的实例的创建不是通过简单的继承,下面给出一个创建实例的方法:MyX509TrustManager.getSSLSocketFactory():
public class MyX509TrustManager implements X509TrustManager {
private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[]{};
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return _AcceptedIssuers;
}
public static SSLSocketFactory getSSLSocketFactory() {
SSLContext context = null;
TrustManager[] trustManagers = new TrustManager[]{new MyX509TrustManager()};
try {
context = SSLContext.getInstance("TLS");
context.init(null, trustManagers, new SecureRandom());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
return context.getSocketFactory();
}
}
X509TrustManager是一个接口,提供了验证服务器证书的checkServerTrusted
方法,在这里面我们就可以检查服务端证书是否正确。
/**
* Given the partial or complete certificate chain provided by the
* peer, build a certificate path to a trusted root and return if
* it can be validated and is trusted for server SSL
* authentication based on the authentication type.
* <p>
* The authentication type is the key exchange algorithm portion
* of the cipher suites represented as a String, such as "RSA",
* "DHE_DSS". Note: for some exportable cipher suites, the key
* exchange algorithm is determined at run time during the
* handshake. For instance, for TLS_RSA_EXPORT_WITH_RC4_40_MD5,
* the authType should be RSA_EXPORT when an ephemeral RSA key is
* used for the key exchange, and RSA when the key from the server
* certificate is used. Checking is case-sensitive.
*
* @param chain the peer certificate chain
* @param authType the key exchange algorithm used
* @throws IllegalArgumentException if null or zero-length chain
* is passed in for the chain parameter or if null or zero-length
* string is passed in for the authType parameter
* @throws CertificateException if the certificate chain is not trusted
* by this TrustManager.
*/
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException;
参数X509Certificate[] chain就是X.509 v3证书链,一般chain[0]就是根证书,chain[1]可能是中间证书,chain[2]就是服务器证书,取决于chain数组的长度,可以通过代码获取每个证书版本号,序列号,有效日期,CA签名,使用者名称,使用者公钥等等。
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
for (X509Certificate c : chain) {
Log.d("TAG", "c version:"+c.getVersion()+" SerialNumber:" + c.getSerialNumber()+
" Principal:"+c.getIssuerDN().toString()+" X500Principal:"+c.getIssuerX500Principal().toString()+
" getSubjectDN:"+c.getSubjectDN().toString() +
" pb:" + GlobalUtils.getMD5(c.getPublicKey().getEncoded()));
}
}
如果不信任该证书,这里手动抛出异常即可,该连接就会断掉
如果需要自己处理来信任证书,比如自签名证书,也是在这里处理
我们可以看一下服务器证书一般是长啥样的,以win10系统,浏览器chrome访问百度https://www.baidu.com为例:
参考:
volley源码
通过 HTTPS 和 SSL 确保安全
Android 根证书管理与证书验证
Android Https相关完全解析 当OkHttp遇到Https
X.509证书的解析、验证及使用
SSL证书原理讲解
关于SSL证书之证书链
https证书配置完整证书链常见问题