密码学-数字证书与PKI

gong_yz大约 22 分钟安全相关

一、为什么需要数字证书与PKI

文章来源:https://zhuanlan.zhihu.com/p/563337960open in new window

要学习数字证书和PKI,首先需要知道为什么需要它们,或者说它们的作用是什么。

首先考虑一个问题,在一个不安全的网络环境比如互联网环境中,如何安全地与一个可信任程度未知的目标进行实时通信?这里的实时通信特指需要通信双方实时地,多次地交互消息的通信,比如在线聊天,网站访问等等。这种通信需要满足哪些安全要求?我们大致罗列一下:

  1. 机密性(Secrecy/Confidentiality),指通信时消息是保密的,不是明文,只有通信双方能够解密,中间拦截者无法获取明文。
  2. 完整性或者说一致性(Integrity),指通信时消息在传输过程中没有发生改变,不多不少也没有被修改,即没有被篡改,保持原状地发送到通信目标方。
  3. 身份认证(Authentication),指确认通信时对方的真实身份,保证消息是由可信源发来的,或者消息发送给了可信任的对方。
  4. 不可否认(Non-repudiation/Undeniable),也叫不可抵赖,意思是不能否认发送过的消息,在必要的情况下能够向第三方证明接收到的消息是由当时的通信对方发来的。

那么现在我们尝试用之前学习过的密码学算法来解决这些问题:

  1. 机密性可以通过对称加密实现。
  2. 完整性可以通过散列或者HMAC实现。
  3. 身份认证可以通过数字签名来实现。
  4. 不可否认可以通过数字签名来实现,但一般不做。

(1) 关于机密性与完整性的实现,理论上,机密性也可以通过非对称加密实现,完整性也可以用数字签名实现。但实时通信并不是邮件那样每次只发一条消息,一次实时通信其实是一个会话,这个会话会发生很多次消息交互。机密性与完整性针对的是通信会话中的每一条消息,而一次通信会话中的消息交互可能是很频繁的,一条消息也可能是比较长的,此时对每一条消息做非对称加密以及数字签名是很不划算的。但如果使用对称加密中适当的分组模式,比如GCM模式,就可以在保证消息机密性、完整性的同时,提供相比数字签名与非对称加密更加优秀的性能表现。 (2) 身份认证通过数字签名实现,只需要在实时通信开始时做一次就够了,不会影响到后续消息交互的性能。 (3) 不可否认有点麻烦,不可否认也需要针对每条消息做数字签名,但这无疑会极大地增加实时通信的开销,所以更多的时候,实时通信不会对每条消息做签名。如果某条消息确实非常重要,必须具有不可否认性,那么更好的选择是另外通过其他通信方式,比如正式的邮件,来确保不可否认性。很显然,邮件是一种离线的通信方式,每次通信只发送一条消息,完全可以用数字签名确保其不可否认性。

通过以上分析,一个安全的通信,需要一个带HMAC或支持AEAD的对称加密算法,以及一个签名算法。而对称加密算法又需要通信双方协商密钥与IV,因此还需要一个密钥协商算法。那么,为了确保在不安全的网络环境中安全地进行通信,我们需要:

  1. 通信双方各自生成自己的公私钥。
  2. 通信双方互换公钥。
  3. 通信双方在每次通信前发送自己的签名给对方,让对方验签,从而确认双方身份。
  4. 通信双方在每次通信前使用密钥协商算法协商出一组密钥,用于HMAC和对称加密的密钥/IV。
  5. 通信双方在这次通信接下来的消息交互中对每次交互的消息做对称加密,以及HMAC,从而保证消息的机密性与完整性。

除了不能确保不可否认性,这个过程看起来很完美了。 有没有漏洞?答案是"有",就在双方互换公钥这里。 如果互换公钥的过程被攻击者拦截了,并换成攻击者的公钥,那么双方就无法正确地完成身份认证,谁知道你在跟什么鬼通信?因此我们需要一种能证明某个公钥确实属于通信对方的方法。

