HTTPS(Secure Hypertext Transfer Protocol)安全超文本传输协议 它是一个安全通信通道,它基于HTTP开发,用于在客户计算机和服务器之间交换信息。它使用安全套接字层(SSL)进行信息交换,简单来说它是HTTP的安全版,是使用 TLS/SSL 加密的 HTTP 协议。
HTTP 协议采用明文传输信息,存在信息窃听、信息篡改和信息劫持的风险,而协议 TLS/SSL 具有身份验证、信息加密和完整性校验的功能,可以避免此类问题。
TLS/SSL 全称安全传输层协议 Transport Layer Security, 是介于 TCP 和 HTTP 之间的一层安全协议,不影响原有的 TCP 协议和 HTTP 协议,所以使用 HTTPS 基本上不需要对 HTTP 页面进行太多的改造。
简单介绍了HTTPS,如果要补基础知识,看这里,那么问题来了
一个一个问题来解析
要解释这些问题,首先要知道Https握手时候使用的俩种加密方式。
* 前提 非常重要的事情—加密方案中的加密算法都是公开的,而密钥是保密的,没有密钥就不能解密,有了密钥就能解密 *
共享密钥加密
加密和解密同用一个密钥的方式,客户端和服务器得拥有同一个密钥,客户端使用密钥A加密,服务器使用密钥A解密。
缺点:如果攻击者也有了这个密钥既然共用同一个密钥,那么客户端肯定不能写死,或者存储密钥,那么服务器如何将密钥传输给客户端尼,哈哈,如果密钥能安全的传输,那么其他信息也可以安全传输,那就没Https什么事情了。
公开密钥加密
公开密钥就避免了共享密钥的传输问题,因为顾名思义,他的密钥是公开的,他使用的是非对称加密,一个公开密钥,一个私有密钥,公钥公开,大家都可以拿到,私钥只有服务有一份。客户端使用公钥对内容进行加密,服务器使用私钥才能解密。这里有个前提,没有私钥的情况下要对加密进行解密,不是不可能,但是可以认为几乎不可能。
知道了这俩种加密方式,再来看客户端如何校验公钥证书合法性,如果出现了中间人攻击,中间替换了公钥证书,客户端是如何校验的?既然是合法性的问题,服务器和客户端俩个当事人谁说了都不算,由数字证书认证机构(CA,Certificate Authority)和其相关机构颁发的公开密钥证书说了算.
证书包含以下信息:申请者公钥、申请者的组织信息和个人信息、签发机构 CA 的信息、有效时间、证书序列号等信息的明文,同时包含一个签名;
签名的产生算法:首先,使用散列函数计算公开的明文信息的信息摘要,然后,采用 CA 的私钥对信息摘要进行加密,密文即签名;
客户端在对服务器say hello之后,服务器将公开密钥证书发送给客户端,注意这个证书里面包含了公钥+各种信息+签名(私钥对各种信息加密后生成签名),客户端收到公开密钥证书后,相当于收到了一个包裹里面有公钥+各种信息+签名,怎么样使用这三个数据来校验尼,很简单,公钥加密,私钥解,私钥加密公钥也可以解,只要利用公钥对签名进行解密,然后最和各种信息做比较就可以校验出证书的合法性。
那么这里再推理几种情况能不能破解这个校验方案:
客户端say hello 后,被代理服务器拦截,然后他再向我们真正的服务器发相同的say hello,这时候服务器不知道你是谁,给了代理服务器公开密钥证书,然后代理服务器再把证书转发给客户端,客户端校验没毛病,然后用公钥加密的随机字符串发给代码服务器,这个时候代理服务器懵逼了,因为他没有私钥,解密不到随机字符串到底是什么,好吧,他虽然看不懂,但是还是把这一坨东西原封不动的给了服务器,服务器一看,没毛病,再把加密信息给代理服务器,代理服务器,因为不知道刚才的随机字符串是什么,而这个时候客户端和服务器已经不再使用公钥加密,而是使用了共享密钥加密,而关键的关键就是密钥就是刚才的随机字符串。代理服务器再次懵逼,就看数据来来回回,可是他看不懂,这样的中间人,只能算蠢萌的中转人,这也就是使用了HTTPS之后,packetCapture截取到的数据都是乱码的原因,关于这个可以参考这里。
客户端say hello 后,被代理服务器拦截,代理服务器也有CA颁发的证书,他把自己的合法证书发给客户端,客户端用证书里面的公钥解密签名之后,得到了各种信息,一看信息都对上了,没毛病信任。然后代理服务器宰相真正的服务器say hello,服务器下发真正的证书,代理服务器假装自己是客户端,信任了证书,然后校验通过,发用真正公钥加密后的随机数给服务器,服务器当然能解开,也就是代理服务器作为客户端和真正的服务器建立了合法的通信。再看客户端在校验了代理服务器自己的证书后也建立了合法的通信,这个时候,代理服务器作为中间人,因为他有自己的私钥和真正的公钥,可以欺上瞒下,俩边的信息都从它这里过了一次,信息被他偷窥到。攻击成功。
客户端内置真正的公钥,当代理服务器把它自己的证书传过来的时候,客户端用内置的公钥去解密证书中的签名,因为不是真正的私钥加密的所以解密失败,校验也就失败,连接中断。这也就是客户端信任所有的证书的风险—被中间人攻击。
如果你使用了OKHttp那就简单了看下面
try {
//载入证书
SSLSocketFactory factory = setCertificates(getAssets().open("https.cer"));
if (factory != null) {
httpClientBuilder.sslSocketFactory(factory);
}
} catch (IOException e) {
e.printStackTrace();
}
/**
* 载入证书
*/
private SSLSocketFactory setCertificates(InputStream... certificates) {
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) {
e.printStackTrace();
}
}
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
sslContext.init(
null,
trustManagerFactory.getTrustManagers(),
new SecureRandom()
);
return sslContext.getSocketFactory();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
解析到这里,想必大家也知道第三个问题的答案 :客户端如果没有内置公钥可以使用HTTPS么?当然可以,只是不安全,会被MITM。