后浪笔记一零二四

1 TLS安全密码套件解读

        身份验证   算法强度模式
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
   秘钥交换            密码    MAC或者PRF

秘钥交换算法(生成对称加密中所需要的秘钥)
身份验证算法(使用非对称加密算法来验证身份)
对称加密算法、强度、工作模式(AES_128_GCM)
签名hash算法(sha256)

2 为什么对称加密算法包含强度和模式

2.1 XOR与填充

对称加密的核心就是XOR运算,由于XOR运算需要操作数和被操作数的长度一致,而现实中明文远远比秘钥要长,所以需要对明文进行分组然后才能执行XOR运算。

填充padding:

  1. Block cipher分组加密:将明文分成多个等长的Block模块,对每个模块分别加解密
  2. 目的:当最后一个明文Block模块长度不足时,需要填充
  3. 填充方法: 位填充: 以bit位为单位来填充
    字节填充: 以字节为单位来填充
    补零法: |DD DD DD DD DD DD DD DD| DD DD DD DD 00 00 00 00|
    ANSI x9.23: |DD DD DD DD DD DD DD DD| DD DD DD DD 00 00 00 04|
    ISO 10126: |DD DD DD DD DD DD DD DD| DD DD DD DD 81 A6 23 04|
    RKCS7(RFC5652) |DD DD DD DD DD DD DD DD| DD DD DD DD 04 04 04 04|
    AES算法就是使用RKCS7填充方法

2.2 使用工作模式来减少密文的特征

分组工作模式的定义: block cipher mode of operation:
允许使用同一个分组密码秘钥对多于一块的数据进行加密,并保证其安全性。

  1. ECB(Electronic codebook)模式 直接将明文分解为多个块,对每个块独立加密。
    问题: 无法隐藏数据特征。
          Plaintext                  Plaintext                  Plaintext 
              V                          V                          V
key ---->Block Cipher      key ---->Block Cipher      key ---->Block Cipher
          Decryption                 Decryption                 Decryption
              V                          V                          V
         Ciphertext                 Ciphertext                 Ciphertext  

key: 作为异或操作的一部分,做一些变换。
但是不管怎样做一些变换,都会导致一个问题,比如说我的明文是一张图片,那么明文的一些数据特征会在密文中得到显现。
  1. CBC(Cipher-block chaining)模式 每个明文块先与前一个密文块进行异或后,再进行加密
    问题:加密过程串行化
                 Plaintext                  Plaintext                  Plaintext 
Initialization       |                          |                          |
    Vector(IV)------>⊕       |---------------->⊕       |---------------->⊕
                     V        |                 V        |                 V
       key ---->Block Cipher  |   key ---->Block Cipher  |   key ---->Block Cipher
                 Decryption   |             Decryption   |             Decryption
                     V--------|                 V--------|                 V
                 Ciphertext                 Ciphertext                 Ciphertext
  1. CTR(Counter)模式 通过递增一个加密计数器以产生连续的密钥流
    问题:不能提供密文信息完整性校验
Nonce:随机数
         
        Nonce+Counter            Nonce+Counter               Nonce+Counter  
              V                          V                          V
key ---->Block Cipher      key ---->Block Cipher      key ---->Block Cipher
          Decryption                 Decryption                 Decryption
              |                          |                          |
Plaintext---->⊕           Plaintext---->⊕           Plaintext---->⊕
              V                          V                          V
         Ciphertext                 Ciphertext                 Ciphertext  
  1. GCM(Galois/Counter Mode: CTR+GMAC) 验证完整性: hash函数(不定长度的文本,通过同一个hash函数之后可以得到一个定长的较短的一个hash编码, 这个hash编码相当于它的一个指纹,通常一个好的hash函数会使得不同的输入,很难得到同一个hash值,也就是说碰撞的概率是很低的)
input                                hash sum
fox  ---->       Hash function   --> DFCD3454

The red fox
runs across ---->Hash function   --> 52ED879E
  the ice

The red fox
walks across---->Hash function   --> 46042841
  the ice

有了hash函数之后呢,我们就有了这样的一个算法:MAC(Message Authentication Code),它可以实现对称加密的完整性。

         sender:                     receiver:
       Message(生成的密文)         Message
          |   \                   /     |
          |    -->             -->      |
          V       \           /         V
key->  mac算法     ->Message--    key->mac算法
(秘钥)    |        --->mac值--          |
          |       /           \         |
          |  ---->             --> mac->=?<=mac值
          V /                           |     
       mac值                            V
                              If the same MAC is found: then
                              the message is authentic and
                              Integrity checked 
                              Else: something is not right

