Watch & Learn

Debugwar Blog

Step in or Step over, this is a problem ...

一文读懂对称加密、非对称加密、哈希值、签名、证书、https之间的关系

2021-11-01 02:21:56

文字作为人类特有的交流工具,伴随着整个人类的发展史。可以毫不夸张的说,没有文字人类不可能达到今天的科技成就。因此,人类一直在追求提高信息交流过程中的安全性——从股市到战争再到日常通讯,各种加密方法、手段层出不穷。

如果各位还get不到信息加密的重要性,那请看下面的故事……

一个关于喝酒的故事


故事的开始


要想搞清楚各种术语之间的关系,我们还得从一顿酒开始。假设今天A想叫B出来喝酒,让我们看看最原始状态的的情况:


一切看起来都是那么简单、直接、高效,直到有一个新角色的加入:


形式瞬间变得严峻了起来,由于“B的老婆”在A与B传递信息的过程中偷听到了今晚B要去喝酒的事情,因此果断出手,挫败了这场暗地里进行的交易。

斗争


学术上我们称A与B的这次通信遭遇了中间人攻击,为了对抗这种攻击方式A与B想到了一种方法:如果双方事先约定一种暗号,我们只需要将想说的话按照暗号约定的方式“加密”一下再发送出去不就可以保证消息的私密性了?于是情况变成了下面这个样子:


问题似乎得到了完美的解决——直到B的老婆在打扫卫生时翻出了A与B约定的暗号加密方式笔记……

于是,A与B的通讯再次变成了“未加密”状态——酒又喝不成了。

俗话说福无双至,祸不单行,伴随着疫情隔离的要求,A与B的见面都成了个问题,双方更不可能私下约定暗号的加密方式,如果改成线上约定加密方式,情况就又回到了故事的最开始——在“中间人”偷听的情况下,这种交换显得毫无意义。

斗争的继续


上面这种情况下,A与B约定的暗号我们称之为密钥。A通过密钥加密数据,B通过相同的密钥解密数据——这种方式我们称之为对称加密。通过上面的原理可以得出,在加密算法强度足够的前提下,密钥的安全性就是整个加密过程的安全性,一旦密钥泄漏信息交换的过程将不再安全。

不幸的是,互联网环境下发生信息交换的双方是不可能通过线下的方式约定密钥的,而线上的方式又容易遭受中间人攻击。因此,如何在非安全条件下安全的交换密钥就成了一个迫在眉睫需要解决的课题。

那么有没有什么算法,能保证中间人即使拿到密文和密钥也无法解密数据呢?答案是:有!使用非对称加密可以达成上述需求。

非对称加密算法一般的理论依据是一些数学难题(例如常见的椭圆曲线算法等),这里举个浅显的例子:我们都知道10 2=100,然而如果将这个运算“倒过来”,也就是求解 lg100=? 的话会比较困难,其需要的计算量不是一个级别。

非对称加密算法通常会生成一对公私钥匙,公钥各位可以理解成上文中的100, 私钥可以理解成上文中的2,我可以告诉对方底数(10)和结果(100),但是由于lg100=?是一个数学“难题”很难求解,因此只要保证2这个私钥不泄露,那么整个算法的结果就是安全的。

非对称加密有一个特性:公钥加密的数据只能用私钥解密,反过来也成立。这就好比一把钥匙只能开一把锁,但是锁本身是不能开锁。这其中的底层原理涉及到复杂的数学证明,不过各位不需要关注这些细节,因为已经有很多现成的非对称算法给我们使用。

OK,啰嗦了这么多其实还是为了解决A和B喝酒的问题。由于公钥加密的数据只能用私钥解密,所以只拿到密文和公钥是不能还原出原文的,因此B只需要将想要约定的对称加密的密钥用公钥加密一下再发送给A不就完成了“在非安全条件下安全的交换密钥”吗?

首先A将自己的公钥交给B, B在收到公钥后,用公钥加密上一节中的对称密钥。这就好比把对称加密用到的密钥装到箱子里,然后用A给的锁头把箱子上锁:


这样就算加了锁的箱子被B的老婆截获,由于没有私钥依然没办法将箱子打开(说强拆的这位同学请你出去!)。

A在收到B发过来的箱子后, 用私钥打开箱子,获取到对称加密的密钥。再然后就又是第二节中套路了,AB用协商好的对称加密密钥通信。


