TCP/IP系列(4)-SSL/TLS详解(1)

SSL/TLS详解(1)--安全规范

Posted by Jason Lee on 2020-03-23

概述

SSL/TSL 协议

**SSL(安全套接字层)**是一种标准安全协议,用于在在线通信中建立Web服务器和浏览器之间的加密链接。

TLS:(Transport Layer Security): 是SSL协议(Secure Sockets Layer)的升级版,TLS 1.0通常被标示为SSL 3.1,TLS 1.1为SSL 3.2,TLS 1.2为SSL 3.3。现在习惯将这个两个组合在一起称为SSL/TLS,它是一种用于加密的安全协议就好了。

发展史

  • 1995: SSL 2.0. 由 Netscape 提出,这个版本由于设计缺陷,并不安全,很快被发现有严重漏洞,已经废弃。
  • 1996: SSL 3.0. 写成 RFC,开始流行。目前(2015年)已经不安全,必须禁用。
  • 1999: TLS 1.0. 互联网标准化组织 ISOC 接替 NetScape 公司,发布了 SSL 的升级版 TLS 1.0 版。
  • 2006: TLS 1.1. 作为 RFC 4346 发布。主要 fix 了 CBC 模式相关的如 BEAST 攻击等漏洞。
  • 2008: TLS 1.2. 作为 RFC 5246 发布。增进安全性。目前(2015 年)应该主要部署的版本,请确保你使用的是这个版本。
  • 2018:8月10日 RFC8446 TLS 1.3 协议正式发布,它剔除了 TLS 1.2 协议中不安全的因素,极大地增强了协议的安全性和性能。

作用

TCP/IP 协议栈

不使用SSL/TLS的HTTP通信,就是不加密的通信。所有信息明文传播,带来了三大风险。

  • (1) 窃听风险(eavesdropping):第三方可以获知通信内容。
  • (2) 篡改风险(tampering):第三方可以修改通信内容。
  • (3) 冒充风险(pretending):第三方可以冒充他人身份参与通信。

SSL/TLS协议是为了解决这三大风险而设计的,希望达到:

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

互联网是开放环境,通信双方都是未知身份,这为协议的设计带来了很大的难度。而且,协议还必须能够经受所有匪夷所思的攻击,这使得SSL/TLS协议变得异常复杂。

加密算法

单项加密

单项加密,又称不可逆的加密
特性:

  • 定长输出: 无论原始数据是多大,结果大小都相同的
  • 雪崩效应: 输入的微小改变,将会引起结果的巨大改变
  • 单向加密算法:MD5(128位)、SHA1、SHA256、SHA384、SHA512

缺点: 雪崩,不可逆

主要用途: 特征码

对称加密

在对称加密算法中,数据发信方将明文(原始数据)和加密密钥一起经过特殊加密算法处理后,使其变成复杂的加密密文发送出去。

收信方收到密文后,若想解读原文,则需要使用加密用过的密钥及相同算法的逆算法对密文进行解密,在对称加密算法中,使用的密钥只有一个,发收信双方都使用这个密钥对数据进行加密和解密,这就要求解密方事先必须知道加密密钥和加密算法。

缺点: 不安全,局限性,需要知道对称加密秘钥

优点:加密解密速度快

公钥加密(非对称加密)

现代互联网普遍用的加密手段。

  • 特点:

    • 公钥公开,私钥保密
    • 公钥加密,私钥解密
    • 私钥加密,公钥解密
  • 用途: 接收方用发送方的公钥解密,若能解密就以为这这个数据一定是拥有该公钥对应的私钥的人发送的,实现了身份认证机制。

    • 数字签名
    • HTTPS/OPENSSL/SSL
    • 区块链
  • 公钥加密算法
    RSA、Elgamal、背包算法、Rabin、D-H、ECC(椭圆曲线加密算法)。
    使用最广泛的是RSA算法,Elgamal是另一种常用的非对称加密算法。

  • 缺点: 加密解密速度较慢,无法对大规模的数据进行加密
    一般情况下非对称加密用来数字签名的。

  • RSA 算法原理
    做个连接 不做过多解读 RSA 算法原理

  • ECC 算法
    Elliptic Curve Cryptography: a gentle introduction