有了mac之后,就可以推出如下GCM算法:

    {iv}
     |
     V
{Counter 0}-->{incr}-->{Counter 1}-->{incr}-->{Counter 2}
     |                      |                      |  
     V                      V                      V
   {E_k}                  {E_k}                  {E_k}
     |                      |                      |
     |                      V                      V
     |      {Plaintext 1}->(+)     {Plaintext 2}->(+)
     |                      |                      |
     |                      V                      V
     |              {Ciphertext 1}         {Ciphertext 2}
     |                      |                      |
     |                      V                      V
     |      +------------->(+)         +--------->(+)
     |      |               |          |           |
     |      |               V          |           V
     |  {mult_H}         {mult_H}------+        {mult_H}
     |      ^                                      |
     |      |                                      V
     |  {Auth Data 1}           {len(A)||len(C)}->(+)
     |                                             |
     |                                             V
     |                                          {mult_H}
     |                                             |
     |                                             V
     +------------------------------------------->(+)
                                                   |
                                                   V
                                                {Auth Tag}

3 如何在TLS握手中,由双方各自独立地生成后续对称加密时所要使用的秘钥

RSA密钥交换
事实上我们使用RSA算法所生成的公私钥,基于这些公私钥来生成对称加密所使用的秘钥,也是可行的。

# 由客户端生成对称加密的密钥
   ClientDevice                       Server
       |                                 |
       |-------------Hello-------------->|
       |                                 |
       |<---------PublicKey--------------|
       |                                 |
       |                                 |
       |--------(Secret)PublicKey--------|
       |                                 |

问题:没有前向保密性
什么叫做前向保密性呢?比如说现在有个中间人,我现在还没有破解server的私钥,我现在通过中间的一些网络设备把所有的报文都保存下来了,
然后,当某一天我破解了这个server的私钥,那么我就可以基于私钥破解出对称加密算法所使用的的秘钥了,最后就可以把所有的消息全部解了。
这就叫做没有前向保密性。

3.1 DH密钥交换

  • 1976年由Bailey Whitfield Diffie和Martin Edward Hellman首次发表,故称为Diffie-Hellman key exchange,简称DH
  • 它可以让双方在完全没有对方任何预先信息的条件下通过不安全信道创建一个秘钥。
          ClientDevice                       Server
              |                                 |
              |-------------Hello-------------->| Private key 1
              |                                 |
PrivateKey2   |<---------PublicKey1-------------|
              |                                 |
              |                                 |
              |----------PublicKey2------------>|
              |                                 |
        PrivateKey2--------(Secret)-----------PrivateKey1
        PublicKey1                            PublicKey2
              |                                 |

TLS1.2:

                   浏览器                               Web服务器(nginx)
1.Client Hello       ^-------------------------------------->^
                     |      支持安全套件列表                 |
                     |                                       |
                     |<--------------------------------------|  2.Server Hello
                     |      选择安全套件                     |
    check            |<--------------------------------------|  3.Server Certificates
    certificate      |      发送证书                         |
    validity         |                                       |
                     |<--------------------------------------|  4.ServerKey Exchange
                     |      -ServerDH公钥                    |
                     |<--------------------------------------|  5.Server Hello Done
                     |                                       |
                     |                                       |
6.ClientKey Exchange |-------------------------------------->|
                     |     -ClientDH公钥                     |
                     |                                       |
7.生成密钥           |                                       |  7.生成密钥
                     |                                       |
                     |                                       |
8.CipherSpec Exchange|-------------------------------------->|
  Finished           |                                       |
(密钥交换结束)       |                                       |
                     |<--------------------------------------| 8.CipherSpec Exchange(密钥交换结束)
                     |                                       |   Finished
                     |                                       |
                     |                                       |
                     |                                       |
                     |        Encrypted data                 |
                     |                                       |

我们的客户端首先发起一个Hello,我们server收到之后就知道了我们开始进行协商沟通了。
server先自己生成了一对公私钥,其中私钥它自己保存,叫做private key 1,然后把公钥public key1发送给
客户端。客户端接收到这个公钥之后呢,客户端自己也生成了一对密钥,私钥private key 2自己保存,
把公钥public key 2发送给server。

现在呢,客户端基于PrivateKey2和PublicKey1,用一种算法生成出来一个秘钥。
而server呢,基于PrivateKey1和PublicKey2,用一种算法也生成出一个密钥。
这两个密钥是相同的。

DH密钥交换协议举例:

* g(5),p(23),A,B公开
* a(6),b(15)保密
* 生成共同密钥K


   Alice                                             Bob
  a,g,p                                               b
A = g^a mod p, A=8 ----------g,p,A----------------> B = g^b mod p, B=19

K = B^a mod p, K=2 <---------B--------------------- K = A^b mod p, K=2

K = A^b mod p = (g^a mod p)^b mod p = g^(ab) mod p = (g^b mod p)^a mod p = B^a mod p