看样子A和B又可以愉快的在一起喝酒了……………………吗?

欺骗的艺术


本来A、B以为终于可以愉快的大声密谋喝酒的问题了。这时B的老婆微微一笑表示,你们对力量一无所知:


B的老婆继续利用中间人劫持技术,首先获取了A的公钥(黑色锁),然后告诉B另外一个公钥(红色锁)。此后B将对称密钥用公钥(红色锁)加密后回复了他自己的老婆,此时B的老婆便获得了本次通信要用到的对称密钥,随后再次使用之前从A处获得的私钥(黑色锁)将对称密钥再次加密后回复给A。

注意,整个过程中,锁在箱子里的密钥依然为B的密钥,只不过此密钥已经失去了私密性(被B的老婆截获)。

再之后的故事便又双叒回到了文章一开始的状态,B的老婆由于掌握了对称加密的密钥,因此可以解密整个A、B的通话过程。

B的老婆甚至可以冒充对方进行回复:


此时球再一次踢到了A、B这一方脚下……

证明你是你自己


实际上除去作为中间人转发A、B的消息,B的老婆还可以直接冒充A直接对B发起欺骗:


上图中B的老婆冒充A给B发送消息,B误以为这条消息真的是A发过来的,于是按照正常流程给予了肯定的回复。

  1. B事后说:你这种亲自上场的行为……是犯规的……  
  2. B的老婆说:你这是又皮痒了?  

此时此刻,A、B又多了两个需要解决的问题:

  1. 如何防止消息被篡改?
  2. 如何防止身份被伪造?

我们先来看第一个。

不知道大家有没有见过法院在查封财物时给一些门窗、盒子之类的物件贴上封条的操作,又或者是古代在传递信件时用火漆对信件进行封口处理。这两个例子本质上就是使用某种额外手段对内容进行再一次佐证。

那在通信过程中是否也有这样一种方法可以达成佐证信息的手段呢?答案依然是:有!这个答案就是对消息本身进行一次哈希算法(摘要算法),常见的哈希算法有md5、sha1、sha256等等。

由于哈希算法本身的特性,哪怕消息有1个比特的改变,哈希后的值也会变得完全不一样。如果在传递消息的时候再附上这条消息的哈希值,接收方只需要用相同的算法对消息正文重做一遍哈希,再拿着自己算出的哈希值和传递过来的哈希值比对一下就可以知道消息是否被篡改。

上面对整个消息正文执行哈希算法得到的值,我们一般称作消息的签名。该值具有唯一性可以用来证明消息本身有没有被篡改。

在实践中,通常我们会对消息哈希后的值再用私钥加密一遍,收到消息的人正好可以用对应的公钥解密,这样就可以进一步确认消息的确是持有私钥的"本人"发出的。

讲到这里不知道各位想没想到还有什么漏洞?没错,既然我知道有签名机制,那我对篡改后的消息重新做一遍签名不就行了……

这就涉及到上面的第二个问题了——如何防止身份被伪造?

抛开所有技术细节,我们先来想一个场景:

  1. 有一份合同上有XXX的名字和XXX的身份证号
  2. 某人拿着XXX的身份证,且身份证上的照片和这个"某人"长得一样

那是不是可以得出结论,“某人”一定是这份合同的签署方之一?答案显然是肯定的,因为我们日常生活中就是这么操作的。

这里其实有一个隐含条件——身份证。你我默认身份证是一个权威信息可以用来验真或证伪。而身份证是由国家颁发的——即你我都选择相信一个权威第三方。

在上面的例子中,“身份证”可以认为是证书,而国家即颁发这个证书的权威机构(CA),身份证号即CA对某人的签名。如果我们要验明某人是否真的是一张身份证的持有人,只需要拿着身份证号去问CA,然后根据CA返回的姓名、照片等信息和“某人”持有的这张身份证比对即可,这个过程可以称之为验签。

如果在A、B的通信中, B先验证了A的身份,然后又检验了A声明的消息签名值,那么我们可以说,这封消息一定是A发送的(前提是A的私钥不能泄漏)。

讲到这里,证书的概念就出来了:证书即包含权威第三方机构对某对象的认证信息以及这些认证信息签名的文件。

最终版本

OK,在引入了CA和证书的概念之后, 让我们来看一下这个故事的最终版本:


在新的故事中,A向B发送的,将不再是公钥,而是证书,证书的构成如下:


当B收到A的证书时,首先会看一下CA是否为自己信任的CA,如果是自己信任的CA,则会计算上图蓝色部分的哈希值1,然后用CA的公钥解密签名信息得到哈希值2,最后比较哈希值1和2,如果两个值相等,则证明数据没有被篡改过。

此时,B就完成了整个验证过程。而此时B只要抽取证书中A的公钥,完成后续的加密过程即可保证整个会话数据传输的安全性。

这种情况下,由于B的老婆无法冒充A的身份向CA申请证书(CA理论上会验证申请人的身份),因此她无法再使用中间人劫持的手法获取整个会话过程协商阶段传输的私钥信息。

小结


可以看到,我们做的所有这些保障性工作,其目的都是为了保障最终传输的对称加密密钥不被窃取。而上文中提到的证书(包含公钥、签名的经过第三方认证的文件),仅仅是解决了“在非安全条件下安全的交换密钥”这一原始需求而已。

看到这里,我想大家已经初步理解了对称加密、非对称加密、哈希、签名、证书之间的关系。

接下来让我们来看几个实际的应用。

证书在现实世界的应用


签署https网站


首先,我们来直观的看一下一张证书长什么样子,以本站的https证书为例:


可以看到证书主要展示了3个部分:

  • 颁发给谁
  • 谁颁发的
  • 有效期

如果证书没有问题的话, 一般浏览器地址栏处会有一个锁的标志,像chrome这种浏览器还会用更醒目的绿色提示你该网站是安全的。那么,浏览器是如何认定网站是安全的呢?这个就需要看这张证书的证书链了:


上图即为本站证书的证书链, 可以看到位于最顶层位置的是一张叫ISRG Root X1的证书,这张证书签发了R3, 然后R3又签发了本站的证书。

那么浏览器是如何认定本站是安全的呢?

以IE为例,在其  Internet选项 -> 内容 -> 证书 这个标签页中,我们可以在“受信任的根证书颁发机构”这个标签页下看到ISRG Root X1这张证书。也正是因为这张证书是受信任的,因此它认证的网站也一起被浏览器信任:



在写作本文时,在笔者的一台电脑上恰好浏览器告警“不是私密连接”,让我们看一下这种情况下的证书链:


原来这台机器上的根证书DST Root CA X3过期了,此证书不再被浏览器信任,因此浏览器产生了上述告警。

到这里,可能你会有两个疑问:

  1. 最顶部的根证书是谁签发的?
  2. 这些根证书是从哪里来的?

答案是:

  1. 根证书自己签发自己,因为根证书的签发机构站在实力的角度被大家所认可
  2. 这些受信任的根证书一般是操作系统或者浏览器自带的

更多证书的细节