数字签名

私钥是保密的,而公钥是公开的,用私钥加密,那相当于所有人都可以用公钥解密,在这个意义上来说,加密就毫无意义。

在实际应用的时候,签名实际上并不是针对原始消息,而是针对原始消息的哈希进行签名,也就是说对原始消息的哈希特征值进行加密,保证这个消息不会被其他第三方串改。

  • 数字签名的作用
    • 防止伪造;
    • 防止抵赖;
    • 检测篡改。
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
public static void main(String[] args) throws Exception {

// 生成RSA公钥/私钥:
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA");
kpGen.initialize(1024);
KeyPair kp = kpGen.generateKeyPair();
PrivateKey sk = kp.getPrivate();
PublicKey pk = kp.getPublic();

// 待签名的消息:
byte[] message = "Hello, I am Bob!".getBytes(StandardCharsets.UTF_8);

// 用私钥签名:
Signature s = Signature.getInstance("SHA1withRSA");
s.initSign(sk);
s.update(message);
byte[] signed = s.sign();
System.out.println(String.format("signature: %x", new BigInteger(1, signed)));

// 用公钥验证:
Signature v = Signature.getInstance("SHA1withRSA");
v.initVerify(pk);
v.update(message);
boolean valid = v.verify(signed);
System.out.println("valid? " + valid);

}

现代加密过程

非对称加密既然也有缺陷,那我们就将对称加密,非对称加密两者结合起来,取其精华、去其糟粕,发挥两者的各自的优势

如下图:

加密解密过程和原理详细说明:
1、发送端B :

  • (1)为保证安全,要对报文加密。加密方法有三类:对称加密、公钥加密和单向加密。对称加密不安全,单向加密是不可逆的,因而使用公钥加密。
    公钥加密安全(一般为2048位),但是加密过程太慢了,不适用当前网络需求

  • (2)为了解决上述问题,B可以用单向加密提取出报文的特征码(特征码能保证报文的数据完整性),再使用自身的私钥对特征码进行公钥加密(特征码数据小,对其进行公钥加密速度快),并把加密后的特征码附加到报文后。(使用私钥加密是为了验证身份)但是,这种方式能实现数据完整性和身份验证的检验,但是却缺失了报文的数据保密性

  • (3)为了解决上述问题,B在把加密的特征码附加到报文后,把特征码和报文当做一个数据(假设为data),使用对称加密算法对该数据(data)加密得出一个密码,再把密码附加到该数据(data)后。为了使得在传输过程中密码不被其他人获取或篡改,使用A的公钥对密码进行加密(只有A的私钥能对其解密),把加密的密码附加到数据data后,再这些数据一并发送给A。

对称加密算法有很多种,那么如何选择加密算法呢? 这是因为前面会有个加密算法协商的过程,客户端和服务器各自支持的加密算法不一样,所有在数据加密传输过程中工会有加密算法协商的过程

2、接收端A:

  • (1)A接收到B传来的报文,利用自身的私钥对其解密,获得密码。因为只有A的私钥能对B传来的报文(使用A的公钥加密密码)解密,所以能防止其他人对该传输的报文进行解密而获得其中的信息,保证了数据的保密性。
  • (2)A利用获得的密码解密其中对称加密的数据,获得经过加密的特征码和原报文。
  • (3)A使用B的公钥对该特征码解密,能解密则说明该报文是B发送过来的,实现了身份验证。(假设解密后的特征码是fcode)
  • (4)A使用同等单向加密算法对接收到的原报文提取其特征码。使用该特征码和解密后获得的特征码(fcode)做比较,如果一样,则说明原报文的数据完整。