DH密钥交换协议的问题:

  1. 中间人伪造攻击
   Alice                     中间人                     Bob
     |                         |                         |
     |----------------------->(1)                        |
     |                         |                         |
     |                        (2)<-----------------------|
     |                         |                         |

1.向Alice假装自己是Bob,进行一次DH密钥交换,
2.向Bob假装自己是Alice,进行一次DH密钥交换
3.接着DH算法交换生成的密钥K,中间人就知道了

解决中间人伪造攻击: 身份验证(使用PKI),以检测第2步是否被攻击了。

  1. 计算过程中含有大量乘法,它的安全性依赖于大因素分解的困难性,所以需要比较长的秘钥位数

3.2 ECC椭圆曲线的特性

椭圆曲线的表达式: y^2 = x^3 + ax + b, 4a^3 + 27b^2 != 0 由于表达式左边是y的平方,所以它始终关于X轴对称,实际上它的图形并不是椭圆,只是某些运算和椭圆很像。

  1. +运算
  • P+Q = R
    • +运算的几何意义:R为P、Q连线与曲线交点在X轴上的镜像
    • P + P = R 当P点和Q点重合的时候,就会出现P+P=R ECC椭圆曲线.png
  • +运算满足交换律 a + b = b + a
  • +运算满足结合律 (a+b) + c = a + (b + c)

接下来我们从代数的公式上看看如何计算P+Q=R的。

  • 先计算出斜率m,再计算出R点的坐标 $ x_R = m^2 - x_P - y_P $ $ y_R = y_P + m(x_R - x_P) $ $ m=\begin{cases} \frac{y_P-y_Q}{x_P-x_Q} ,x_P \neq x_Q \ \frac{3x^2_P+a}{2y_P} ,P = Q\ \end{cases} $

ECC+运算举例:

* 设曲线: y^2 = x^3 - 7x + 10
* 设P=(1,2), Q=(3,4),计算出R(-3,-2)
    * P在曲线上,因为2^2 = 4 = 1^3 - 7*1 + 10
    * Q在曲线上,因为4^2 = 16 = 3^3 - 3*7 + 10 = 27 - 21 + 10
    * R在曲线上,因为(-2)^2 = 4 = -3 - 7*(-3) + 10 = -27 + 21 + 10

    $m = \frac{y_P-y_Q}{x_P-x_Q} = \frac{2-4}{1-3} = 1$
    $x_R = m^2 - x_P - x_Q = 1^2 -1 -3 = -3 $
    $y_R = y_P + m(x_R - x_P) = 2 + 1*(-3-1)=-2$
    $y_R = y_P + m(x_R - x_P) = 4 + 1*(-3-3) = -2$

我们看到了ECC的几何意义和代数意义,它是如何实现TLS中的加密的呢。

ECC的关键原理:

  • Q=K.P (K个P相加)
    • 已知K与P,正向运算快速
    • 已知Q与P,计算K的逆向运算非常困难 已知Q点和P点,要计算出有多少个K是非常困难的。

所以在ECDH交换协议中,P点往往是已知的公共的一个点,而K呢就是已知的Client和Server他们各自的一个私钥,这个私钥乘以K之后,就得到Q这个公钥。然后把公钥交换给对方,就可以生成用于对称加密的一个秘钥。

3.3 DH协议升级:基于椭圆曲线的ECDH协议

ECDH秘钥交换协议:

  • DH秘钥交换协议使用椭圆曲线后的变种,称为Elliptic Curve Diffie-Hellman key Exchange, 缩写为ECDH,优点是比DH计算速度快、同等安全条件下秘钥更短
  • ECC (Elliptic Curve Cryptography):椭圆曲线密码学
  • 魏尔斯特拉斯椭圆函数(Weierstrass ’s elliptic functions): y^2 = x^3 + ax + b。

ECDH的步骤:

  • 步骤
  1. Alice选定大整数Ka作为私钥
  2. 基于选定曲线及曲线上的共享P点,Alice计算出Qa=Ka.P
  3. Alice将Qa、选定曲线、共享P点传递给Bob
  4. Bob选定大整数Kb作为私钥,将计算了Qb=Kb.P,并将Qb传递给Alice
  5. Alice生成秘钥Qb.Ka = (X,Y),其中X为对称加密的秘钥
  6. Bob生成秘钥Qa.Kb = (X,Y),其中X为对称加密的秘钥
  • 为什么X值是一致的呢,因为椭圆曲线的加法满足交换律和结合律 Qb.Ka = Ka.(Kb.P) = Ka.Kb.P = Kb.(Ka.P) = Qa.Kb

3.4 TLS1.3

