近来项目中使用了https,然后发现对此不是十分了解,借此简单梳理一下。
https原理
https简介
HTTP 协议(HyperText Transfer Protocol,超文本传输协议):是客户端浏览器或其他程序与Web服务器之间的应用层通信协议 。
HTTPS 协议(HyperText Transfer Protocol over Secure Socket Layer):可以理解为 HTTP+SSL/TLS, 即 HTTP 下加入 SSL 层,HTTPS 的安全基础是 SSL,因此加密的详细内容就需要 SSL,用于安全的 HTTP 数据传输。
原先的http协议使用的是明文传输,第三方可能会窃听、篡改、冒充。SSL/TLS协议就像是一道保护层,它的加入保证了:
- 所有信息都是加密传播,第三方无法窃听。
- 具有校验机制,一旦被篡改,通信双方会立刻发现。
- 配备身份证书,防止身份被冒充。
https原理
1.客户端发出请求
客户端想服务器发起请求,发送:
- 协议版本号,比如TLS 1.0版
- 生成随机数C1
- 客户端支持的加密方式
- 支持的压缩方法
2.服务器回应
服务器接收到请求,确认相关信息并作回应:
- 确认协议达成一致,否则关闭连接
- 生成随机数S1
- 选择一种加密方式,例如RSA公钥加密
- 服务器证书
3.客户端验证
客户端收到回应后,对服务器证书进行认证:
- 是否是可信机构颁布
- 证书中的域名与实际域名不一致
- 证书有效期
验证通过后,取出证书中的公钥。4.客户端回应
生成随机数C2,并使用证书公钥非对称加密C2生成P,回应给服务器。客户端现在拥有随机数C1+C2+S1和约定的加密算法,生成”对话密钥”K。5.服务器回应
服务器接收到P,使用服务器证书私钥解密P得到随机数C2。服务器端也拥有了随机数C1+C2+S1和约定的加密算法,生成”对话密钥”K。然后之后客户端与服务器端的通信都使用K加密内容。
Android踩坑
在客户端交互的时候会涉及证书一说,一种是自己签发的证书,一种是第三方机构认证的证书。详细可查看这个。然而Android对此就会有以下两种情况。
自签证书
信任所有证书
对于任意的证书都可信任,简单,但有一定安全局限。
以OkHttp为例,所有重写方法不做任何的检验和校对。
1 2 3 4 5 6 7 8 9 10 11
| static {
internalHttpClientWithLongTimeout = new OkHttpClient.Builder() .connectTimeout(30, TimeUnit.SECONDS) .writeTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .sslSocketFactory(SSLSocketClient.getSSLSocketFactory()) .hostnameVerifier(SSLSocketClient.getHostnameVerifier()) .proxy(Proxy.NO_PROXY) .build(); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| public class SSLSocketClient {
public static SSLSocketFactory getSSLSocketFactory() { try { SSLContext sslContext = SSLContext.getInstance("SSL"); sslContext.init(null, getTrustManager(), new SecureRandom()); return sslContext.getSocketFactory(); } catch (Exception e) { throw new RuntimeException(e); } }
private static TrustManager[] getTrustManager() { TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) { }
@Override public void checkServerTrusted(X509Certificate[] chain, String authType) { }
@Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[]{}; } } }; return trustAllCerts; }
public static HostnameVerifier getHostnameVerifier() { HostnameVerifier hostnameVerifier = new HostnameVerifier() { @Override public boolean verify(String s, SSLSession sslSession) { return true; } }; return hostnameVerifier; } }
|
只信任唯一证书
将证书文件srca.cer 文件,复制到 Android 项目的 resource/raw/中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| private void customTrust() { X509TrustManager trustManager; SSLSocketFactory sslSocketFactory; InputStream cerIn = getResources().openRawResource(R.raw.srca); try { trustManager = trustManagerForCertificates(cerIn); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, new TrustManager[]{trustManager}, null); sslSocketFactory = sslContext.getSocketFactory(); } catch (GeneralSecurityException e) { throw new RuntimeException(e); } mOkHttpClient = new OkHttpClient.Builder() .sslSocketFactory(sslSocketFactory, trustManager) .build(); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| public static X509TrustManager trustManagerForCertificates(InputStream in) throws GeneralSecurityException {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(in); if (certificates.isEmpty()) { throw new IllegalArgumentException("expected non-empty set of trusted certificates"); }
char[] password = "password".toCharArray(); KeyStore keyStore = newEmptyKeyStore(password); int index = 0; for (Certificate certificate : certificates) { String certificateAlias = Integer.toString(index++); keyStore.setCertificateEntry(certificateAlias, certificate); }
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(keyStore, password); TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(keyStore); TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) { throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers)); } return (X509TrustManager) trustManagers[0]; }
|
1 2 3 4 5 6 7 8 9 10 11 12
| private static KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException { try { KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); InputStream in = null; keyStore.load(in, password); return keyStore; } catch (IOException e) { throw new AssertionError(e); } }
|
数字证书签发机构(CA)颁发的证书
如果是这种情况,且该权威CA内置于系统内部,那么代码无需做任何处理。
参考引用
https://www.zhihu.com/question/33645891
http://pingguohe.net/2016/02/26/Android-App-secure-ssl.html
https://jaq.alibaba.com/community/art/show?articleid=545
https://yq.aliyun.com/articles/109939