首先来看一下本站证书的所有细节:

  1. hacksign@XSignLaptop [17:28:45] : ~/Work   
  2. >> openssl s_client -connect debugwar.com:443 -servername debugwar.com < /dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > debugwar.crt  
  3. depth=2 C = US, O = Internet Security Research Group, CN = ISRG Root X1  
  4. verify return:1  
  5. depth=1 C = US, O = Let's Encrypt, CN = R3  
  6. verify return:1  
  7. depth=0 CN = debugwar.com  
  8. verify return:1  
  9. DONE  
  10. hacksign@XSignLaptop [17:28:45] : ~/Work   
  11. >> openssl x509 -in debugwar.crt -text -noout  
  12. Certificate:  
  13.     Data:  
  14.         Version: 3 (0x2)  
  15.         Serial Number:  
  16.             03:aa:4b:8b:74:af:65:a6:89:9c:b9:cc:46:9b:1b:43:0c:6f  
  17.         Signature Algorithm: sha256WithRSAEncryption  
  18.         Issuer: C = US, O = Let's Encrypt, CN = R3  
  19.         Validity  
  20.             Not Before: Oct 23 07:40:48 2021 GMT  
  21.             Not After : Jan 21 07:40:47 2022 GMT  
  22.         Subject: CN = debugwar.com  
  23.         Subject Public Key Info:  
  24.             Public Key Algorithm: rsaEncryption  
  25.                 RSA Public-Key: (2048 bit)  
  26.                 Modulus:  
  27.                     00:e1:db:4f:66:e1:e6:8d:f3:1e:31:df:47:9e:d2:  
  28.                     e2:04:de:f2:80:f3:5c:56:8c:8d:1a:a4:62:f9:44:  
  29.                     69:02:7e:20:69:f2:6f:d1:cf:25:3a:ca:6f:25:9a:  
  30.                     dc:7a:ca:2b:d7:05:55:ad:71:ee:ab:08:dc:58:a9:  
  31.                     e4:e2:27:8c:25:1c:8c:60:ed:97:6e:c5:69:95:dd:  
  32.                     99:14:3b:5f:07:a6:36:d5:00:fd:f0:ac:7c:e4:69:  
  33.                     0e:d0:f2:41:73:28:d2:01:b3:8b:38:68:8a:20:ce:  
  34.                     2f:e4:5e:90:56:e6:0c:8f:5f:44:4a:7a:64:ee:d4:  
  35.                     19:ca:76:09:08:64:a3:cb:ce:67:e5:2e:dd:2e:8e:  
  36.                     9e:e0:60:68:65:e0:68:68:02:13:db:78:b7:f8:ed:  
  37.                     4c:d6:79:01:ce:7b:6e:4d:f8:34:b3:b9:a7:54:86:  
  38.                     02:ef:a2:1e:a6:f8:f6:a7:ae:5a:84:a1:6a:22:3f:  
  39.                     7b:db:1e:13:91:16:2f:19:03:8a:c6:35:49:c4:69:  
  40.                     6d:6a:1c:1b:f1:3d:01:3c:2f:0c:81:d2:8c:fb:67:  
  41.                     c6:90:aa:1c:d2:78:16:0b:a8:0d:a9:98:52:d4:97:  
  42.                     33:26:ed:48:6a:af:a1:4c:99:f2:07:31:c3:8e:fd:  
  43.                     98:b5:ba:ab:59:0e:bb:f0:58:fb:3f:fa:71:7d:46:  
  44.                     ea:63  
  45.                 Exponent: 65537 (0x10001)  
  46.         X509v3 extensions:  
  47.             X509v3 Key Usage: critical  
  48.                 Digital Signature, Key Encipherment  
  49.             X509v3 Extended Key Usage:   
  50.                 TLS Web Server Authentication, TLS Web Client Authentication  
  51.             X509v3 Basic Constraints: critical  
  52.                 CA:FALSE  
  53.             X509v3 Subject Key Identifier:   
  54.                 80:99:14:55:B5:FB:47:D9:FD:6E:1C:07:C7:DA:65:3C:EB:65:BD:5E  
  55.             X509v3 Authority Key Identifier:   
  56.                 keyid:14:2E:B3:17:B7:58:56:CB:AE:50:09:40:E6:1F:AF:9D:8B:14:C2:C6  
  57.   
  58.             Authority Information Access:   
  59.                 OCSP - URI:http://r3.o.lencr.org  
  60.                 CA Issuers - URI:http://r3.i.lencr.org/  
  61.   
  62.             X509v3 Subject Alternative Name:   
  63.                 DNS:*.debugwar.com, DNS:debugwar.com  
  64.             X509v3 Certificate Policies:   
  65.                 Policy: 2.23.140.1.2.1  
  66.                 Policy: 1.3.6.1.4.1.44947.1.1.1  
  67.                   CPS: http://cps.letsencrypt.org  
  68.   
  69.             CT Precertificate SCTs:   
  70.                 Signed Certificate Timestamp:  
  71.                     Version   : v1 (0x0)  
  72.                     Log ID    : DF:A5:5E:AB:68:82:4F:1F:6C:AD:EE:B8:5F:4E:3E:5A:  
  73.                                 EA:CD:A2:12:A4:6A:5E:8E:3B:12:C0:20:44:5C:2A:73  
  74.                     Timestamp : Oct 23 08:40:48.764 2021 GMT  
  75.                     Extensions: none  
  76.                     Signature : ecdsa-with-SHA256  
  77.                                 30:44:02:20:1B:B1:81:FF:FE:2E:56:62:31:B8:EA:CE:  
  78.                                 6C:10:8A:13:6F:A2:BB:62:B8:EF:0F:CB:DE:F5:B6:26:  
  79.                                 A9:18:9D:20:02:20:57:A7:B5:7D:15:C6:30:41:B1:C7:  
  80.                                 DB:EA:3F:88:99:91:F5:F0:22:47:0D:DC:47:82:6C:32:  
  81.                                 BE:5C:E2:D4:E1:3C  
  82.                 Signed Certificate Timestamp:  
  83.                     Version   : v1 (0x0)  
  84.                     Log ID    : 29:79:BE:F0:9E:39:39:21:F0:56:73:9F:63:A5:77:E5:  
  85.                                 BE:57:7D:9C:60:0A:F8:F9:4D:5D:26:5C:25:5D:C7:84  
  86.                     Timestamp : Oct 23 08:40:48.752 2021 GMT  
  87.                     Extensions: none  
  88.                     Signature : ecdsa-with-SHA256  
  89.                                 30:45:02:21:00:C9:7B:C2:41:3A:FE:57:A3:75:E1:30:  
  90.                                 2B:E8:EC:A0:BD:65:8E:D4:77:41:D1:21:63:A7:63:70:  
  91.                                 CB:8A:B5:6F:85:02:20:45:D5:FB:99:EE:F4:5C:43:10:  
  92.                                 4D:DA:9D:DF:C0:61:FD:61:B1:3B:8C:5A:C6:24:FE:24:  
  93.                                 DA:61:BA:1A:4D:20:9A  
  94.     Signature Algorithm: sha256WithRSAEncryption  
  95.          62:c8:ba:69:15:7a:f4:eb:8a:32:f2:95:15:b5:6e:cf:fa:46:  
  96.          7a:b2:6e:4b:bf:97:c3:52:b6:da:dc:c5:ca:ad:6b:8c:05:7b:  
  97.          fd:d3:c4:4b:f4:09:1a:e2:89:d5:8d:16:64:99:87:e0:8d:d6:  
  98.          ec:82:36:51:08:bc:23:6e:c7:ef:04:2c:61:22:e5:af:67:7e:  
  99.          40:08:50:c7:9f:94:dc:e6:6e:b9:70:7c:f2:60:67:b6:4b:d7:  
  100.          4d:84:04:40:21:fa:c7:9f:94:64:e7:6b:17:b8:b9:df:da:7d:  
  101.          d3:8c:b8:5a:29:3a:80:48:ba:11:ac:91:b9:38:9f:3b:05:49:  
  102.          c0:8a:7d:80:60:10:07:81:c7:af:47:97:f9:5d:2b:d5:c7:f1:  
  103.          78:bd:b6:ee:55:b6:f7:f1:e0:2e:f2:73:54:8d:06:d9:e6:97:  
  104.          6e:fb:b1:b6:fa:92:ec:dc:8b:2d:4f:1d:d7:09:ac:07:e8:dc:  
  105.          58:ea:c0:a9:06:ee:e9:eb:af:a8:f3:79:21:44:b3:08:7b:51:  
  106.          8d:86:df:c2:e9:cd:12:0c:5b:7f:89:58:cd:9d:c5:d7:9f:d8:  
  107.          cd:97:f5:55:86:5e:f1:3f:09:40:d7:eb:a5:ae:8b:2e:b3:16:  
  108.          9e:e1:26:28:fc:61:cc:7a:3d:49:8b:0e:a1:b6:59:cb:42:49:  
  109.          fa:5c:08:4a  