TLS1.3只支持以下5种安全套件:
TLS13-AES-256-GCM-SHA384
TLS13-CHACHA20-POLY1305-SHA256
TLS13-AES-128-GCM-SHA256
TLS13-AES-128-CCM-8-SHA256
TLS13-AES-128-CCM-SHA256

测试TLS站点支持情况:https://www.ssllabs.com/ssltest/index.html

握手过程中,TLS1.3有哪些改进:
因为在握手过程中,TLS1.2会有一个Client Hello和Server Hello来选择安全套件,
导致TLS1.2有了一个RTT这样的一个不必要的延时
那么TSL1.3中又是怎样解决的呢?
Client会在预先在握手之前就已经生成了它的Private Key2和Public Key2,
然后在握手的第一次中,就把Public Key发送给Server了。
而Server呢,同时也会生成自己的Private Key1和PublicKey1,
那接下来大家就可以共同的来通讯了。

在这样的一个过程中,有一个问题就是openssl 1.1.1中支持5种加密套件,
那么Client如何知道后续他们应该使用哪一种呢,通常Client会把这5种全部
生成各自的privateKey和PublicKey,然后把所有的PublicKey发送给Server,
由server选定某一种方式以后,并使用这一种曲线所使用到的这样的一个PublicKey,
并告诉我们的Client。

3.5 quic+tls1.3和tcp+tls1.3

handshakeRTT

4 附录: RSA算法

4.1 RSA算法中公私钥的产生

  1. 随机选择两个不相等的质数p和q
  2. 计算p和q的乘积n (明文小于n,也就是我们将明文转换为十进制之后,它必须小于n)
  3. 计算n的欧拉函数v = φ(n),即: v = (p-1) * (q-1) 欧拉函数:所有小于n的正整数中,与n互质的数的数量
  4. 随机选择一个整数k 1 < k < v, 且k与v互质(它们的最大公约数是1)
  5. 计算k对于v的模反元素d:(d*k)%v = (k*d)%v = 1
  6. 公钥: (k,n)
  7. 私钥: (d,n)

例如, p = 11; q = 29; n = 319 则:v = 280; k = 3; d = 187 public key: (3,319) private key: (187,319)

  1. 加密: c≡ m^k mod n m是明文, c是密文
  2. 解密: m ≡ c^d mod n
  3. 举例: 对明文数字123加解密 公钥(3,319)加密 123^3 mod 319 = 140 对140密文用私钥(187, 319)解密 140^187 mod 319 = 123 私钥(187,319)加密 123^187 mod 319 = 161 公钥 (3, 319)解密 161^3 mode 319 = 123

4.2 为什么说RSA算法是安全的呢?

比如说一个第三者拿到了这个公钥(3, 319),他能不能推导出(187,319)这个私钥呢。 由于n是已知的,所以只需要找到d,如何找到d呢,只要他拿到了v,它自然就找到了d。 v=(p-1)*(q-1),也就是说他必须找到p和q, 而p*q=n,n是一个很大的数字。

对于一个很大的数n,要通过因式分解获取到p和q是非常困难的。

所以,RSA算法的安全性来源于对一个大数进行因式分解非常困难。

4.3 基于openssl实战验证RSA

 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