以上这种方式能保证数据完整性、身份验证和数据的保密性,在加密和解密的过程中都要用到对方的公钥,如何在传输过程中安全可靠地获得对方的公钥就成了关键的一环

遗留问题

  • 1. 公钥如何安全的获取服务器公钥?
  • 2. 对称加密的秘钥如何生成,加密算法如何协商?

要解决三个问题必须要客户端和服务器进行协商,协商出双方一致的秘钥算法。所以,客户端和服务器必须有一个完整的秘钥交换机制。

秘钥交换

直接秘钥交换

由于公钥公开,所以当客户端连接服务器的时候,服务器会给客户端发自己公钥,然后客户端用公钥加密密码(也就是对称秘钥),发送给服务器,服务器有私钥,可以解密数据。这样就实现了数据加密的传输。 这种秘钥交换在https 里映射为对称秘钥的交换

虽然公钥是公开的,但是也并不意味着和所有人都可以拥有公钥,公钥的公开是指公钥可以在网络上传输。
如果你能保证公钥和私钥一样,能安全的进行交换,那么双方的传输安全是没有问题。一般情况下为人肉传输/线下传输。比如:公钥放到专用的安全U盘上,然后在去服务器传输。

漏洞: 公钥会暴露给中间人。

原因:秘钥在网络上传输导致的。但是很多时候,无法进行线下传输,秘钥交换必不可少。

实现:RSA秘钥交换算法

DH算法解决秘钥交换漏洞

如果要解决中间人替换漏洞:Diffie-Hellman算法应运而生。
DH算法解决了密钥在双方不直接传递密钥的情况下完成密钥交换,这个神奇的交换原理完全由数学理论支持。

DH算法的过程

我们来看DH算法交换密钥的步骤。假设甲乙双方需要传递密钥,他们之间可以这么做:

  • 1、通信方A和通信方B约定一个初始数 g,如g=5,一个质数p,如p=23,g和p是公开的,且1< g < p

  • 2、Alice生成一个随机数aa是保密的,如a=6

  • 3、Alice 计算 A=g^a%p 发送给B 即:g^a%p=5^6%23=8

  • 4、B生成一个随机数b,b是保密的,如 b=15

  • 5、Bob计算B=g^b%p发送给A,B=g^b%p=5^15%23=19

  • 6、A接收到g^b%p后,再使用保密的a,计算(g^b%p)^a%p=19^6%23=2

  • 7、B接收到g^a%p后,再使用保密的b,计算(g^a%p)^b%p=8^15%23=2

  • 算法实现如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let { createDiffieHellman } = require('crypto')

let client = createDiffieHellman(512) // 内部已经有随机值a
let clientKey = client.generateKeys(); // 生成A
let prime = client.getPrime(); // 生成 p
let generator = client.getGenerator() // 生成g

// 第一次传输 传输prime(p) 和 generator(g) 和 clientKey (A

let server = createDiffieHellman(prime, generator) // 内部生成随机值b 保存 p 和 g
let serverKey = server.generateKeys() // 生成B

//第二次传输 服务传输 serverKey (B)

// 计算 B^a%p
let secretClient = client.computeSecret(serverKey)
// 计算 A^b%p
let secretServer = server.computeSecret(clientKey)
// secretServer == secretClient
console.log(secretClient.toString("hex"))
console.log(secretServer.toString("hex"))

解决:秘钥不用再公开传输
详见:Diffie–Hellman_key_exchange

ECC 算法就是DH算法的一个实现

秘钥交换协议的常用实现

RSA密钥交换算法(直接交换)

  • (1):任意客户端对服务器发起请求,服务器首先发回复自己的公钥到客户端(公钥明文传输)。

  • (2):客户端使用随机数算法,生成一个密钥S,使用收到的公钥进行 加密,生成C,把C发送到服务器。

  • (3):服务器收到C,使用公钥对应的私钥进行解密,得到S。

  • (4):上述交换步骤后,客户端和服务器都得到了S,S为密钥(预主密钥)。

