@wxy
This commit is contained in:
Xingyu Wang 2020-08-13 15:06:09 +08:00
parent 4d19ccd366
commit 7b2da03b5d

View File

@ -1,6 +1,6 @@
[#]: collector: (lujun9972)
[#]: translator: (wxy)
[#]: reviewer: ( )
[#]: reviewer: (wxy)
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (How to use OpenSSL: Hashes, digital signatures, and more)
@ -12,27 +12,25 @@
> 通过 OpenSSL 深入了解密码学的细节:哈希值、数字签名、数字证书等。
![A person working.][1]
![](https://img.linux.net.cn/data/attachment/album/202008/13/150012t7zwwk6x7wr69wih.jpg)
[本系列的第一篇文章][2]通过 OpenSSL 库和命令行实用程序介绍了哈希、加密/解密、数字签名和数字证书。这第二篇文章将对细节进行深入探讨。让我们从计算中无处不在的哈希开始,并考虑是什么使哈希函数*加了密*。
[本系列的第一篇文章][2]通过 OpenSSL 库和命令行实用程序介绍了哈希、加密/解密、数字签名和数字证书。这第二篇文章将对细节进行深入探讨。让我们从计算中无处不在的哈希开始,并考虑是什么使哈希函数*具备密码学意义*。
### 密码学哈希
OpenSSL 源代码的[下载页面](https://www.openssl.org/source/)包含了一个带有最新版本的表格。每个版本都有两个<ruby>哈希值<rt>hash</rt></ruby>160 位 SHA1 和 256 位 SHA256。这些值可以用来验证下载的文件是否与存储库中的原始文件相匹配下载者在本地重新计算下载文件的哈希值然后将结果与原始文件进行比较。现代系统有计算这种哈希值的实用程序。例如Linux有 `md5sum``sha256sum`。OpenSSL 本身也提供类似的命令行实用程序。
OpenSSL 源代码的[下载页面](https://www.openssl.org/source/)包含了一个带有最新版本的表格。每个版本都有两个<ruby>哈希值<rt>hash</rt></ruby>160 位 SHA1 和 256 位 SHA256。这些值可以用来验证下载的文件是否与存储库中的原始文件相匹配下载者在本地重新计算下载文件的哈希值然后将结果与原始文件进行比较。现代系统有计算这种哈希值的实用程序。例如Linux `md5sum``sha256sum`。OpenSSL 本身也提供类似的命令行实用程序。
哈希值被用于计算的许多领域。例如,比特币区块链使用 SHA256 哈希值作为区块标识符。挖比特币就是生成一个低于指定阈值的 SHA256 哈希值,也就是至少有 N 个前导零的哈希值。N 的值可以上升或下降,这取决于特定时间的挖矿生产力)。作为一个兴趣点,今的矿机是为并行生成 SHA256 哈希值而设计的硬件集群。在 2018 年的一个高峰期,全球的比特币矿工每秒产生约 7500 万个太哈希值 —— 这真是一个不可思议的数字。
哈希值被用于计算的许多领域。例如,比特币区块链使用 SHA256 哈希值作为区块标识符。挖比特币就是生成一个低于指定阈值的 SHA256 哈希值,也就是至少有 N 个前导零的哈希值。N 的值可以上升或下降,这取决于特定时间的挖矿生产力)。作为一个兴趣点,今的矿机是为并行生成 SHA256 哈希值而设计的硬件集群。在 2018 年的一个高峰期,全球的比特币矿工每秒产生约 7500 万个<ruby>太哈希值<rt>terahash</rt></ruby> —— 这真是一个不可思议的数字。
网络协议也使用哈希值(在这里通常叫做“<ruby>校验和<rt>checksum</rt></ruby>”)来支持消息的完整性;也就是说,保证收到的消息与发送的消息是一样的。消息发送者计算消息的校验和,并将结果与消息一起发送。当消息到达时,接收方重新计算校验和。如果发送的校验和和重新计算的校验和不一致,那么消息在传输过程中发生了一些事情,或者发送的校验和发生了一些事情,或者两者都发生了。在这种情况下,应该重新发送消息和它的校验和,或者至少应该触发一个错误条件。(低级网络协议,如 UDP理会校验和。)
网络协议也使用哈希值(在这里通常叫做“<ruby>校验和<rt>checksum</rt></ruby>”)来支持消息的完整性;也就是说,保证收到的消息与发送的消息是一样的。消息发送者计算消息的校验和,并将结果与消息一起发送。当消息到达时,接收方重新计算校验和。如果发送的校验和与重新计算的校验和不一致,那么消息在传输过程中可能出现了一些问题,或者发送的校验和出现了问题,或者两者都出现了问题。在这种情况下,应该重新发送消息和它的校验和,或者至少应该触发一个错误情况。(如 UDP 这样的低级网络协议不会理会校验和。)
哈希的其他例子大家都很熟悉。比如一个网站,要求用户用密码进行验证,用户在浏览器中输入密码,然后,他们的密码通过 HTTPS 连接到服务器,从浏览器加密发送到服务器。一旦密码到达服务器,就会被解密,然后进行数据库表的查询。
哈希的其他例子大家都很熟悉。比如一个网站,要求用户用密码进行验证,用户在浏览器中输入密码,然后,他们通过 HTTPS 连接到服务器,密码从浏览器加密发送到服务器。一旦密码到达服务器,就会被解密,然后进行数据库表的查询。
在这个查询表中应该存储什么?存储密码本身是有风险的。风险要小得多的是存储一个由密码生成的哈希值,也许在计算哈希值之前加一些<ruby><rt>salt</rt></ruby>(额外的位)到里面。你的密码可能会被发送到 Web 服务器上,但网站可以向你保证,密码不会存储在那里。
在这个查询表中应该存储什么?存储密码本身是有风险的。风险要小得多的方式是存储一个由密码生成的哈希值,也许在计算哈希值之前加一些<ruby><rt>salt</rt></ruby>(额外的位)改善口味”。你的密码可能会被发送到 Web 服务器上,但网站可以向你保证,密码不会存储在那里。
哈希值还出现在安全的各个领域。例如,<ruby>基于哈希值的消息认证码<rt>hash-based message authentication code</rt></ruby>[HMAC][3]使用一个哈希值和一个秘密的加密密钥来认证通过网络发送的消息。HMAC 码轻量级且易于在程序中使用,在 Web
服务中很受欢迎。一个 X509 数字证书包括一个称为<ruby>指纹<rt>fingerprint</rt></ruby>的哈希值,它可以方便证书验证。一个存放于内存中的<ruby>可信存储<rt>truststore</rt></ruby>可以实现为一个以这种指纹为键的查找表 —— 作为一个支持恒定查找时间的<ruby>哈希映射<rt>hash map</rt></ruby>。来自传入的证书的指纹可以与可信存储中的密钥进行比较,以确定是否匹配。
<ruby>密码学哈希<rt>cryptographic hash</rt></ruby>函数应该有什么特殊属性?它应该是<ruby>单向<rt>one-way</rt></ruby>的,这意味着很难被逆转。一个加密哈希函数应该是比较容易计算的,但是计算它的反函数(将哈希值映射回输入位串的函数)在计算上应该是困难的。下面是一个描述,用 `chf` 作为加密哈希函数,我的密码 `foobar` 作为样本输入。
哈希值还出现在安全的各个领域。例如,<ruby>基于哈希值的消息认证码<rt>hash-based message authentication code</rt></ruby>[HMAC][3])使用一个哈希值和一个秘密的<ruby>加密密钥<rt>cryptographic key</rt></ruby>来认证通过网络发送的消息。HMAC 码轻量级且易于在程序中使用,在 Web 服务中很受欢迎。一个 X509 数字证书包括一个称为<ruby>指纹<rt>fingerprint</rt></ruby>的哈希值,它可以方便证书验证。一个存放于内存中的<ruby>可信存储<rt>truststore</rt></ruby>可以实现为一个以这种指纹为键的查找表 —— 作为一个支持恒定查找时间的<ruby>哈希映射<rt>hash map</rt></ruby>。来自传入的证书的指纹可以与可信存储中的密钥进行比较,以确定是否匹配。
<ruby>密码学哈希函数<rt>cryptographic hash function</rt></ruby>应该具有什么特殊属性?它应该是<ruby>单向<rt>one-way</rt></ruby>的,这意味着很难被逆转。一个加密哈希函数应该是比较容易计算的,但是计算它的反函数(将哈希值映射回输入位串的函数)在计算上应该是困难的。下面是一个描述,用 `chf` 作为加密哈希函数,我的密码 `foobar` 作为样本输入。
```
+---+
@ -42,18 +40,17 @@ foobar—>|chf|—>hash value ## 简单直接
相比之下,逆向操作是不可行的:
```
+-----------+
hash value—>|chf inverse|—>foobar ## 棘手困难
+-----------+
```
例如,回忆一下 SHA256 哈希函数。对于一个任意长度为 N > 0 的输入位串,这个函数会生成一个 256 位的固定长度的哈希值;因此,这个哈希值甚至不会反映出输入位串的长度 N更不用说字符串中每个位的值了。顺便说一下SHA256 不容易受到<ruby>[长度扩展攻击][4]<rt>length extension attack</rt></ruby>。唯一有效的逆向工程方法是通过蛮力搜索将计算出的 SHA256 哈希值逆向返回到输入位串,这意味着尝试所有可能的输入位串,直到找到与目标哈希值匹配的位串。这样的搜索在 SHA256 这样一个完善的加密哈希函数上是不可行的。
例如,回忆一下 SHA256 哈希函数。对于一个任意长度为 N > 0 的输入位串,这个函数会生成一个 256 位的固定长度的哈希值;因此,这个哈希值甚至不会反映出输入位串的长度 N更不用说字符串中每个位的值了。顺便说一下SHA256 不容易受到<ruby>[长度扩展攻击][4]<rt>length extension attack</rt></ruby>。唯一有效的逆向工程方法是通过蛮力搜索将计算出的 SHA256 哈希值逆向返回到输入位串,这意味着需要尝试所有可能的输入位串,直到找到与目标哈希值匹配的位串。这样的搜索在 SHA256 这样一个完善的加密哈希函数上是不可行的。
现在,最后一个回顾的知识点是有序。加密哈希值是统计学上的唯一,而不是无条件的唯一,这意味着两个不同的输入位串产生相同的哈希值是不太可能的,但也不是不可能的 —— 这称之为<ruby>碰撞<rt>collision</rt></ruby>。[生日问题][5]提供了一个很好的反直觉的碰撞例子。对各种哈希算法的<ruby>抗碰撞性<rt>collision resistance</rt></ruby>有着广泛的研究。例如MD5128 位哈希值)在大约 221 次哈希之后,抗碰撞能力就会崩溃。对于 SHA1160 位哈希值),大约在 261 次散列后开始崩溃。
现在,最后一个回顾的知识点是<ruby>有序<rt>in order</rt></ruby>。加密哈希值是统计学上的唯一,而不是无条件的唯一,这意味着两个不同的输入位串产生相同的哈希值是不太可能的,但也不是不可能的 —— 这称之为<ruby>碰撞<rt>collision</rt></ruby>。[生日问题][5]提供了一个很好的反直觉的碰撞例子。对各种哈希算法的<ruby>抗碰撞性<rt>collision resistance</rt></ruby>有着广泛的研究。例如MD5128 位哈希值)在大约 2^21 次哈希之后,抗碰撞能力就会崩溃。对于 SHA1160 位哈希值),大约在 2^61 次哈希后开始崩溃。
对于 SHA256 的抗碰撞能力的剖析目前还没有一个很好的估计。这个事实并不奇怪。SHA256 有 2256 个不同的哈希值范围,这个数字的十进制表示法有 78 位之多! 那么SHA256 哈希会不会发生碰撞呢?当然可能,但可能性极小。
对于 SHA256 的抗碰撞能力的剖析目前还没有一个很好的估计。这个事实并不奇怪。SHA256 有 2^256 个不同的哈希值范围,这个数字的十进制表示法有 78 位之多! 那么SHA256 哈希会不会发生碰撞呢?当然可能,但可能性极小。
在下面的命令行示例中,有两个输入文件被用作位串源:`hashIn1.txt` 和 `hashIn2.txt`。第一个文件包含 `abc`,第二个文件包含 `1a2b3c`
@ -87,21 +84,21 @@ SHA256(hashIn2.txt)= 3eaac518777682bf4e8840dd012c0b104c2e16009083877675f00e99590
让我们来了解一下数字签名是如何创建的。如前所述,没有公钥和私钥对就没有数字签名。当使用 OpenSSL 创建这些密钥时,有两个独立的命令:一个是创建私钥,另一个是从私钥中提取匹配的公钥。这些密钥对用 base64 编码,在这个过程中可以指定它们的大小。
<ruby>私钥<rt>private key</rt></ruby>由数值组成,其中两个数值(一个<ruby>模数<rt>modulus</rt></ruby>和一个<ruby>指数<rt>exponent</rt></ruby>)组成了公钥。虽然私钥文件包含了<ruby>公钥<rt>public key</rt></ruby>,但提取出来的公钥并**不**透露相应私钥的值。
<ruby>私钥<rt>private key</rt></ruby>由数值组成,其中两个数值(一个<ruby>模数<rt>modulus</rt></ruby>和一个<ruby>指数<rt>exponent</rt></ruby>)组成了公钥。虽然私钥文件包含了<ruby>公钥<rt>public key</rt></ruby>,但提取出来的公钥并**不**透露相应私钥的值。
因此,生成的带有私钥的文件包含了完整的密钥对。将公钥提取到自己的文件中是很实用的,因为这两把钥匙有不同的用途,这种提取方式也将私钥可能被意外公开的危险降到最低。
因此,生成的带有私钥的文件包含了完整的密钥对。将公钥提取到自己的文件中是很实用的,因为这两把钥匙有不同的用途,这种提取方式也将私钥可能被意外公开的危险降到最低。
接下来,这对密钥的私钥被用来生成目标工件(如电子邮件)的哈希值,从而创建签名。在另一端,接收者的系统使用这对密钥的公钥来验证附在工件上的签名。
现在举个例子。首先,用 OpenSSL 生成一个 2048 位的 RSA 密钥对
现在举个例子。首先,用 OpenSSL 生成一个 2048 位的 RSA 密钥对
```
openssl genpkey -out privkey.pem -algorithm rsa 2048
```
在这个例子中,我们可以舍去 `-algorithm rsa` 标志,因为 `genpkey` 默认为 RSA 类型。文件的名称(`privkey.pem`)是任意的,但是<ruby>隐私增强邮件<rt>Privacy Enhanced Mail</rt></ruby>PEM扩展名 `.pem` 是默认 PEM 格式的惯用格式如果需要的话OpenSSL 有命令可以在各种格式之间进行转换。)如果需要更大的密钥大小(例如 4096那么 最后一个参数 `2048` 可以改成 `4096`。这些大小总是二的幂。
在这个例子中,我们可以舍去 `-algorithm rsa` 标志,因为 `genpkey` 默认为 RSA 类型。文件的名称(`privkey.pem`)是任意的,但是<ruby>隐私增强邮件<rt>Privacy Enhanced Mail</rt></ruby>PEM扩展名 `.pem` 是默认 PEM 格式的惯用扩展名如果需要的话OpenSSL 有命令可以在各种格式之间进行转换。)如果需要更大的密钥大小(例如 4096那么最后一个参数 `2048` 可以改成 `4096`。这些大小总是二的幂。
下面是产生的 `privkey.pem` 文件的一个片断,它是 base64 编码的
下面是产生的 `privkey.pem` 文件的一个片断,它是 base64 编码的
```
-----BEGIN PRIVATE KEY-----
@ -134,13 +131,13 @@ O0xnAKpAvysMy7G7qQIDAQAB
openssl dgst -sha256 -sign privkey.pem -out sign.sha256 client.c
```
`client.c` 源文件的摘要是 SHA256私钥在前面创建的 `privkey.pem` 文件中。由此产生的二进制签名文件是 `sign.sha256`,这是一个任意的名字。要得到这个文件的可读版本(比如 base64后续命令是
`client.c` 源文件的摘要是 SHA256私钥在前面创建的 `privkey.pem` 文件中。由此产生的二进制签名文件是 `sign.sha256`,这是一个任意的名字。要得到这个文件的可读版本(比如 base64后续命令是
```
openssl enc -base64 -in sign.sha256 -out sign.sha256.base64
```
文件 `sign.sha256.base64` 现在包含:
文件 `sign.sha256.base64` 现在包含如下内容
```
h+e+3UPx++KKSlWKIk34fQ1g91XKHOGFRmjc0ZHPEyyjP6/lJ05SfjpAJxAPm075
@ -148,7 +145,7 @@ VNfFwysvqRGmL0jkp/TTdwnDTwt756Ej4X3OwAVeYM7i5DCcjVsQf5+h7JycHKlM
o/Jd3kUIWUkZ8+Lk0ZwzNzhKJu6LM5KWtL+MhJ2DpVc=
```
或者,可执行文件 `client` 也可以被签名,由此产生的 base64 编码签名将预期的不同:
或者,可执行文件 `client` 也可以被签名,由此产生的 base64 编码签名将预期的不同:
```
VMVImPgVLKHxVBapJ8DgLNJUKb98GbXgehRPD8o0ImADhLqlEKVy0HKRm/51m9IX
@ -217,7 +214,7 @@ Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:ba:36:fb:57:17:65:bc:40:30:96:1b:6e🇩🇪73:
00:ba:36:fb:57:17:65:bc:40:30:96:1b:6e:de:73:
Exponent: 65537 (0x10001)
Attributes:
@ -228,7 +225,7 @@ Signature Algorithm: sha256WithRSAEncryption
### 自签证书
在开发 HTTPS 网站的过程中,手头有一个不用经过 CA 程的数字证书是很方便的。在 HTTPS 握手的认证阶段,<ruby>自签证书<rt>self-signed certificate</rt></ruby>就能满足要求,尽管任何现代浏览器都会警告说这样的证书毫无价值。继续这个例子,自签证书的 OpenSSL 命令(有效期为一年,使用 RSA 公钥)如下:
在开发 HTTPS 网站的过程中,手头有一个不用经过 CA 程的数字证书是很方便的。在 HTTPS 握手的认证阶段,<ruby>自签证书<rt>self-signed certificate</rt></ruby>就能满足要求,尽管任何现代浏览器都会警告说这样的证书毫无价值。继续这个例子,自签证书的 OpenSSL 命令(有效期为一年,使用 RSA 公钥)如下:
```
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:4096 -keyout myserver.pem -out myserver.crt
@ -273,21 +270,21 @@ Signature Algorithm: sha256WithRSAEncryption
...
```
如前所述RSA 私钥包含的值是用来生成公钥的。但是,给定的公钥**不**泄露匹配的私钥。关于底层数学理论的介绍,见 <https://simple.wikipedia.org/wiki/RSA_algorithm>
如前所述RSA 私钥包含的值是用来生成公钥的。但是,给定的公钥**不**泄露匹配的私钥。关于底层数学理论的介绍,见 <https://simple.wikipedia.org/wiki/RSA_algorithm>
数字证书与用于生成证书的密钥对之间存在着重要的对应关系,即使证书只是自签的:
数字证书与用于生成证书的密钥对之间存在着重要的对应关系,即使证书只是自签的:
* 数字证书包含构成公的指数和模数值。这些值是最初生成的 PEM 文件中密钥对的一部分,在本例中,是文件 `myserver.pem`
* 指数几乎都是 65,537本例中),所以可以忽略。
* 密钥对的模数应该与数字证书的模数相匹配。
* 数字证书包含构成公钥的指数和模数值。这些值是最初生成的 PEM 文件中密钥对的一部分,在本例中,是文件 `myserver.pem`
* <ruby>指数<rt>exponent</rt></ruby>几乎总是 65,537本例中),所以可以忽略。
* 密钥对的<ruby>模数<rt>modulus</rt></ruby>应该与数字证书的模数相匹配。
模数是一个很大的值,为了便于阅读,可以进行哈希处理。下面是两个 OpenSSL 命令,它们检查相同的模数,从而确认数字证书是基于 PEM 文件中的密钥对。
```
% openssl x509 -noout -modulus -in myserver.crt | openssl sha1 ## modulus from CRT
% openssl x509 -noout -modulus -in myserver.crt | openssl sha1 ## 证书中的模数
(stdin)= 364d21d5e53a59d482395b1885aa2c3a5d2e3769
% openssl rsa -noout -modulus -in myserver.pem | openssl sha1 ## modulus from PEM
% openssl rsa -noout -modulus -in myserver.pem | openssl sha1 ## 密钥中的模数
(stdin)= 364d21d5e53a59d482395b1885aa2c3a5d2e3769
```
@ -295,20 +292,20 @@ Signature Algorithm: sha256WithRSAEncryption
### 回到密钥分发问题上
让我们回到第一部分末尾提出的一个问题:`client` 程序和 Google Web 服务器之间的 TLS 握手。握手协议有很多种,即使是`client` 例子中工作的 Diffie-Hellman 版本也有回旋余地。尽管如此,`client` 例子遵循了一个共同的模式。
让我们回到第一部分末尾提出的一个问题:`client` 程序和 Google Web 服务器之间的 TLS 握手。握手协议有很多种,即使是用在 `client` 例子中的 Diffie-Hellman 版本也有不同的方式。尽管如此,`client` 例子遵循了一个共同的模式。
首先,在 TLS 握手过程中,`client` 程序和 Web 服务器就<ruby>加密套件<rt>cipher suite</rt></ruby>达成一致,其中包括要使用的算法。在本例中,该套件是 `ECDHE-RSA-AES128-GCM-SHA256`
现在感兴趣的两个要素是 RSA 密钥对算法和 AES128 块密码,用于在握手成功的情况下对消息进行加密和解密。关于加密/解密,这个过程有两种风味<ruby>对称<rt>symmetric</rt></ruby><ruby>非对称<rt>asymmetric</rt></ruby>。在对称风味中,加密和解密使用的是*相同的*密钥,这首先就提出了<ruby>密钥分发问题<rt>key distribution problem</rt></ruby>。如何将密钥安全地分发给双方?在非对称风味中,一个密钥用于加密(在这种情况下,是 RSA 公钥),但另一个密钥用于解密(在这种情况下,是来自同一对密钥的 RSA 私钥)。
现在值得关注的两个要素是 RSA 密钥对算法和 AES128 块密码,用于在握手成功的情况下对消息进行加密和解密。关于加密/解密,这个过程有两种流派<ruby>对称<rt>symmetric</rt></ruby><ruby>非对称<rt>asymmetric</rt></ruby>。在对称流派中,加密和解密使用的是*相同的*密钥,这首先就引出了<ruby>密钥分发问题<rt>key distribution problem</rt></ruby>。如何将密钥安全地分发给双方?在非对称流派中,一个密钥用于加密(在这种情况下,是 RSA 公钥),但另一个密钥用于解密(在这种情况下,是来自同一对密钥的 RSA 私钥)。
`client` 程序拥有来认证证书的 Google Web 服务器的公钥,而 Web 服务器拥有来自同一对密钥的私钥。因此,`client` 程序可以向 Web 服务器发送加密信息,而 Web 服务器可以单独对该通信进行解密。
在 TLS 的情况下,对称方式有两个显著的优势:
* 在 `client` 程序与 Google Web 服务器之间的互动中认证是单向的。Google Web 服务器向 `client` 程序发送三张证书,但 `client` 程序并没有向 Web 服务器发送证书,因此,网络服务器没有来自客户端的公钥,无法对客户端的消息进行加密
* 在 `client` 程序与 Google Web 服务器之间的互动中认证是单向的。Google Web 服务器向 `client` 程序发送三张证书,但 `client` 程序并没有向 Web 服务器发送证书,因此,Web 服务器没有来自客户端的公钥,无法加密发给客户端的消息
* 使用 AES128 的对称加密/解密比使用 RSA 密钥的非对称加密/解密快了**近千倍**。
TLS 握手将两种加密/解密方式巧妙地结合在一起。在握手过程中,`client` 程序生成随机位,即所谓的<ruby>预主密<rt>pre-master secret</rt></ruby>PMS。然后`client` 程序用服务器的公钥对 PMS 进行加密,并将加密后的 PMS 发送给服务器,服务器再用 RSA 密钥对的私钥对 PMS 信息进行解密:
TLS 握手将两种加密/解密方式巧妙地结合在一起。在握手过程中,`client` 程序生成随机位,即所谓的<ruby>预主密<rt>pre-master secret</rt></ruby>PMS。然后`client` 程序用服务器的公钥对 PMS 进行加密,并将加密后的 PMS 发送给服务器,服务器再用 RSA 密钥对的私钥对 PMS 信息进行解密:
```
+-------------------+ encrypted PMS +--------------------+
@ -316,7 +313,7 @@ client PMS--->|servers public key|--------------->|servers private key|---
+-------------------+ +--------------------+
```
在这个过程结束时,`client` 程序和 Google Web 服务器现在拥有相同的 PMS 位。每一方都使用这些比特生成一个<ruby>主密码<rt>master secret</rt></ruby>,并在短时间内生成一个称为<ruby>会话密钥<rt>session key</rt></ruby>的对称加密/解密密钥。现在有两个不同但等价的会话密钥,连接的每一方都有一个。在 `client` 的例子中,会话密钥是 AES128 类的。一旦在 `client` 程序和 Google Web 服务器两边生成了会话密钥,每一边的会话密钥就会对双方的对话进行保密。如果任何一方(例如,`client` 程序或另一方在这种情况下Google Web 服务器)要求重新开始握手,握手协议如 Diffie-Hellman 允许整个 PMS 过程重复进行。
在这个过程结束时,`client` 程序和 Google Web 服务器现在拥有相同的 PMS 位。每一方都使用这些生成一个<ruby>主密码<rt>master secret</rt></ruby>,并立即生成一个称为<ruby>会话密钥<rt>session key</rt></ruby>的对称加密/解密密钥。现在有两个不同但等价的会话密钥,连接的每一方都有一个。在 `client` 的例子中,会话密钥是 AES128 类的。一旦在 `client` 程序和 Google Web 服务器两边生成了会话密钥,每一边的会话密钥就会对双方的对话进行保密。如果任何一方(例如,`client` 程序或另一方在这种情况下Google Web 服务器)要求重新开始握手,握手协议(如 Diffie-Hellman允许整个 PMS 过程重复进行。
### 总结
@ -330,7 +327,7 @@ via: https://opensource.com/article/19/6/cryptography-basics-openssl-part-2
作者:[Marty Kalin][a]
选题:[lujun9972][b]
译者:[wxy](https://github.com/wxy)
校对:[校对者ID](https://github.com/校对者ID)
校对:[wxy](https://github.com/wxy)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出