🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
https = http +ssl 即基于于HTTP协议,通过SSL或TLS提供加密处理数据、验证对方身份以及数据完整性保护 ## 优点 内容加密:采用混合加密技术,中间者无法直接查看明文内容 验证身份:通过证书认证客户端访问的是自己的服务器 保护数据完整性:防止传输的内容被中间人冒充或者篡改、 ## 加密类型 先科普一下,加密算法的类型基本上分为了两种: ### 对称加密 **指加密和解密使用相同的密钥** 典型的有DES、RC5、IDEA(分组加密),RC4(序列加密); ### 非对称加密 **又称为公钥加密算法**,是指加密和解密使用不同的密钥(公开的公钥用于加密,私有的私钥用于解密 比如A发送,B接收,A想确保消息只有B看到,需要B生成一对公私钥,并拿到B的公钥。于是A用这个公钥加密消息,B收到密文后用自己的与之匹配的私钥解密即可。 反过来也可以用私钥加密公钥解密。也就是说对于给定的公钥有且只有与之匹配的私钥可以解密,对于给定的私钥,有且只有与之匹配的公钥可以解密 典型的算法有RSA,DSA,DH; **由于非对称加密,有公钥也没有解密,也因此,相比较对称加密而言,非对称加密安全性更高,但是加解密耗费的时间更长,速度慢。** ### 散列算法 散列变换是指把文件内容通过某种公开的算法,变成固定长度的值(散列值),这个过程可以使用密钥也可以不使用。 这种散列变换是不可逆的,也就是说不能从散列值变成原文。因此,散列变换通常用于验证原文是否被篡改。 典型的算法有:MD5,SHA,Base64,CRC等 ## CA 证书 ### 关于证书: 服务端向客户端下发自己的证书,通常是CA认证的证书。证书包括了很多信息,主要有“公钥信息”、“签名”、“组织机构地区等信息”、“证书颁发机构”,关联的中级证书(medium certificate)、根证书(root certificate)等。 X.509 应该是比较流行的 SSL 数字证书标准,包含(但不限于)以下的字段: | 字段 | 值说明 | | --- | --- | | 对象名称(Subject Name) | 用于识别该数字证书的信息 | | 共有名称(Common Name) | 对于客户证书,通常是相应的域名 | | 证书颁发者(Issuer Name) | 发布并签署该证书的实体的信息 | | 签名算法(Signature Algorithm) | 签名所使用的算法 | | 序列号(Serial Number) | 数字证书机构(Certificate Authority, CA)给证书的唯一整数,一个数字证书一个序列号 | | 生效期(Not Valid Before) | (`・ω・´) | | 失效期(Not Valid After) | (╯°口°)╯(┴—┴ | | 公钥(Public Key) | 可公开的密钥 | | 签名(Signature) | 通过签名算法计算证书内容后得到的数据,用于验证证书是否被篡改 | **主要就是签名,用于验证是否篡改过** ### 客户端如何通过证书确定服务端的身份? 证明下面两点,(然后才可以使用证书上的公钥来加密生成Session key的随机数) > 1. 证明baidu.com这个证书确实是百度的 > 2. 证明baidu.com这个证书没有被其他人攥改过 证书以证书链的形式组织,在颁发证书的时候首先要有根CA机构颁发的根证书,再由根CA机构颁发一个中级CA机构的证书,最后由中级CA机构颁发具体的SSL证书。 数字证书采用信任链验证。数字证书的信任锚(信任的起点)就是根证书颁发机构。根证书(root certificate)是一个无签名或自签名的证书。是用于识别根证书颁发机构(CA)的公钥证书。 验证的具体实现如下图所示: ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f3cabb54bb904c70abcdb43ff345e15d~tplv-k3u1fbpfcp-watermark.awebp) 1. 从左往右,用户证书指向签署它的中级证书,并且,用户证书的摘要经由中级证书的私钥加密,密文作为该用户的证书签名(signature)记录在用户证书上。 2. 中级证书相应的,指向签署它的根证书,同时,中级证书的摘要经由根证书的私钥加密,密文作为中级证书的签名记录在中级证书上 3. 根证书在签署的时候,使用签发机构的私钥对证书的摘要进行加密,密文作为根证书的签名记录在根证书上。 **如何验证证书** #### 1.如何验证证书没有被篡改过 比如验证用户证书A,需要用到中级证书的公钥B解密前者的签名得到摘要 Digest1,我们的客户端也计算A证书的内容得到摘要 Digest2。对比这两个摘要就能知道前者是否被篡改。后者同理,使用他的证书签发者提供的公钥验证。当验证到**到受信任的根证书**时,就能确定这个证书是可信的。 #### 2.为什么根证书是可信的 数字证书认证机构(Certificate Authority, CA)签署和管理的 **CA 根证书**,会被纳入到你的浏览器和操作系统的可信证书列表中,并由这个列表判断根证书是否可信。所以不要随便导入奇奇怪怪的根证书到你的操作系统中。 ## HTTPS握手的过程 在 HTTPS 加密原理的过程中把对称加密和非对称加密都利用了起来,SSL协议是用非对称密码算法来协商密钥,并使用密钥加密明文并传输的 HTTPS大体分为以下几个步骤: 1. 验证证书的有效性(是否被更改,是否合法) 2. 握手生成会话密钥 3. 利用会话密钥进行内容传输 假设现在有客户端 A 和服务器 B : 1. 首先,客户端 A 访问服务器 B ,比如我们用浏览器打开一个网页https://www.baidu.com,这时,浏览器就是客户端 A ,百度的服务器就是服务器 B 了。 2. 时候客户端 A 会生成一个随机数1,把**随机数1 、自己支持的 SSL 版本号以及支持的加密**算法(譬如说,对称加密算法有DES,RC5,密钥交换算法有RSA和DH,摘要算法有MD5和SHA)等这些信息告诉服务器 B。 3. 服务器 B 知道这些信息后,然后确认一下双方的加密算法,”用DES-RSA-SHA这对组合好了“,**然后服务端也生成一个随机数 B ,并将随机数 B 和 CA 颁发给自己的证书一同返回给客户端 A。** 4. 客户端 A 得到 CA 证书后,会去校验该 CA 证书的有效性,校验方法在上面已经说过了。校验通过后,**客户端生成一个随机数3 ,然后用证书中的公钥加密随机数3 并传输给服务端 B 。** 5. 服务端 B 得到加密后的随机数3,然后利用私钥进行解密,得到真正的随机数3。 6. 最后,客户端 A 和服务端 B 都有**随机数1、随机数2、随机数3**,然后双方利用这三个随机数生成一个对话密钥。之后传输内容就是利用对话密钥来进行加解密了。这时就是利用了对称加密,一般用的都是 AES 算法,也就是上文约定好的协议。 7. 客户端 A 通知服务端 B ,指明后面的通讯用对话密钥来完成,同时通知服务器 B 客户端 A 的握手过程结束。 8. 服务端 B 通知客户端 A,指明后面的通讯用对话密钥来完成,同时通知客户端 A 服务器 B 的握手过程结束。 9. SSL 的握手部分结束,SSL 安全通道的数据通讯开始,客户端 A 和服务器 B 开始使用相同的对话密钥进行数据通讯。 到此,SSL 握手过程就讲完了。可能上面的流程太过于复杂,我们简单地来讲: 1. 客户端和服务端建立 SSL 握手,客户端通过 CA 证书来确认服务端的身份; 2. 互相传递三个随机数,之后通过这随机数来生成一个密钥; 3. 互相确认密钥,然后握手结束; 4. 数据通讯开始,都使用同一个对话密钥来加解密; ## 为什么一定要用三个随机数,来生成"会话密钥"? 答:不管是客户端还是服务器,都是下需要随机数,这样生成的密钥才不会每次都一样,由于SSL协议中证书是静态的,因此十分有必要引入一种随机因素来保证协商出的密钥的随机性。 对于RSA密钥交换算法来说,pre-master-key本身就是一个随机数,再加上第一步、第三步消息中的随机数,三个随机数通过一个密钥导出器最终导出一个对称密钥。pre master 的存在在于SSL协议不信任每一个主机都能产生完全的随机数,如果随机数不随机,那么pre master secret就可能被猜出来,那么仅适用于pre master secret作为密钥就不合适了,因此必须引入新的随机因素,那么客户端和服务器加上pre master secret三个随机数一同生成的密钥就不容易被猜出了,一个伪随机数可能完全不随机,但是三个伪随机就十分接近随机了,每增加一个自由度,随机性增加的可不是一。 ### 中间人攻击与https抓包 一个针对SSL的中间人攻击过程如下: ![](https://img.kancloud.cn/33/31/3331bae6206f6c66cc109cc44972154f_882x867.png) 中间人其实是做了一个偷梁换柱的动作,核心是如何欺骗客户端,从而让客户端能够放心的与中间人进行数据交互而没有任何察觉。我们来看Charles如何做到HTTPS抓包的,网上有很多fiddlers如何抓HTTPS包的教程,几步就搞定了,其中最核心的就是: **将私有CA签发的数字证书安装到手机中并且作为受信任证书保存** 当私有的CA证书添加到系统信任证书后,就可以完成证书链验证过程 fiddler抓包过程,详情可见:[www.cnblogs.com/afeng2010/p…](https://link.juejin.cn?target=https%3A%2F%2Fwww.cnblogs.com%2Fafeng2010%2Fp%2F10073446.html "https://www.cnblogs.com/afeng2010/p/10073446.html") #### android7.0之后用户CA限制 Android从7.0开始系统不再信任用户CA证书(应用targetSdkVersion >= 24时生效,如果targetSdkVersion = 24的应用的HTTPS包就抓不到了。 Android 6.0(API 23)及更低版本应用的默认网络安全性配置如下: ~~~java <!-- 默认允许所有明文通信 --> <base-config cleartextTrafficPermitted="true"> <trust-anchors> <!-- 信任系统预装 CA 证书 --> <certificates src="system" /> <!-- 信任用户添加的 CA 证书,Charles 和 Fiddler 抓包工具安装的证书属于此类 --> <certificates src="user" /> </trust-anchors> </base-config> ~~~ 而在 Android 7.0(API 24)到 Android 8.1(API 27)的默认网络安全性配置如下: ~~~java <!-- 默认允许所有明文通信 --> <base-config cleartextTrafficPermitted="true"> <trust-anchors> <!-- 信任系统预装 CA 证书 --> <certificates src="system" /> </trust-anchors> </base-config> ~~~ 而在 Android 9.0(API 28)及更高版本的默认网络安全性配置如下: ~~~java <!-- 默认禁止所有明文通信 --> <base-config cleartextTrafficPermitted="false"> <trust-anchors> <!-- 信任系统预装 CA 证书 --> <certificates src="system" /> </trust-anchors> </base-config> 复制代码 ~~~ 如果我们要抓自已APP的包,解决方式就是使用 Android 6.0 以下的网络安全性配置: 添加res/xml/network\_security\_config.xml: ~~~java <?xml version="1.0" encoding="utf-8"?> <network-security-config> <base-config cleartextTrafficPermitted="true"> <trust-anchors> <certificates src="system" /> <certificates src="user" /> </trust-anchors> </base-config> </network-security-config> ~~~ 然后在清单文件中指向该文件: ~~~java <?xml version="1.0" encoding="utf-8"?> <manifest ... > <application android:networkSecurityConfig="@xml/network_security_config" ... > ... </application> </manifest> ~~~ ### 如何防止中间人攻击 通过上面的讲解,大家可以知道 假设你的设备没有安装信任过来历不明的证书,那么不管在任何WIFI或者网络环境下,通常HTTPS通信都是安全的 并且在android7.0及以上,就算手机安装信任了证书,在一般情况下也是安全的,因为android7.0以上默认不信任用户证书 所以通常情况下,我们不需要做什么特别操作来防止中间人攻击 不过有部分金融类的APP需要额外校验来保证https的安全性 我们可以下载服务器端公钥证书,然后将公钥证书编译到Android应用中一般在assets文件夹保存,由应用在交互过程中去验证证书的合法性。 ~~~java //使用内置证书 public static SSLSocketFactory getSSLSocketFactory_Certificate(Context context) { try { //获取X.509格式的内置证书 CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null); Collection<? extends Certificate> cers = certificateFactory.generateCertificates(context.getAssets().open("CACertificate.cer")); int index = 0; for (Certificate certificate : cers) { String certificateAlias = Integer.toString(index++); keyStore.setCertificateEntry(certificateAlias, certificate); } //用这个KeyStore去引导生成的TrustManager来提供验证 final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(keyStore); //用TrustManager生成SSLContext SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom()); //SSLContext返回SSLSocketFactory return sslContext.getSocketFactory(); } catch (Exception e) { throw new RuntimeException(e); } } ~~~ ## 参考资料 [https加密原理](https://blog.csdn.net/fei20121106/article/details/84104843) [Android程序员需要了解的https与中间人攻击](https://juejin.cn/post/6880024440143347719)