1. 什么是https
https,也叫做http over TLS,TLS的前身是SSL。https与http的区别在于https在http与tcp之间加了一层安全层,https会对通信双方进行身份校验和数据加密,保证了传输的安全性。
2. 证书
凡是https的网站都需要一张证书
证书可以是权威机构CA颁发的,也可以是自签名的证书
证书一般有好几层,最顶层就是CA的根证书,这也很好理解,全世界那么多https网站,如果不分层的话,客户端无法保证内置全部的证书,证书群体就像一个树型结构,它们的子证书不一样,但根证书就那么一个或者几个。
客户端一般会内置CA的根证书,因此CA颁发的证书会被校验通过,而自签名的证书就需要在客户端内置了。
证书包含了很多信息,如颁发机构,有效期,不过其中最重要的就是公钥和私钥了,其中公钥是公开的,私钥是保存在服务端的。
3. https的通信过程
第一步:校验证书
客户端发起一个https的请求,服务端会返回一个证书链给客户端(公钥可以在证书中看到),由于客户端内置了CA根证书,如何该证书是CA颁发或者CA的二级证书结构颁发的,就会校验通过,如果是自签名的证书,则需要将证书导入到客户端。
当然如果证书过期了,也不会校验通过。
这里我其实有一个疑问,如果拦截方的证书也是CA颁发的,会怎么样?
第二步:生成对称秘钥
客户端随机生成一个key值,该key值就是对称秘钥,利用证书中的公钥对对称秘钥加密,然后发送给服务端,服务端利用证书中的私钥解密,拿到对称秘钥,这个过程就是用到了非对称加密
第三步:对通信内容进行加密
通过对称秘钥对请求进行加密,发送给服务端,服务端利用对称秘钥解密,并对响应内容进行加密,客户端也同样能解密,这个过程就是对称加密
至此就是https得安全传输过程
4. OkHttp中https的应用
由于CA颁发的证书,手机里面已经预置了,这里只需要对添加对自签名证书的校验即可
public class Https1Utils {
private InputStream[] certificates;
private InputStream bksFile;
private String password;
public SSLSocketFactory sslSocketFactory;
public X509TrustManager trustManager;
public Https1Utils(InputStream[] certificates, InputStream bksFile, String password) {
this.certificates = certificates;
this.bksFile = bksFile;
this.password = password;
initSSL();
}
private void initSSL() {
getSslSocketFactory();
}
private SSLSocketFactory getSslSocketFactory() {
try {
trustManager = getTrustManager();
KeyManager[] keyManagers = prepareKeyManager();
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, new TrustManager[]{trustManager}, null);
sslSocketFactory = sslContext.getSocketFactory();
return sslSocketFactory;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
return null;
}
private X509TrustManager getTrustManager() {
if (certificates == null || certificates.length <= 0)
return null;
try {
CertificateFactory certificateFactory = CertificateFactory
.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
int index = 0;
for (InputStream certificate : certificates) {
String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(certificateAlias,
certificateFactory.generateCertificate(certificate));
try {
if (certificate != null)
certificate.close();
} catch (IOException e)
{
}
}
TrustManagerFactory trustManagerFactory = null;
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));
}
X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
return trustManager;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private KeyManager[] prepareKeyManager() {
try {
if (bksFile == null || password == null)
return null;
KeyStore clientKeyStore = KeyStore.getInstance("BKS");
clientKeyStore.load(bksFile, password.toCharArray());
KeyManagerFactory keyManagerFactory = KeyManagerFactory
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(clientKeyStore, password.toCharArray());
return keyManagerFactory.getKeyManagers();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
if (HttpConfig.certificates != null) {
Https1Utils https1Utils=new Https1Utils(HttpConfig.certificates, null, null);
builder.sslSocketFactory(https1Utils.sslSocketFactory,https1Utils.trustManager);
}
只需要在Application中将证书流设置进来即可
NetWorkUtil.getInstance().setCertificates(getAssets.open(xxx.cer));
//如果服务端给的是字符串,可以做如下转换
NetWorkUtil.getInstance().setCertificates(new Buffer()
.writeUtf8(CER_12306)
.inputStream());