0%

https原理和Android踩坑

近来项目中使用了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协议就像是一道保护层,它的加入保证了:

  1. 所有信息都是加密传播,第三方无法窃听。
  2. 具有校验机制,一旦被篡改,通信双方会立刻发现。
  3. 配备身份证书,防止身份被冒充。

    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 {

//获取这个SSLSocketFactory
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);
}
}

//获取TrustManager
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;
}

//获取HostnameVerifier
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 {
//通过trustManagerForCertificates方法为证书生成 TrustManager
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);
}
//设置 OkHttpClient
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 {
//InputStream 可以包含多个证书

//CertificateFactory 用于生成 Certificate,也就是数字证书
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");
}

// 将证书放入 keyStore
char[] password = "password".toCharArray(); // "password"可以任意设置
KeyStore keyStore = newEmptyKeyStore(password);
int index = 0;
for (Certificate certificate : certificates) {
String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(certificateAlias, certificate);
}

// 用 KeyStore 生成 X509 trust manager.
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;
// 传入 'null' 会生成一个空的 Keytore
//password 用于检查 KeyStore 完整性和 KeyStore 解锁
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