数字证书和PKI就是为了解决这个问题。解决的思路如下:

  1. 双方不再直接互换公钥,而是互换数字证书。数字证书的主要内容是两部分: 一是证书主体,其内容核心是证书拥有者的公钥与身份信息;二是证书签名,是某个权威机构给该证书主体做的数字签名。证书主体是为了通信双方互换公钥并确认对方身份,而证书签名则是为了证明公钥与身份信息是安全可信任的。
  2. 此时问题的关键就是通信双方如何通过对证书签名的验签来确认这个证书确实安全可信?这就是PKI的作用,PKI提供了一个证书颁发机构,是线下公认的权威机构,这个机构有自己的公私钥,公钥已经事先通过某种被大家认可的方式公开给互联网上的大众,预埋在其本地。通信双方在发生通信之前,要先向这些机构申请数字证书,申请者要提供自己的公钥和身份信息,然后由这些机构验证申请者身份属实之后,用机构自己的私钥签名并生成数字证书,再颁发给申请者。之后互联网上的通信双方在通信时,要将自己的数字证书发送给对方,对方只要使用自己本地预埋好的权威机构的公钥对通信对方发来的数字证书进行验签,即可确认对方数字证书是否合法安全,进而确认证书中的对方的身份信息与其公钥是否可信。

因此,在不安全的网络环境中安全地进行通信的过程就变成了:

  1. 通信双方向CA申请数字证书。
  2. 通信双方在每次通信前相互发送数字证书,并验证对方数字证书是否合法安全,从而确认双方身份。
  3. 通信双方在每次通信前使用密钥协商算法协商出一组密钥,用于HMAC和对称加密的密钥/IV。
  4. 通信双方在这次通信接下来的消息交互中对每次交互的消息做对称加密,以及HMAC,从而保证消息的机密性与完整性。

这个过程,实际上就是TLS通信协议的基本思路。而其中第一步申请数字证书,就需要用到PKI了。


二、什么是数字证书与PKI

接下来,我们具体地学习数字证书与PKI。

2.1 什么是PKI

PKIPublic Key Infrastructure的缩写,意思是公钥基础设施。PKI利用公钥密码学技术为网络应用提供数字签名等密码学服务,并提供必需的密钥和证书管理体系。PKI是一个提供安全服务的基础设施,它是信息安全技术的核心,同时也是电子商务的关键和基础技术。注意,PKI不是一个具体的协议,也不是一个具体的软件,它是一个标准。依据这个标准所发展出的,以实现安全基础服务为目标的技术,都可以统称为PKI

当前PKI面向互联网大众的主要功能,可以用一句不那么精确的话来概括: PKI可以简单理解为管理数字证书的一套体系,包括数字证书的申请、创建、颁发与撤销。

PKI目前有一系列标准规范定义,主要包括:

  • IETF组织的Public Key Infrastructure X.509,简称PKIX
  • 国际电信联盟的ITU-T X.509标准。
  • RSA公司的Public-Key Cryptography Standards,简称PKCS

PKI的主要组件,以及运转过程如下图所示:

PKI主要有三个组件:

  • CA : Certificate Authority,证书授权中心,PKI核心组件服务,负责数字证书的创建、颁发与撤销。
  • RA : Registration Authority,证书登记机构,负责接受PKI用户发出的证书签名请求(Certificate Signing Request, CSR),并对申请者身份进行验证(通常是线下验证)。
  • VA : Validation Authority,证书验证机构,负责管理证书撤销列表(Certificate Revoke List, CRL),并提供CRL的下载服务。