接下来笔者挑几个有用的字段解释一下。

首先是下面几个字段:

  1. Issuer: C = US, O = Let's Encrypt, CN = R3  
  2. Validity  
  3.     Not Before: Oct 23 07:40:48 2021 GMT  
  4.     Not After : Jan 21 07:40:47 2022 GMT  
  5. Subject: CN = debugwar.com  
  6. Subject Public Key Info:  
  7.     Public Key Algorithm: rsaEncryption  
  8.         RSA Public-Key: (2048 bit)  
  9.         Modulus:  
  10.             00:e1:db:4f:66:e1:e6:8d:f3:1e:31:df:47:9e:d2:  
  11.             ………………  
  12.     X509v3 extensions:  
  13.         X509v3 Subject Alternative Name:   
  14.             DNS:*.debugwar.com, DNS:debugwar.com  
  15. Signature Algorithm: sha256WithRSAEncryption  
  16.     ………………  

Issuer:指明了该证书是由谁颁发的
Validity:指明了该证书的有效起始和终止日期
Subject:表明了该证书颁发给谁,CN是Common Name的缩写,即该网站的地址
Subject Public Key Info: 网站的公钥以及一些公钥使用的算法信息
X509v3 extensions: 这个字段下是一些扩展用的信息,其中比较重要的是 X509v3 Subject Alternative Name 简称SAN,我们经常可以看见有一些网站是以blog.xxx.com等形式来区分网站各个板块的,这种情况下每个子域就需要单独签署一个对应Subject的CN是对应值的证书,显然这是不合理的,所以在SAN字段中,我们可以用*.xxx.com来表示所有二级子域均可以使用这个证书。
Signature Algorithm:即上文中提到的签名字段,可以用于验证本证书是否经过篡改