一般预备主密钥还要经过一个随机数及其序列号生成主密钥,用于防止重放攻击使用

DHE算法流程文字描述如下:

DHE算法流程文字描述如下:

  • (1):客户端计算一个随机值Xa,使用Xa作为指数,即计算Pa = q^Xa mod p,其中q和p是全世界公认的一对值。客户端把Pa发送至服务器,Xa作为自己私钥,仅且自己知道。

  • (2):服务器和客户端计算流程一样,生成一个随机值Xb,使用Xb作为指数,计算   Pb = q^Xb mod p,将结果Pb发送至客户端,Xb仅自己保存。

  • (3):客户端收到Pb后计算Sa = Pb ^Xa mod p;服务器收到Pa后计算Sb = Pa^Xb mod p

  • (4):算法保证了Sa = Sb = S,故密钥交换成功,S为密钥(预主密钥)。

上述密钥交换流程中,和RSA密钥交换有较大不同,DHE密钥交换时,服务器私钥没有参与进来。也就是说,私钥即使泄漏,也不会导致会话加密密钥S被第三方解密。

实际使用过程中,私钥的功能被削弱到用来身份认证(上图中没有画出)。

上图中DHE参数和Pb都是通过server key exchange发送给客户端,Pa通过client key exchange发送给服务器。server key exchange的结尾处需要使用服务器私钥对该报文本身进行签名,以表明自己拥有私钥(图中为了表明私钥没有参与密钥计算,没有画出,但不影响理解DHE算法)。

ECDHE or ECDH 密钥交换算法

只要理解DHE密钥交换原理,那么理解ECDHE密钥交换原理其实并不难(如果不想深究的话)。
ECDHE的运算是把DHE中模幂运算替换成了点乘运算,速度更快,可逆更难。

ECDHE算法流程文字描述如下:

  • (1):客户端随机生成随机值Ra,计算Pa(x, y) = Ra * Q(x, y),Q(x, y)为全世界公认的某个椭圆曲线算法的基点。将Pa(x, y)发送至服务器。

  • (2):服务器随机生成随机值Rb,计算Pb(x,y) - Rb * Q(x, y)。将Pb(x, y)发送至客户端。

  • (3):客户端计算Sa(x, y) = Ra * Pb(x, y);服务器计算Sb(x, y) = Rb *Pa(x, y)

  • (4):算法保证了Sa = Sb = S,提取其中的S的x向量作为密钥(预主密钥)。

ECDHE与ECDH算法的区别
字面少了一个E,E代表了“临时”,即在握手流程中,作为服务器端,ECDH少了一步计算Pb的过程,Pb用证书中的公钥代替,而证书对应的私钥就是Xb。由此可见,使用ECDH密钥交换算法,服务器必须采用ECC证书;服务器不发送server key exchange报文,因为发送certificate报文时,证书本身就包含了Pb信息。

ECDHE与RSA的区别
    ECDHE(DHE)算法属于DH类密钥交换算法, 私钥不参与密钥的协商,故即使私钥泄漏,客户端和服务器之间加密的报文都无法被解密,这叫 前向安全(forward secrity)。由于ECDHE每条会话都重新计算一个密钥(Ra、Rb),故一条会话被解密后,其他会话仍旧安全。(随机数 + 会话序列帧)

然而,ECDH算法服务器端的私钥是固定的,即证书的私钥作为Rb,故ECDH不被认为前向安全,因为私钥泄漏相当于Rb泄漏,Rb泄漏,导致会话密钥可被第三方计算。ECDH交换算法已经被OpenSSL废

中间人劫持-Man-In-The-Middle attack MITM。

中间人攻击

mitmproxy模拟中间人挟持

解决:当发送方要发送公钥的时候,不发送公钥,而是发送一个数字证书,证书中会包含一些具体信息。接收方收到证书后验证证书真伪,然后获取证书内的公钥。
所以安全可靠地获取对方的公钥靠CA(Certificate Authority )证书授权中心来实现。

CA证书体系