PKI的运转过程:

  1. 证书申请。PKI用户A创建证书签名请求(CSR),提交给RACSR包括自己的身份信息、公钥以及其他相关信息。PKI用户可以是个人,也可以是组织。公私钥由PKI用户自行创建或以某种不会泄露的方式获取。比如某个公开网站要支持https的话,就需要先找相关机构(比如阿里云的SSL证书服务)服务申请数字证书。
  2. 请求签发证书。证书登记机构RA对申请者做身份验证,验证通过后,RACSR转发给证书授权中心CA。验证工作包括申请者身份的确认,证书用途调查等。
  3. 颁发证书。CA为申请者创建数字证书,并发送给申请者(这里就是PKI用户A)。数字证书的主要内容是申请者信息及其公钥,以及CA使用自己的私钥对证书主体做的签名。
  4. 更新CRLCA会不定期将一些自己认为不再安全的数字证书加入证书撤销列表CRL,通知VA更新CRL
  5. 发送数字证书。PKI用户A拿到CA颁发给自己的数字证书后,就可以在与B通信时,将自己的数字证书发给对方。
  6. 查验A的数字证书是否已是撤销证书。B在拿到A的数字证书后,会先从VA提供的撤销证书列表中查看A的数字证书是否在CRL中。
  7. 验证A的数字证书。A的证书不是已撤销证书的话,B再进一步使用CA的公钥来对A的数字证书做验签。CA的公钥来自CA的数字证书(CA证书),该证书被CA公开给大众使用,会事先预埋在操作系统或浏览器的信任证书列表中。

而B在验证了A的数字证书无误后,就可以安全地拿到A的公钥,进而做后续的处理,比如验证A的某个数字签名,与A做密钥协商等等。

在较老的使用RSA的TLS通信协议中,通信双方验证数字身份后,会拿对方的数字证书中的RSA公钥来继续做密钥协商。而最新的使用ECDHE的TLS通信协议中,通信双方验证对方数字证书仅仅用作身份验证,后续的密钥协商并不使用对方证书中的公钥,而是动态生成新的公私钥。

2.2 什么是数字证书

前面简单说了下数字证书的内容,这里给出其定义和结构。

数字证书就是通过数字签名实现的数字化的证书,它提供了一种在互联网或不安全的网络环境中验证身份的方法。

数字证书最开始的作用,是为了证明某个公钥的所属者到底是谁。而其引申作用,就是在互联网或不安全的网络环境中,证明证书持有者的身份是谁,类似于现实世界的身份证、护照、驾驶证等。

数字证书由CA创建并颁发,其主体内容包含证书拥有者的身份信息及其公钥,并由CA使用CA自己的私钥对证书主体进行签名。

数字证书的具体结构如下:

  • Version : 版本信息,证书结构的版本信息,目前的最新标准是X.509 V3,对应版本是3 (0x2)
  • Serial Number : 证书序列号,颁发者分配给证书的一个正整数,同一颁发者颁发的证书序列号各不相同,可用与颁发者名称一起作为证书唯一标识。
  • Signature Algorithm : 签名算法,颁发者给证书签名时使用的签名算法,通常是数字签名算法+散列算法,散列算法用于签名内容摘要的生成,如ecdsa-with-SHA384
  • Issuer : 颁发者,颁发该证书的机构名称,必须与颁发者自己的数字证书中的主体名一致。通常为CA服务器的名称。
  • Validity : 有效期,包含有效的起、止日期,不在有效期范围的证书为无效证书。
  • Subject : 持有者,证书拥有者的信息,如果与颁发者相同则说明该证书是一个自签名证书。自签名证书会在后续章节中继续学习。
  • Subject Public Key Info : 公钥信息,证书拥有者对外公开的公钥以及公钥算法信息。
  • X509v3 extensions : 扩展信息,通常包含了证书用途、CRL(撤销证书列表)的发布地址、证书拥有者的其他相关信息等可选字段。
  • Signature Value : 证书签名,由颁发者Issuer使用自己的私钥,对上述所有内容做数字签名,签名算法即上述内容中的Signature Algorithm

数字证书目前的最新标准是X.509 V3规范,参考文档:https://www.rfc-editor.org/rfc/rfc5280.html