http/https/ssl/tls协议

提到https协议,就不得不说http协议以及ssl/tls协议。

首先科普两个大家可能知道的知识点:

  1. http协议是一个4层协议
  2. http协议是明文传输内容的

可能大家都了解这个事情, 但是并不知道在实际应用过程中并不知道实际是干什么用的。

接下来我们可以结合下面的图来直观的感受一下:


http协议是一个4层协议


上图中左面绿色的部分为wireshark抓到的对百度这个ip地址一个个数据包,中间白色的部分是对771帧数据包的详细结构分析,右面黑色的部分是命令行中请求http协议的百度首页的命令。

从中间的白色部分可以看到,Hypertext Transfer Protocol上面依次是:Transmission Control Protocol、Internet Protocol Version 4、Ethernet II。这几个即大名鼎鼎的Http协议、TCP协议、IP协议、以太网协议等,分别对应下图网络模型的应用层、传输层、网络层、网际接口层


这里额外岔开一句,还记得面试高频被问的问题么: 请简述TCP的三次握手和四次挥手。这个其实对应上面wireshark截图中的第766、769、770和777、780、781、782帧。

BasicAuth认证


我们日常常见的地址格式


其实并不是完整的格式,http协议的地址URI字符定义中,我们其实可以制定用户名和密码用于验证的目的,这种验证方式叫做BasicAuth,在浏览器中访问开启了BasicAuth的网站会看到如下对话框:


从上面的wireshark截图中我们也可以看到,如果访问的是http协议的网站,其BasicAuth的用户名和密码是明文传输的,这意味着在传输的过程中可以被其他人看到用户名和密码。

ssl/tls/https协议


既然http明文传输不安全,那如何保证我们数据的安全性呢?这即是https协议希望达到的目的。

其实本质上来说,https协议只是利用ssl或tls协议在http开始传输数据之前先将数据加密在发送出去,其传输的还是“明文”的数据,只不过因为数据被提前加密过了因此不再具备可读性。

先来看一下https协议的过程:


ClientHello:Client端将自己的TLS协议版本,加密套件,压缩方法,随机数,SessionID(未填充)发送给Server端
ServerHello:Server端将选择后的SSL协议版本,压缩算法,密码套件,填充SessionID,生成的随机数等信息发送给Client端
ServerCertificates:Server端将自己的数字证书(包含公钥),发送给Client端。(证书需要从数字证书认证机构(CA)申请,证书是对于服务端的一种认证),若要进行更为安全的数据通信,Server端还可以向Client端发送Cerficate Request来要去客户端发送对方的证书进行合法性的认证。
ServerHelloDone:当完成ServerHello后,Server端会发送Server Hello Done的消息给客户端,表示ServerHello 结束了。
ClientKeyExchage:当Client端收到Server端的证书等信息后,会先对服务端的证书进行检查,检查证书的完整性以及证书跟服务端域名是否吻合,然后使用加密算法生成一个PreMaster Secret,并通过Server端的公钥进行加密,然后发送给Server端。
ClientFinishd:Client端会发送一个ChangeCipherSpec(一种协议,数据只有一字节),用于告知Server端已经切换到之前协商好的加密套件的状态,准备使用之前协商好的加密套件加密数据并进行传输了。然后使用Master Secret(通过两个随机数、PreMaster Secret和加密算法计算得出)加密一段Finish的数据传送给服务端,此数据是为了在正式传输应用数据之前对刚刚握手建立起来的加解密通道进行验证。
Server Finishd:Sever端在接收到Client端传过来的加密数据后,使用私钥对这段加密数据进行解密,并对数据进行验证,然后会给客户端发送一个ChangeCipherSpec,告知客户端已经切换到协商过的加密套件状态,准备使用加密套件加密数据并传输了。之后,服务端也会使用Master Secret加密一段Finish消息发送给客户端,以验证之前通过握手建立起来的加解密通道是否成功。