28
29
# 生成私钥(公私钥格式参见RFC3447)
$ openssl genrsa -out private.pem
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA6I1sFwlJibBLH4ephI9RIXRS5vuFkWyFS6qq4kdAK667mzhq
ynsmj5V06oJLbd6q81rKh/3Ork/z9aE8dpMZAPMZjauLMVjllXkeEoi4K/ty6gWT
kz4OJGlLmqfV6Vcn1oW8I7Bup+SXiCsQCDjHMQu9SePyUh/an/UPYZPKqFAIhHky
IqK3+77mCT84JgYcoFHK9U4+IQTlG4cZKQ6ssBBVkIaJ1OyfYFU2Dc5ldmVb7OBB
18guvzFvNigEC1fJv5aPg3HvjugWvW83NkpU+Cg2CYJtBELypBURIJXVxCRC8GcV
R4ZopGPV3B5vMf02cneWbF5D49RUyVwoTMHJ6wIDAQABAoIBAC/VJu52EpInmMwb
c1q4v89JxeOMyKRo1eICcLOqRCC4CA6dRBnxyrSt4SZ7Nlia/D6h5haitehUubFE
QNOC3hm75v2k0zSLPG3KQ808IeWqCrDoBLq8gNwsbEibTPx52M/fIhuVJq2R7zkq
4llOlGoaKA0Svbz2oFlWZpuDcqO7riSmEoxii9JuB9IpNxV1IvklmlIDO7Rn3h8U
I2Kxj2E7tIv98puiR5b/gjG3JnA1lxxvOEPxpj8XKunS0vVWjCr9JGDCjw9q2QV3
i/gKh3vZFTGPhURxsSK3c1ImqL09w9jN1/FDOJK8uSm/IFi8vR3BmnfGS0S8roEQ
n/Hzq4ECgYEA9XvWsFpg5onNcen7/hh+6H4tl+PluRh7RMKQTEGTsSiO3wZuSv9M
m/G50rt8LXij/78TrxMoONNZyzvnY2Qv05aBzncYr8DdMWCUMgmhd47NJ/yqgscz
e+g5L7Fu+qRnuULwf/E8crlP3AZHG/J/xMzHLHvHQZQ2uTutwVkLG1kCgYEA8oPE
261rvx5/xKQHV9so2QI5tSBYALXSDGOOYOewudfRRI+ESxxUv1f8mYlyHu9lwopa
/kYIJGW9A9Nm0qJq7v2axG42AVMYj6s9cVsGxBH8Yisjv+1RS4CYdiRdftRBm1QY
1ECnqnLNQUVZ8bZaioQjHaU3/uk3cy7k7Od9muMCgYEAueSZYtpN3sYjiUvCw1wR
XkWOiSLO5yee0Sb01ooiKVmpN0JLaweBH+Yg7W+ETXq51gUABufe4YlKyhgG1MEd
ZoDms06EpzLJTYbxMiMQ3tSIjutKw9XNKttj0rXucGbnFOeX74riV43054zpdERi
dMkNWqYw8XwnJIYIwg2H16ECgYEAh+MRwU3xG8wJJ7QJcU8aGk7G6xjOdsdqhQqb
0uy/98JYPiR0qmmbiwPQRG/hhDf8Ov+vcN7o3Qws1yHzcX8UkAmGWR6wIBnhn08C
RKwOpHq3goJNq9NcRAQwabKCuPhSMqjyqKv5xuZA4JvthCT7orXbv1a+g5VGPsI+
AAjONqUCgYEAntur/aWfhKO6Pbvr1FJJZbiaXx1nb7lXQHz8zfF2xD8WJ9hDP/Nn
DdBDpYOd2JP+BYMjiGE9vd+V5vMrCSGuWOYYofBVYCOJ2BNlU+D3x4fWxR4kbcoX
NQQYlx353tbq75p9fd8KSdsmuJfUCwJTEkGbws0kzjitj3QZMJddSR0=
-----END RSA PRIVATE KEY-----

注意,openssl genras -out private.pem这一步生成的private.pem文件中, 包含了上节课中我们介绍的p,q,n,v,k,d等等信息。

  1. 从private.pem文件中提取出私钥: 私钥是使用ASN.1格式进行编码的,所以我们需要使用openssl的子命令asn1parse
 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
$ openssl asn1parse -i -in private.pem
RSAPrivateKey ::= SEQUENCE {
  version Version,
  modules INTEGER, -- n
  publicExponent INTEGER, -- k
  privateExponent INTEGER, -- d
  prime1 INTEGER, -- p
  prime2 INTEGER, -- q
  exponent1 INTEGER, -- d mod (p-1)
  exponent2 INTEGER, -- d mod (q-1)
  coefficient INTEGER, -- (inverse of q) mod p
  otherPrimeInfos OtherPrimeInfos OPTIONAL
}