CA是什么?

上面提到的数字证书就是CA发行的。CA是Certificate Authority的缩写,也叫“证书授权中心”。它是负责管理和签发证书的第三方机构,作用是检查证书持有者身份的合法性,并签发证书,以防证书被伪造或篡改。

所以,CA实际上是一个机构,负责“证件”印制核发。就像负责颁发身份证的公安局、负责发放行驶证、驾驶证的车管所。

CA证书的信任链

实际上,证书之间的信任关系,是可以嵌套的。比如,C 信任 A1,A1 信任 A2,A2 信任 A3…这个叫做证书的信任链。只要你信任链上的头一个证书,那后续的证书,都是可以信任滴。
 

处于最顶上的树根位置的那个证书,就是“根证书”。除了根证书,其它证书都要依靠上一级的证书,来证明自己。那谁来证明“根证书”可靠捏?实际上,根证书自己证明自己是可靠滴(或者换句话说,根证书是不需要被证明滴)。
  聪明的同学此刻应该意识到了:根证书是整个证书体系安全的根本。所以,如果某个证书体系中,根证书出了问题(不再可信了),那么所有被根证书所信任的其它证书,也就不再可信了。

CA证书的结构

  • 1、CA证书标准:x.509
    x.509: 定义了证书结构和认证协议标准;(基于公钥和数字签名)
    用于:IP安全、TLS/SSL(传输层安全)和S/MIME(安全电子邮件通信)

x.509证书标准详细说明:

  • (1)版本号(默认为1,如果有多个扩展,可能为3)
  • (2)证书序列号(是一个整数,在CA中唯一标识,表明发行了多少个证书)
  • (3)算法参数 (标志用了那种算法)
  • (4)发行者的名称(CA自己的名字)
  • (5)有效期限
  • (6)主体名称(证书拥有者名称)(很关键!!!)(个人用户使用的是个人用户名,主机使用的必须是主机名而不是ip地址)
  • (7)公钥(最重要)(公钥由证书拥有者提供)
  • (8)发行者的ID(CA的唯一编号)
  • (9)主体的ID(CA生成的证书拥有者唯一编号)
  • (10)扩展
  • (11)CA的签名(用于验证CA的来源合法性) CA是相对于发送方B和接收方A的第三方,是具有公信力的机构。

证书的验证过程

假设你想要成为一个受信任的网站或机构,只需要找你的上级机构去颁发证书给你

  • 颁发时你将自己的公钥,host等信息发送到颁发机构,该机构会将自己的证书附上你的信息,并用自己的私钥签名,做成一张新的证书发给你;而这个上级机构他的证书又是同样的方法由CA颁发的。

  • 首先你的证书会在https握手过程中被传递到浏览器,浏览器从你的证书中找到了颁发者,从颁发者的证书(如果你电脑上有的话)又找到了CA的证书(CA证书会在操作系统安装时就安装好,所以每个人电脑上都有根证书),使用CA证书中带的公钥来对颁发者证书做验签,一旦匹配,说明你电脑上的颁发者证书不是伪造的

  • 颁发者证书的公钥,解密你的证书的特征码,然后再用收到的证书中的加密算法,再使用同样的单向加密算法提取收到证书的特征码,比较这两个特征码是否一样,如果一样,则表示获得你的的数字证书是完整的。

  • 从证书中获取你的公钥

遗留问题

通过我们前面的认识,我们来回答下了几个问题

  • 1. 公钥如何安全的获取: 通过CA证书的方式来获取 解决中间人攻击
  • 2. 对称加密的秘钥如何生成/获取 通过DH算法来获取(秘钥协商)

那么我们接下来就要讨论一下,在应用层面上来说,服务器客户端双方怎么能快速的达成秘钥的交换和生成。
如果要交换秘钥,TLS在连接之前会进行握手操作,该操作用于交换秘钥,协商加密算法等等,用于建立安全的连接。

参考



支付宝打赏 微信打赏

赞赏一下