只看文字还是太枯燥了,我们在wireshark中观察一下整个通信的数据包:


首先和最后,依然是28-30与56-61的三次握手和四次挥手。

然后在ClientHello(31、33)和ServerHello(34、35)之后之后客户端和服务端分别通过Client Key Exchange(36、37)和Change Cipher Spec(38、39)交换了协商好的算法。

最后的两条Application Data(40-43)分别为请求和响应,理论上40这个数据包中理论上应该存在发送的username和password,现在由于数据已经被加密,因此我们看到的数据是“毫无意义”的。

特别需要提示的一点是,上述交互过程基本每个数据包都是成对出现的,每个包会紧跟一个ACK包——还记得TCP协议的内容么……可靠传输,ACK包即是可靠的保证。

双向认证


其实上一节中的https协议依旧是简化版本,真正完整的https协议是支持双向认证的,即客户端不仅要认证服务器、服务器也要认证客户端。

只不过是平时大家访问的网站都是开门做生意的状态——来的都是客。因此服务器并不会验证客户端的身份。

但是在一些特殊场景下,需要做双向认证。本节将以nginx为例,配置一个双向认证的服务器。其结果是,如果是没有指定私钥的浏览器想访问nginx上运行的网站,则直接报错。

先直观的感受一下最终的结果。

首先是没有私钥的访问:


然后我们在浏览器中导入特定的私钥


然后再次访问该站点:


此时该站点会要求我们提供凭证,如果提供正确的凭证,即可获得该站点的所有内容:


上述是手动演示整个认证的过程,其实我们可以通过nginx的配置,自动完成上述认证过程。通过两个nginx实例完成认证访问。

下文中我们将会称两个nginx实例一个是Client端另外一个是Server端,Client端用于模拟上文手动过程中浏览器对服务器的访问,Server端上则跑着我们实际的站点。

首先是作为Client端的nginx配置, 该配置会认证server端发送的证书以及server端的身份,防止有人劫持server端从而冒充server端的身份:

  1. upstream backend.dynamic-domain.com {  
  2.     jdomain backend.dynamic-domain.com port=1203 interval=10;  
  3. }  
  4.   
  5. server {  
  6.     listen      443 ssl;  
  7.     ssl_certificate                 /path/to/certificate/issued/by/letsencrypt/website.crt;  
  8.     ssl_certificate_key             /path/to/certificate/issued/by/letsencrypt/website.key;  
  9.   
  10.     proxy_ssl_verify on;  
  11.     proxy_ssl_trusted_certificate   /path/to/self/signed/certificate/ca.crt;  
  12.     proxy_ssl_certificate           /path/to/self/signed/certificate/client.crt;  
  13.     proxy_ssl_certificate_key       /path/to/self/signed/certificate/client.key;  
  14.   
  15.     location = / {  
  16.         proxy_pass      https://backend.dynamic-domain.com;  
  17.     }  
  18.   
  19.     location / {  
  20.         proxy_pass      https://backend.dynamic-domain.com;  
  21.     }  
  22. }  

第7、8行是站点的证书,此证书即正常的网站证书,从let's encrypt申请,由于let's encrypt签署的证书可以被各大浏览器信任,因此当其他人访问网站时浏览器会提示安全。

第10至13行开启了nginx对服务端的ssl验证,其中ca.crt是私人自签名的根证书,由于这份证书是私人签署的,导致其他人通过任何机构获得的证书都不被信任,只有由这份私人根证书签署的子证书才被认为是合法的。

第12、13行分别是由ca.crt签署的客户端子证书以及密钥文件,client.crt用于向server端表明自己的身份,server端也同样配置了信任私人ca.crt根证书签署的子证书。

下面是server端的配置文件,该配置会验证客户端身份并发送自己的证书给客户端(是否验证取决于客户端):

  1. server {  
  2.     listen  443 ssl;  
  3.     ssl_verify_client   on;  
  4.     ssl_certificate         /path/to/self/signed/certificate/server.crt;  
  5.     ssl_certificate_key     /path/to/self/signed/certificate/server.key;  
  6.     ssl_client_certificate  /path/to/self/signed/certificate/ca.crt;  
  7.   
  8.     location / {  
  9.         proxy_pass  http://192.168.1.100:1203;  
  10.     }  
  11. }  