$ openssl asn1parse -i -in private.pem 
    0:d=0  hl=4 l=1189 cons: SEQUENCE          
    4:d=1  hl=2 l=   1 prim:  INTEGER           :00
    7:d=1  hl=4 l= 257 prim:  INTEGER           :E88D6C17094989B04B1F87A9848F51217452E6FB85916C854BAAAAE247402BAEBB9B386ACA7B268F9574EA824B6DDEAAF35ACA87FDCEAE4FF3F5A13C76931900F3198DAB8B3158E595791E1288B82BFB72EA0593933E0E24694B9AA7D5E95727D685BC23B06EA7E497882B100838C7310BBD49E3F2521FDA9FF50F6193CAA8500884793222A2B7FBBEE6093F3826061CA051CAF54E3E2104E51B8719290EACB01055908689D4EC9F6055360DCE6576655BECE041D7C82EBF316F3628040B57C9BF968F8371EF8EE816BD6F37364A54F8283609826D0442F2A415112095D5C42442F06715478668A463D5DC1E6F31FD367277966C5E43E3D454C95C284CC1C9EB(这是n)
  268:d=1  hl=2 l=   3 prim:  INTEGER           :010001(这是k)
  273:d=1  hl=4 l= 256 prim:  INTEGER           :2FD526EE7612922798CC1B735AB8BFCF49C5E38CC8A468D5E20270B3AA4420B8080E9D4419F1CAB4ADE1267B36589AFC3EA1E616A2B5E854B9B14440D382DE19BBE6FDA4D3348B3C6DCA43CD3C21E5AA0AB0E804BABC80DC2C6C489B4CFC79D8CFDF221B9526AD91EF392AE2594E946A1A280D12BDBCF6A05956669B8372A3BBAE24A6128C628BD26E07D22937157522F9259A52033BB467DE1F142362B18F613BB48BFDF29BA24796FF8231B7267035971C6F3843F1A63F172AE9D2D2F5568C2AFD2460C28F0F6AD905778BF80A877BD915318F854471B122B7735226A8BD3DC3D8CDD7F1433892BCB929BF2058BCBD1DC19A77C64B44BCAE81109FF1F3AB81(这是d)
  533:d=1  hl=3 l= 129 prim:  INTEGER           :F57BD6B05A60E689CD71E9FBFE187EE87E2D97E3E5B9187B44C2904C4193B1288EDF066E4AFF4C9BF1B9D2BB7C2D78A3FFBF13AF132838D359CB3BE763642FD39681CE7718AFC0DD3160943209A1778ECD27FCAA82C7337BE8392FB16EFAA467B942F07FF13C72B94FDC06471BF27FC4CCC72C7BC7419436B93BADC1590B1B5(这是p)
  665:d=1  hl=3 l= 129 prim:  INTEGER           :F283C4DBAD6BBF1E7FC4A40757DB28D90239B5205800B5D20C638E60E7B0B9D7D1448F844B1C54BF57FC9989721EEF65C28A5AFE46082465BD03D366D2A26AEEFD9AC46E360153188FAB3D715B06C411FC622B23BFED514B809876245D7ED4419B5418D440A7AA72CD414559F1B65A8A84231DA537FEE937732EE4ECE77D9AE3(这是q)
  797:d=1  hl=3 l= 129 prim:  INTEGER           :B9E49962DA4DDEC623894BC2C35C115E458E8922CEE7279ED126F4D68A222959A937424B6B07811FE620ED6F844D7AB9D6050006E7DEE1894ACA1806D4C11D6680E6B34E84A732C94D86F1322310DED4888EEB4AC3D5CD2ADB63D2B5EE7066E714E797EF8AE2578DF4E78CE974446274C90D5AA630F17C27248608C20D87D7A1
  929:d=1  hl=3 l= 129 prim:  INTEGER           :87E311C14DF11BCC0927B409714F1A1A4EC6EB18CE76C76A850A9BD2ECBFF7C2583E2474AA699B8B03D0446FE18437FC3AFFAF70DEE8DD0C2CD721F3717F14900986591EB02019E19F4F0244AC0EA47AB782824DABD35C44043069B282B8F85232A8F2A8ABF9C6E640E09BED8424FBA2B5DBBF56BE8395463EC23E0008CE36A5
 1061:d=1  hl=3 l= 129 prim:  INTEGER           :9EDBABFDA59F84A3BA3DBBEBD4524965B89A5F1D676FB957407CFCCDF176C43F1627D8433FF3670DD043A5839DD893FE05832388613DBDDF95E6F32B0921AE58E618A1F055602389D8136553E0F7C787D6C51E246DCA17350418971DF9DED6EAEF9A7D7DDF0A49DB26B897D40B025312419BC2CD24CE38AD8F741930975D491D
  1. 从primary.pem文件中提取出公钥:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
$ openssl rsa -in private.pen -pubout -out public.pem
$ openssl asn1parse -i -in public.pem 
    0:d=0  hl=4 l= 290 cons: SEQUENCE          
    4:d=1  hl=2 l=  13 cons:  SEQUENCE          
    6:d=2  hl=2 l=   9 prim:   OBJECT            :rsaEncryption
   17:d=2  hl=2 l=   0 prim:   NULL              
   19:d=1  hl=4 l= 271 prim:  BIT STRING  

# 从上面解析的结果中,并没有看到n和k的影子,其实这些信息都在BIT STRING中。
# 对BIT STRING再做一次解析,需要添加-strparse 19参数(从上面的输出中可以发现,BIT STRING的序号是19)
$ openssl asn1parse -i -in public.pem  -strparse 19
    0:d=0  hl=4 l= 266 cons: SEQUENCE          
    4:d=1  hl=4 l= 257 prim:  INTEGER           :E88D6C17094989B04B1F87A9848F51217452E6FB85916C854BAAAAE247402BAEBB9B386ACA7B268F9574EA824B6DDEAAF35ACA87FDCEAE4FF3F5A13C76931900F3198DAB8B3158E595791E1288B82BFB72EA0593933E0E24694B9AA7D5E95727D685BC23B06EA7E497882B100838C7310BBD49E3F2521FDA9FF50F6193CAA8500884793222A2B7FBBEE6093F3826061CA051CAF54E3E2104E51B8719290EACB01055908689D4EC9F6055360DCE6576655BECE041D7C82EBF316F3628040B57C9BF968F8371EF8EE816BD6F37364A54F8283609826D0442F2A415112095D5C42442F06715478668A463D5DC1E6F31FD367277966C5E43E3D454C95C284CC1C9EB(这是n)
  265:d=1  hl=2 l=   3 prim:  INTEGER           :010001(这是k)
  1. 对hello.txt文件进行加解密
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
$ cat hello.txt 
Hello World!