使用openssl x509 -text --noout -in xxx.pem可以查看一个x509证书,例如查看github网站的ssl证书,显示内容如下:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            05:18:9a:54:eb:e8:c7:e9:03:e0:ab:0d:92:55:45:de
        Signature Algorithm: ecdsa-with-SHA384
        Issuer: C = US, O = DigiCert Inc, CN = DigiCert TLS Hybrid ECC SHA384 2020 CA1
        Validity
            Not Before: Mar 15 00:00:00 2022 GMT
            Not After : Mar 15 23:59:59 2023 GMT
        Subject: C = US, ST = California, L = San Francisco, O = "GitHub, Inc.", CN = github.com
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                    04:4a:b0:93:71:85:21:ec:62:3f:cb:74:c0:46:c8:
                    e7:00:dc:27:4a:32:4b:8a:d5:51:83:08:11:23:52:
                    65:c5:9d:64:75:94:10:9f:99:6d:3f:7b:fb:29:3b:
                    58:b8:37:54:78:4b:b7:3d:1c:77:7e:90:dd:bb:67:
                    23:32:5c:80:d1
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        X509v3 extensions:
            X509v3 Authority Key Identifier: 
                0A:BC:08:29:17:8C:A5:39:6D:7A:0E:CE:33:C7:2E:B3:ED:FB:C3:7A
            X509v3 Subject Key Identifier: 
                78:AA:72:C6:71:69:68:14:B5:59:B1:9E:8B:6E:2B:40:87:42:3B:1E
            X509v3 Subject Alternative Name: 
                DNS:github.com, DNS:www.github.com
            X509v3 Key Usage: critical
                Digital Signature
            X509v3 Extended Key Usage: 
                TLS Web Server Authentication, TLS Web Client Authentication
            X509v3 CRL Distribution Points: 
                Full Name:
                  URI:http://crl3.digicert.com/DigiCertTLSHybridECCSHA3842020CA1-1.crl
                Full Name:
                  URI:http://crl4.digicert.com/DigiCertTLSHybridECCSHA3842020CA1-1.crl
            X509v3 Basic Constraints: 
                CA:FALSE
            ...
    Signature Algorithm: ecdsa-with-SHA384
    Signature Value:
        30:65:02:30:28:95:86:a7:ed:ec:bf:ec:b4:19:af:d5:7a:92:
        44:d7:c4:fe:31:bf:02:a2:88:6e:68:c6:4e:87:0d:6e:e4:3b:
        92:dd:42:b3:b9:11:83:e6:f7:6f:70:bd:22:9b:b9:15:02:31:
        00:ce:dc:b4:62:ab:60:31:ad:d2:91:fc:88:5e:ab:cd:ba:7d:
        43:81:d0:5c:c6:57:90:dc:60:f9:ba:e3:5b:19:da:39:51:93:
        0c:dc:0d:1f:bd:38:f3:eb:3f:85:3f:5b:51

从上面展现出来的证书信息中,我们进一步学习其细节。

其中,IssuerSubject的定义支持以下属性:

  • C : 国家
  • O : 组织机构
  • OU : 单位部门
  • CN : 通用名,有时候是一个域名,如xxx.com
  • SERIALNUMBER : 序列号
  • L : 地区
  • ST : 省市
  • STREET : 街道
  • POSTALCODE : 邮编

上面的属性并不会全部使用,常用的是CSTLOOUCN,例如上面的证书中的Subject: C = US, ST = California, L = San Francisco, O = "GitHub, Inc.", CN = github.com,表示该证书属于米国的加里福利亚洲的洛杉矶的GitHub, Inc.组织,通用名为github.com。而Issuer: C = US, O = DigiCert Inc, CN = DigiCert TLS Hybrid ECC SHA384 2020 CA1表示该证书由米国的DigiCert Inc组织的DigiCert TLS Hybrid ECC SHA384 2020 CA1这个CA机构(实际对应一个CA服务)颁发。