上面第3行开启了验证客户端的开关。

第6行为和client端配置中相同的ca.crt根证书,声明只有该证书签署的子证书才认为是合法的。

第4、5行分别是私人根证书签署的服务端子证书和密钥文件,server.crt即是发送给client端验证的证书。

由于client、server端都同时信任ca.crt根证书,因此由ca.crt签署的client.crt和server.crt再双方看来都是合法的,可以正常交互。

如果你已经弄明白了上述双向认证的逻辑, 接下来就只剩下生成如下三个证书了:

  • ca.crt、ca.key : 根证书以及密钥文件
  • client.crt、client.key:由ca.crt签署的客户端子证书
  • server.crt、server.key:由ca.crt签署的服务端子证书

注意: 由于是自签名证书,因此ca.crt是自己给自己签发的,如果你使用了任何公开的第三方证书办法机构作为自己的根证书,在配置不当的情况下,其他人可以非授权访问数据。
生成证书生成证书

生成证书


上文中,我们用到了3个证书,以及3个证书对应的私钥,下面我们一步一步来。

总的来说,生成证书分为三大步:

  1. 生成私钥
  2. 生成CSR请求文件
  3. 生成证书

生成私有根证书


根证书用于签署接下来的客户端和服务端证书。

换句话说,根证书是客户端和服务端证书的父证书,客户端和服务端互为兄弟证书。我们在上述的双向认证nginx中,兄弟之间信任的都是他们共通的父证书。由于父证书是我们自己给自己签署的,因此只要保证父证书的私钥不泄漏,技能保证整个系统之间通信的私密性。

首先我们生成私钥文件:

  1. openssl genrsa -aes256 -out rootca.key.pem 4096  


然后生成CSR证书请求:

  1. openssl req -new -key rootca.key.pem -out rootca.csr.pem  


这里会要求输入第一步创建rootca.key.pem时输入的密码。

同时还要注意, Common Name这一项, 这一项是用来标识使用人的, 网站的证书里用来标识网站的即为这一项,如果这一项和实际访问的域名不匹配,浏览器会提示有风险。

生成自签名证书


  1. openssl x509 -req -in rootca.csr.pem -signkey rootca.key.pem -out rootca.crt.pem  


注意:

  1. -signkey参数用于生成自签名证书,生成client和server证书时,由于不使用此参数。
  2. 如果你参考了其他文档,可能会发现他们使用了openssl ca命令,此命令需要配合配置文件使用,本文为了避免引入过于复杂的概念,使用openssl x509命令生成根证书,这样可以避免使用配置文件。
可以通过命令观察一下刚才生成的根证书,发现其issuer和subject均为自己:


生成client/server证书

下面以生成client证书为例,生成server证书命令是一样的,需要注意的是如下几个点:

  1. 使用不同的密钥和输出文件名(当然也可以使用相同的密钥,但是这样会损失一定的安全性)
  2. 在生成CSR时, Common Name要填写对应的标识信息

生成私钥
  1. openssl genrsa -out client.key.pem 4096  

生成csr

  1. openssl req -new -key client.key.pem -out client.csr.pem  

注意, Common Name要填写对应的标识,换句话说根证书、client证书、server证书的标识要不一样: 


生成证书:

  1. openssl x509 -req -in client.csr.pem -out client.crt.pem -CA rootca.crt.pem -CAkey rootca.key.pem -CAcreateserial -CAserial ./serial.txt 

使用命令观察一下签署状态,可以发现issure是私有CA,subject为client标识的证书:


结尾


本文讲述了对称加密、非对称加密、证书、签名之间的关系,并展示了一个实际应用过程中不太常见的双向验证的https协议使用场景。

希望对各位有所帮助 ;)
Catalog
  • 一个关于喝酒的故事
  • 故事的开始
  • 斗争
  • 斗争的继续
  • 欺骗的艺术
  • 证明你是你自己
  • 最终版本
  • 小结
  • 证书在现实世界的应用
  • 签署https网站
  • 更多证书的细节
  • http/https/ssl/tls协议
  • http协议是一个4层协议
  • BasicAuth认证
  • ssl/tls/https协议
  • 双向认证
  • 生成证书
  • 生成私有根证书
  • 生成自签名证书
  • 结尾
  • CopyRight(c) 2020 - 2025 Debugwar.com

    Designed by Hacksign