加密算法
常用对称加密算法有:AES、DES、3DES、TDEA、Blowfish、RC4、RC5、IDEA
常用非对称加密算法有:RSA、DSA/DSS、Elgamal、Rabin、D-H、ECC
非对称加密算法:一对公钥和私钥,公钥加密的结果只能用私钥解密,私钥加密的结果只能用公钥解密。公钥可以自由传输,不担心被获取,私钥只能自己持有
https 加密演化
总结下来:
-
用一对对称加密密钥P 问题:密钥如何传输?
-
用一对非对称加密密钥A(公钥) 和 A`(私钥):
客户端向服务器发起请求,服务器用A传给客户端,之后,客户端用A加密请求服务器,服务器用A`加密响应客户端。
问题:A`如何可靠传输
- 用两对非对称加密密钥:
客户端向服务器发起请求,并把自己的公钥B传给服务器。服务器把自己的公钥传给客户端,这样它俩就用对方的公钥加密给对方传递消息,并用自己的私钥解密对方传过来的消息。
问题:依然可以被中间人攻击,在传递公钥的过程中,中间人把传递的公钥都保存下来,并把自己的公钥传递给客户端和服务器,这样中间人既可以读到消息内容,还可以伪造消息内容。
并且更加关键的是非对称加密太耗时了,如果消息内容长,那不是成灾难了吗
- 一对非对称加密密钥+一对对称加密密钥:
客户端向服务器发起请求,服务器把自己的公钥A传给客户端,客户端生成一个对称加密密钥P,客户端用A把P加密后传给服务器,之后它俩就可以愉快地用P加密传递消息了。
问题:解决了效率问题,但是依然解决不了中间人攻击
数字证书
传递密钥应该是避免不了中间人攻击吧,所以换了一种做法,数字证书。
数字证书里包括两种信息:
-
证书信息:证书使用者,证书颁发者,证书过期日期,网站域名,公钥(传公钥改成传证书了😄)
-
证书签名:把证书信息做成信息摘要,并用证书颁发者的私钥把信息摘要加密
这样把传公钥换成了传证书,那公钥的可靠性不能保证,证书的可靠性就能保证吗?
可以的
客户端拿到证书之后,用证书的颁发者的公钥解密签名,得到了信息摘要
再把证书信息也做成信息摘要,如果一样,那就证明证书信息没有被篡改,那也就是说公钥没有被篡改,可以用这个公钥
那么,证书颁发者的公钥哪里来?
一种是浏览器或者操作系统直接内嵌的就有,这些证书都是CA的证书
另一种是我们自己信任的,比如自签证书
自签证书
自签证书为什么不安全?我找到的博客,都说的很含糊。我觉得应该是这样的:
根证书是嵌在操作系统和浏览器中的,不需要下载的,所以是安全的,而自签证书是要下载自签的根证书的,那就不可靠了,万一下载过程被劫持了呢。
而且CA签发的证书是可以被调用的,是可以查有效性的,自签证书是没有办法查有效性的。
客户端如何完成 https 的
现在的 https 的流程:
-
服务器准备一对私钥和公钥
-
服务器把私钥和自己的信息做成证书,可以自己信任或者找CA颁发
-
客户端收到证书,判断域名、过期日期等信息,选择是否信任
-
信任的话,生成一个对称密钥,用公钥加密后传递给服务器
Charles https enable 原理
Charles https enable 利用的就是自签证书,首先要在要 https enable 的设备上信任Charles的根证书。
过程如下:
- 客户端发起请求,要求验证服务器
- 服务器把SSL证书发过来,该响应被Charles截获
- Charles验证证书通过后,保存下来该数字证书,并自己的根证书再签发一个SSL证书,给客户端
- 因为客户端已经信任了Charles的根证书,所以验证顺利通过
- 客户端取出SSL证书中的公钥,以为这就是服务器的公钥,实际是Charles的公钥
- 客户端随机生成一个根据对称加密算法生成的密钥,并用Charles的公钥加密后发给服务器
- 该请求又被Charles截获,它用自己的私钥解密后取出客户端的对称密钥
- 用服务器的公钥把对称密钥加密后发给服务器
至此,客户端拿着对称密钥、Charles公钥;
Charles拿着:对称密钥、Charles私钥、服务器公钥
服务器拿着:对称密钥、服务器私钥
三者都拿着对称密钥,所以就没有秘密可言了,我们就能看到 https 请求的内容了。
防抓包
对于HTTPS API接口,如何防止抓包呢?既然问题出在证书信任问题上,那么解决方法就是在我们的APP中预置证书。
在TLS/SSL握手时,用预置在本地的证书中的公钥校验服务器的数字签名,只有签名通过才能成功握手。由于数字签名是使用私钥生成的,而私钥只掌握在我们手上,中间人无法伪造一个有效的签名,因此攻击失败,无法抓包。
同时,为了防止预置证书被替换,在证书存储上,可以将证书进行加密后进行「嵌入存储」,如嵌入在图片中或一段语音中。这涉及到信息隐写的领域,这个话题我们有空了详细说。
预置证书/公钥更新问题
这样做虽然解决了抓包问题,但是也带来了另外一个问题:我们购买的证书都是有有效期的,到期前需要对证书进行更新。主要有两种方式:
提供预置证书更新接口。在当前证书快过期时,APP请求获取新的预置证书,这过渡时期,两个证书同时有效,直到安全完成证书切换。这种方式有一定的维护成本,且不易测试。
在APP中只预埋公钥,这样只要私钥不变,即使证书更新也不用更新该公钥。但是,这样不太符合周期性更新私钥的安全审计需求。
一个折中的方法是,一次性预置多个公钥,只要任意一个公钥验证通过即可。考虑到我们的证书一般购买周期是3-5年,那么3个公钥,可以使用9-15年,同时,我们在此期间还可以发布新版本废弃老公钥,添加新公钥,这样可以使公钥一直更新下去。
证书链
根证书、中间根证书、SSL证书
双向验证
双向认证同时验证服务器与客户端的身份,而单向认证则只验证服务器的身份。比如对于一个下载链接,为防止中间人篡改,只需使用最常见的单向认证。而网银的登录页面,需要控制仅让持有(由银行的CA签发的)数字证书对应私钥的用户登录
双向验证实际也能做到防抓包。服务器要验证客户端的证书,代理工具的证书不会通过服务器的验证的,所以也就不能被抓包,只能获取到正确的客户端证书。比如从app包中获取。
这点我很疑惑了,客户端给服务器发证书的请求,如果代理照原样发出去不就可以了?
可能是因为:服务器要求客户端发证书这个操作不是 http 层面上的,它的操作也跟 http 的不一样,它做不到原样发出去,它可能必须要求证书在机器的某个位置,而数字证书也不能从网络中截获后保存下来。