# 用公钥进行加密
$ openssl rsautl -encrypt -in hello.txt -inkey public.pem -pubin -out hello.en
$ cat hello.en 
9B{?Q?u`?=?{"F??ù(4Հ?ZJ?W???4?
?ɻN	])?nW?u?n????G-??F??TvX6@?c&V #?n`ۆ*??????fu?/?85Mz?b?????>?BP??n0?qѬ??n?6?s????l????,???e?Vs???(??[:<e?٤g?h?X2%???,
     ?ڛ9O?C??V?:9ˮ???ى?+D??C7?kA?pM3???L?K:??x~?H<?D?`A??qqF?~*??n
# 用私钥进行解密
$ openssl rsautl -decrypt -in hello.en -inkey private.pem -out hello.de
$ cat hello.de
Hello World!  

4.4 PKI(Public Key Infrastructrue)证书体系

  1. 基于私钥加密,只能使用公钥解密:起到身份认证的作用

  2. 由Certificate Authority (CA)数字证书机构将用户个人身份与公开密钥关联在一起。 比如说,我有一个网站,我的身份是什么呢,就是我的网站属于哪一家公司,属于哪一个国家,用作什么用途,等等。这些就是我的个人身份。 然后我的这个网站呢,我刚刚使用我的rsa算法生成了一个公钥,我把它公开出去了,我的公钥和我的个人信息交给CA机构以后呢, CA机构在核实完我的身份以后,就会颁发我一个数字证书(颁发数字证书的过程如下所示), 那么通过这样一种方式,就把个人身份和公开秘钥关联在一起了。

     ```
     # openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt
                HashFunction
         Data ---------------> 101100110101(Hash值)
     (用户的个人身份)                 |
                                      |用ca机构自己的私钥(存放在ca.key中)进行加密
                                      |
                                      V
    存在server.csr中的用户公钥    111101101110(signature) 
                 ^________________________^
                              |
                           附加Data
                              |
                              V
                       公钥数字证书(server.crt)
     ```
    
  3. 浏览器认证的过程

               公钥数字证书
               /          \
              /            \
            Data   111101101110(signature)
             |                       |
             |HashFunction           | 用CA机构的公钥(存放在ca.crt中)进行解密
             V                       V
        101100110101    ?=     101100110101
            
  if the hashes are equal, the signature is valid.

上面用于解密的公钥,浏览器是如何获取的? 由于有多级CA机构,它们是一个链式结构,会使用递归的方式执行上面的解密流程, 直到到达Root CA,而Root CA的公钥是维护在操作系统的/etc/pki/tls/certs/ca-bundle.crt文件下的。 这就解决了,公钥在哪里的问题。

Owner's DN
Owner's public key            Get certificate
Issuer's(CA)DN -------------------|
Issuer's(CA)signature             V
             ^                 Issuer's(CA)DN
             |-----------------Owner's public key                  Get certificates
            Verify signature   Issuer's(Root CA)DN--------------------|
                               Issuer's(Root CA)signature             V
                                            ^                   RootCA's DN
                                            |-------------------Root CA's public key
                                        Verify signature        Root CA's signature
  1. 证书申请和浏览器认证的过程
openssl genrsa -des3 -out server.key 2048
  ^    openssl req -new -key server.key -subj "/CN=tunnel-cloud" -out server.csr
  |               ^           openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt
  |               |                   ^
  |        证书签名申请(request       |
  |        certificate issuance,CSR)  |            (CSR)
证书订阅人 -------------------->   登记机构  ------------------>  CA(颁发证书)
    |      <----------------- (验证订阅人的身份)<--------------    |
    |                                                              |
    |部署证书                        ------------------------------|
    V                                v                             v
  web服务器                      CRL服务器                      OCSP响应程序
    ^|                               ^                             ^
    ||                               |                             |
1.请|| 2.验                          |                             |
求证|| 证签名                        |                             |
书  || CERT                          |                             |
    |V           3.检测吊销状态      |                             |
  信赖方------------------------------------------------------------

如果使用Let’s Encrypt,你会发现这个证书只有三个月的有效期,如果使用其他大部分的ssl的CA颁发的证书, 一般是1年的有效期。那么这个有效期是怎样体现的呢?CA中心会把过期的证书放到CRL服务器里面,这个服务器 会把所有的过期证书形成一条链条,所以它的性能非常的差。所以推出了OCSP程序,OCSP可以就一个证书去查它 是否过期,所以浏览器是可以直接去查询OCSP响应程序的,但OCSP响应程序,性能还是不够高,所以Web服务器 (例如Nginx)会有一个OCSP开关,当打开开关以后,nginx会主动去查询OCSP响应程序,这样大量的客户端就可以 直接访问nginx就可以获取到这个证书是否有效。

4.5 用免费SSL证书实现一个HTTPS站点

https://github.com/go-acme/lego todo: 使用lego取代certbot

如果使用centos操作系统,就可以使用yum install python2-certbot-nginx这样的一个工具就可以做到了。 当我们安装好这个工具以后,会提供一个命令叫做certbot,当我们为certbot命令指定–nginx参数之后, 它就会执行nginx配置的自动修改了

 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
28
29
30
31
32
33
34
35
36
37
# 单域名
# --nginx参数会修改nginx.conf配置,在http模块下添加如下内容:
# listen 443 ssl;
# ssl_certificate  /etc/letsencrypt/live/geektime.taohui.pub/fullchain.pem;   #指定证书链
# ssl_certificate_key  /etc/letsencrypt/live/geektime.taohui.pub/private.pem; #指定CA的密钥文件
# include /etc/letsencrypt/options-ssl-nginx.conf;
#     options-ssl-nginx.conf文件的内容:
#     ```ng
#     # 由于SSL中最消耗性能的是握手,所以为了降低握手,我们增加了一个session_cache。
#     # 这个session_cache有多大呢,这里设置为1m,大概能为4000个https连接服务。
#     ssl_session_cache shared:le_nginx_SSL:1m;
#     ssl_session_timeout 1440m;

#     ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
#     # 表示nginx开始决定使用哪些协议与浏览器进行通信
#     ssl_prefer_server_ciphers on;

#     # 怎么决定使用哪些协议呢,就是通过下面的ssl_ciphers的值
#     ssl_ciphers "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256"

#     # 每一个安全套件是使用分号作为分隔符的,可以看到ssl_ciphers定义了很多个安全套件,
#     # 所有安全套件以分号分隔是是有顺序的,排在前面的会优先被使用,所以说如果浏览器支持所有的安全套件的时候,我们会选用第一个。
#     ```
# ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;  # 指定rsa非对称加密算法的加密强度
#$ certbot --nginx --nginx-server-root=/usr/local/openresty/nginx/conf/ -d geektime.taohui.pub

# 泛域名
$ certbot certonly --preferred-challenges dns --manual -d "*.jiaweicheng.site,jiaweicheng.site"
# 注意,这里说是使用_acme-challenge.jiaweicheng.site,其实是添加_acme-challenge=VV0OXICEDiknIu14NqlDczbW8OUvqQmau_HnB2UeRF4的txt记录
# 并使用命令: dig -t txt _acme-challenge.jiaweicheng.site来查看添加是否生效
# 注意:续期证书遵守特殊规则:它们不计入您的每个注册域名的证书数量的限制,但它们受到每周最多 5 张重复证书的限制。
Please deploy a DNS TXT record under the name
_acme-challenge.jiaweicheng.site with the following value:

VV0OXICEDiknIu14NqlDczbW8OUvqQmau_HnB2UeRF4

Before continuing, verify the record is deployed.

定时刷新证书:

1
echo '0 0 * * 1 root certbot renew --post-hook "systemctl restart nginx"' | sudo tee -a /etc/crontab > /dev/null

刷新证书时遇到如下问题:

1
2
3
4
5
6
Could not choose appropriate plugin: The manual plugin is not working; there may be problems with your existing configuration.
The error was: PluginError('An authentication script must be provided with --manual-auth-hook when using the manual plugin non-interactively.',)

解决方案:
# 1. 先删除之前的证书
# 2. 重建证书,使用上面的流程

certbot帮助文档: https://certbot.eff.org/docs/using.html?highlight=domain#changing-a-certificate-s-domains

4.6 Q&A

  1. 检查私钥和证书是否匹配
1
2
openssl rsa -noout -modulus -in private.key | openssl md5
openssl x509 -noout -modulus -in certificate.crt | openssl md5

如果两个命令输出的 MD5 值相同,则说明私钥和证书匹配;否则它们不匹配。


本文发表于 0001-01-01,最后修改于 0001-01-01。

本站永久域名「 jiavvc.top 」,也可搜索「 后浪笔记一零二四 」找到我。


上一篇 « 下一篇 »

赞赏支持

请我吃鸡腿 =^_^=

i ysf

云闪付

i wechat

微信

推荐阅读

Big Image