另外,扩展信息Extensions中的属性都不是必须的,但有一些是常用属性,大部分数字证书都会提供这些扩展信息:

  • X509v3 Subject Key Identifier : 证书持有者密钥ID,可唯一标识本证书。
  • X509v3 Authority Key Identifier : 证书颁发者密钥ID,可唯一标识颁发者的数字证书,即父证书。
  • X509v3 Subject Alternative Name : SAN扩展信息,用于代替Subject中的CN属性,补充证书持有者的相关信息。其子属性有: DNSEmailAddressIPAddressURI,都可以写多个。注意,DNS支持通配符,例如*.xxx.com,如果设置了DNS属性,数字证书认证时,会根据证书中的DNS属性对通信目标的域名做检查。
  • X509v3 Key Usage : 证书用途,常见的用途如Digital Signature代表普通的数字证书,Certificate Sign代表证书签名人,即可以用作CA证书,等等。可以多个用途拼接起来,表示该证书可以有多种用途。
  • X509v3 Extended Key Usage : 证书扩展用途,相比X509v3 Key Usage更加详细的证书用途说明,比如TLS Web Server Authentication表示这个证书可以当作tls web通信的服务端证书(如https网站)。TLS Web Client Authentication表示该证书也可以当作tls web通信的客户端证书。
  • X509v3 CRL Distribution Points : 证书撤销列表的下载站点。供客户端查验证书是否被撤销时使用。
  • X509v3 Basic Constraints : 证书基础约束,其子属性为IsCAMaxPathLenIsCAfalse时,表示该证书不是CA证书,MaxPathLen无效。IsCAtrue时,表示该证书是CA证书,此时MaxPathLen表示该CA证书所属证书信任链中的中间CA证书的数量上限。

CA证书证书信任链将在2.3 证书信任链一节中介绍学习。

除了这些相对固定的扩展属性,X509证书还允许写入自定义属性,比如你有可能在某些证书的扩展属性中看到一个类似1.2.3.4.5.6.7.8.1的自定义属性,这些自定义属性往往是一些特殊业务场景使用数字证书做一些额外的业务处理,比如权限检查等等。

x509证书内容虽多,但其核心内容就是使用证书颁发者签署的证书签名来证明证书拥有者的身份和公钥,包括身份属实和公钥属实:

  • 证书拥有者,对应Subject
  • 证书拥有者的公钥,对应Subject Public Key Info
  • 证书颁发者,对应Issuer
  • 证书签名,对应Signature Value

2.3 证书信任链

前面描述通信双方如何认证对方的数字证书时,简化了认证过程,实际上:

  • 本地预埋的是可信的CA机构自己的数字证书(拥有者是CA机构),而不是直接预埋其公钥。这些数字证书是用来提供CA机构的公钥并证明其可信的,被称作CA证书,这些证书的扩展信息里会明确自己的用途是CA预埋通常就是操作系统或浏览器的信任证书列表。
  • 本地能预埋的CA证书是有限的,目前互联网上有大量的CA服务,其本身的权威性并不是线下公认的,并没有直接预埋到操作系统或浏览器里,它们自己的CA证书是由其他更权威的CA机构颁发的,即其证书的颁发者是其他CA机构,这些证书是中间CA证书。颁发者的CA证书就是父证书
  • 证书的颁发者一直往下追,尽头是一个自签名的CA证书,即这个CA的Issuer就是它自己的Subject Name,这种证书被称为CA根证书,也就是自签名证书。随便一个CA给自己弄个自签名的CA根证书在技术上是允许的,但通信双方是否信任这种自签名的CA证书,则取决于其本地有没有将这些CA根证书加入自己本地的证书信任列表。
  • 通信双方发送数字证书时,会将自己的数字证书及其所有的父证书一起打包为证书链发过去,而通信双方在验证对方数字证书时,实际上是在尝试构建一个证书信任链:根据当前证书的颁发者,去对方发来的证书链中找父证书,如果父证书不是本地信任的CA证书,则继续递归查找这个父证书的父证书,一直递归到某一层的父证书是本地预埋的信任的CA证书,则获取其公钥来对当前证书验签,验签通过则使用当前证书的公钥去验证上一层证书的签名,一层一层往上验签。这样就构建出一条完整的信任链,才算是认证成功。

因此,实际使用的数字证书,其实是一个证书信任链,如下图所示:

CA根证书的自签名使用的私钥(根私钥)非常重要,一旦泄露危害极大。所以事实上,那些能自签名且被互联网大众接受,在本地预埋了其CA根证书的CA,一般都不会直接用自己的根私钥给PKI用户颁发普通的数字证书,它们一般只给中间CA机构颁发中间CA证书,PKI用户通常只能向中间CA机构申请数字证书。

另外,这种证书信任链通常也不会太深,一般也就三层。以github的ssl证书为例,数字证书是github.com,其父证书是中间CA证书DigiCert TLS Hybrid ECC SHA384 2020 CA1,再追一层就是CA根证书DigiCert Global Root CA了。如下图所示:

github的ssl证书

2.4 编码与后缀

X509证书及其相关文件(包括公私钥文件,证书签名请求文件等等)通常都遵循ITU-T X509国际标准,它们通常有两种编码格式:

  • DER : 二进制编码格式。x509证书、公私钥等遵循ASN.1标准转换而来的字节数组。ASN.1是多种语言的数据结构描述标准,比如C,GO等。DERASN.1标准的数据结构的一种具体的编码实现。

  • PEM : 一种文本编码格式,基于DER字节数组做Base64编码,并在前后拼接固定的-----BEGIN <类型标签>----------END <类型标签>-----字符串。 常用的<类型标签>有:

    • CERTIFICATE x509证书
    • CERTIFICATE REQUEST 证书签名请求
    • PRIVATE KEY 私钥
    • PUBLIC KEY 公钥
    • X509 CRL 证书撤销列表

以x509证书为例,它的前后分别是-----BEGIN CERTIFICATE----------END CERTIFICATE-----,私钥文件则是-----BEGIN PRIVATE KEY----------END PRIVATE KEY-----。 另外,PEM格式支持对内容进行加密,一般使用某种对称加密算法。但像x509证书或公钥这种文件是不用加密的,只有包含了私钥的文件才需要选择是否加密。

目前大部分场景使用的都是PEM格式存储相关文件,前面用openssl x509命令查看的x509证书就是PEM格式文件。

X509证书及其相关文件的后缀名比较混乱,有的时候,DER格式会用.der做后缀,PEM格式会用.pem做后缀,但更多的时候是以文件内容区别后缀名:

  • .crt : x509证书文件,格式可能是PEM,也可能是DER。常见于类UNIX系统。
  • .cer : x509证书文件,格式可能是PEM,也可能是DER。常见于Windows系统。
  • .csr : 证书签名请求文件,格式可能是PEM,也可能是DER。
  • .key : 私钥文件,PEM格式。
  • .pub : 公钥文件,PEM格式。

注意,相关处理读取各类PEM格式文件时,并不以后缀名作为文件类型区分,而是用PEM文件中的前后缀来区分。

除了上述常见的文件后缀名之外,还有一些遵循其他标准如PKCS的文件后缀名:

  • .pfx/.p12 : 遵循PKCS#12标准的x509证书+私钥文件,二进制存储。linux上x509证书文件与私钥文件会分为两个不同的文件存储,但在windows的IIS服务上,往往将它们一起存储在一个.pfx.p12文件中。这种文件包含私钥,因此提供了加密保护,提取时需要提取密码。
  • .jks : Java Key Storage文件,利用java的keytool工具生成,或将.pfx文件转换而来。
  • .p10 : 证书签名请求文件,遵循PKCS#10标准。
  • .p7r : 证书签名请求的回复,遵循PKCS#7标准。
  • .p7b/.p7c : 证书链,遵循PKCS#7标准。

PKCSPublic-Key Cryptography Standards的缩写,是由 RSA 实验室与其它安全系统开发商为促进公钥密码学的发展而制订的一系列标准。

  • PKCS#7 : Cryptographic Message Syntax Standard
  • PKCS#10 : Certification Request Standard
  • PKCS#12 : Personal Information Exchange Syntax Standard

三、小结

数字证书是为了证明某个公钥属于某个实体(个人或组织),PKI则是数字证书的管理体系。它们解决了互联网环境下的信任问题,是互联网环境下安全通信的基石,但只有它们还不能完成安全通信。数字证书与PKI配合TLS通信协议,才能实现在互联网环境下的安全通信。