
一、前置知识1.根证书、中间证书、叶子证书、证书链证书链可以理解成一个“层层担保的信任关系”。根证书 Root CA ↓ 签发 中间证书 Intermediate CA ↓ 签发 叶子证书 Leaf Certificate用户真正使用的一般是叶子证书但系统不会单独相信它而是沿着证书链一路验证到一个已经被信任的根证书。①根证书根证书就是信任链的最顶层证书。它通常属于一个 CA也就是证书颁发机构。根证书的特点是它通常是自签名证书它的公钥预先内置在操作系统、浏览器、设备或协议栈里它是信任的起点也叫Trust Anchor信任锚所谓自签名就是根证书用自己的私钥给自己的证书签名②中间证书中间证书是由根证书签发的下一级 CA 证书。为什么不直接用根证书签发所有叶子证书主要原因是安全和管理根证书私钥非常重要通常离线保存日常签发证书交给中间 CA如果中间 CA 出问题可以吊销中间证书不必动最顶层的根证书中间证书本身也有一对密钥中间 CA 私钥用来签发下级证书中间 CA 公钥放在中间证书里供别人验签③叶子证书叶子证书是证书链最底层的证书。它不是用来继续签发别的证书的而是给具体对象使用的。例如访问 HTTPS 网站时服务器给你的主要就是它自己的叶子证书。通常包含主体名称 公钥 有效期 用途 签发者 签名算法 签名值 www.example.com如果是网站证书的话④证书链证书链就是从叶子证书一路连接到根证书的完整路径。网站叶子证书 ↓ 由 Intermediate CA 签发 中间证书 ↓ 由 Root CA 签发 根证书 ↓ 系统已经信任 可信根实际发送时服务器通常会发送叶子证书 中间证书但一般不会发送根证书因为根证书通常已经在客户端系统里了。那怎么验证证书链呢假设客户端收到这个链Leaf Cert Intermediate Cert Root Cert首先验证叶子证书是谁签的叶子证书里写着Issuer Intermediate CA客户端就用中间证书里的公钥去验证叶子证书的签名。如果验签成功说明这个叶子证书确实是 Intermediate CA 签发的其次验证中间证书是谁签的中间证书里写着Issuer Root CA客户端用根证书里的公钥去验证中间证书的签名。如果成功说明这个中间证书确实是 Root CA 签发的最后检查根证书是否可信最后客户端看这个 Root CA 是否在自己的可信根证书库里。如果在就说明这条链可以信任。注意证书链不仅只是眼前还有~证书必须在有效期内~域名或身份是否匹配例如访问https://www.example.com证书里必须包含这个域名。否则即使证书链可信也不能通过。~证书用途是否正确例如服务器证书要有 Server Authentication客户端证书要有 Client AuthenticationCA 证书要允许签发证书不能拿一个普通叶子证书去签发别的证书。~是否被吊销证书可能还没过期但已经被吊销。吊销检查通常通过CRLOCSP⑤根证书(上一级证书)是怎么用私钥签发下一级证书的根证书签发下一级证书的过程如下下一级 CA 生成自己的密钥对下一级 CA 私钥自己秘密保存下一级 CA 公钥准备交给根 CA 认证根 CA 制作下一级证书的正文里面通常包含下一级 CA 的名称下一级 CA 的公钥有效期序列号CAtrue允许签发证书等权限签发者是哪个根 CA根 CA 对证书正文计算摘要证书正文 - SHA-256 - 一小段摘要4.根 CA 使用自己的私钥签名摘要根 CA 私钥 摘要 - 数字签名5.证书正文和签名组合起来下一级证书 下一级信息 下一级公钥 根 CA 的数字签名注意根 CA 私钥不是用来加密整个证书也不会放进证书里。它只负责生成数字签名。收到下一级证书的人可以拿根证书中的公钥验证签名根证书公钥 | v 验证下一级证书上的签名 | -- 验证成功证书确实由这个根 CA 签发内容没有被修改 | -- 验证失败签发者不可信或者证书内容被修改最终证书链类似根 CA 证书 用根 CA 私钥签发 | v 中间 CA 证书 用中间 CA 私钥签发 | v 车辆/充电桩/合同证书根证书比较特殊它通常是自签名证书也就是根 CA 使用自己的私钥给自己的证书签名。它之所以可信不是因为“自己给自己签名”而是因为它已经通过操作系统、设备固件或者项目配置被预先放进了受信任证书库。这里下一级公钥的作用是如果下一级证书仍然是中间证书那它的公钥就是用来验证证书链的。如果下一级证书是叶子证书那它的公钥就是用来验签被叶子证书私钥加密的TLS数据的。而下一级私钥的作用是如果下一级私钥是中间证书生成的私钥那它的私钥是用来签发下一级证书的以及签名自己的CSR的上一级CA系统能用下一级的公钥验签。下一级实体 上一级 CA 系统 | | | 1. 生成自己的公钥和私钥 | | | | 2. 生成 CSR 证书签名请求 | |----------------------------------| | | 3. 审核身份和权限 | | 4. 用上一级 CA 私钥签名 | 5. 返回签发好的证书 | |----------------------------------|CSR 通常包含下一级的身份信息 下一级的公钥 下一级使用自己私钥生成的签名CSR 中的签名用于证明请求者确实持有与这个公钥对应的私钥不是随便拿了别人的公钥来申请。需要特别注意下一级只把公钥交给上一级 CA通常不会交出自己的私钥。上一级 CA 用自己的私钥签发但不会把上一级私钥交给下一级。下一级收到证书后证书必须和自己原来生成的私钥配套使用。⑥那如果需要对数据进行签名该用谁的私钥证书链 公钥证书链可以给别人 私钥 签名用的秘密不能给别人比如说如果你是SECC/EVSE 端TLS 里通常用的是SECC Certificate SECC Private Key应该是拿自己的SECC 私钥做 TLS 握手签名或密钥交换认证。而不会拿V2G ROOT去签普通通信数据。如果你是EVCC/车辆端 Plug Charge应用层签名通常用的是Contract Certificate Contract Private KeyV2G Root 私钥只应该由V2G Root CA持有。它的作用是签发或授权下级证书例如Root CA 私钥 - 签 Sub-CA 证书 Sub-CA 私钥 - 签 SECC/Contract 叶子证书 叶子私钥 - 做 TLS 或应用层签名 你能拿到Root 证书、公钥证书链、你自己的叶子证书、你自己的叶子私钥 你拿不到Root CA 私钥、别人的 Sub-CA 私钥更准确的说法应该是用自己的叶子证书对应的私钥签名然后附带 V2G Root 证书链给对方验证。签名用 SECC 私钥 / Contract 私钥 验证对方用证书链一路验证到 V2G Root“你能拿到Root 证书、公钥证书链、你自己的叶子证书、你自己的叶子私钥。 我自己的叶子私钥是怎么拿到的发送给对方时对方通过叶子证书的上一级证书的公钥来验签么但是上一级的公钥不是证书链里的吗首先要清除你的叶子私钥是怎么拿到的正常流程里叶子私钥不是 CA 发给你的而是你自己生成的。假设你要申请叶子证书。 1. 你本地生成密钥对 Leaf Private Key // 自己保存 Leaf Public Key // 放进 CSR 2. 你生成 CSR CSR 里面包含 你的公钥 你的身份信息 你用叶子私钥生成的 CSR 签名 也就是说CSR 本身会被你的 叶子私钥 签名。 3. 中间 CA 收到 CSR 后验签 中间 CA 会用 CSR 里的公钥验证 CSR 签名 Verify(CSR_public_key, CSR内容, CSR签名) 这一步的目的不是为了信任你而是为了确认 你确实拥有这个公钥对应的私钥CSR 内容没有被篡改 也就是证明申请证书的人确实持有对应的叶子私钥。 4. 中间 CA 生成叶子证书 中间 CA 把你的公钥和身份信息放进证书 Leaf Certificate { subject 信息 leaf public key validity 有效期 key usage 用途 issuer Intermediate CA ... } 5. 中间 CA 用自己的私钥签名证书 证书最终是证书内容 中间 CA 的签名 注意中间CA不会把它的公钥发回来因为在验证时可以直接用中间CA的公钥来验证leaf证书CA 正常只拿到你的公钥不会拿到你的私钥。中间 CA 会用 CSR 里的公钥来验证 CSR 的签名然后用中间 CA 的私钥对新证书做“签名”然后发过来(证书包含公钥)不是“加密证书”。证书本身通常是公开的不需要加密。CSR(Certificate Signing Request)证书签名请求。你想申请一张正式数字证书时先生成一个 CSR 发给 CA证书颁发机构CA 根据它来给你签发证书。CSR 公钥 身份信息 你自己的签名 申请者公钥 主体信息例如域名、组织名、国家、省市等 签名算法信息 申请者自己的签名用来证明这个请求确实由对应私钥持有者发起它和正式证书的区别CSR申请材料还没被 CA 签发CertificateCA 已签名确认的正式证书其次要知道对方收到你的证书链后怎么验这里要分两种“签名”。假设发送data signature leaf certificate sub-ca certificate对方会做两类验证。第一种. 验证证书链Sub-CA 公钥 - 验证 Leaf Certificate 是不是 Sub-CA 签的 Root 公钥 - 验证 Sub-CA Certificate 是不是 Root 签的 本地信任库 - 检查 Root 是不是可信根 1. 用 Intermediate CA 证书里的公钥 验证 Leaf Certificate 的签名 2. 用 Root CA 证书里的公钥 验证 Intermediate CA 证书的签名 3. 检查 Root CA 是否在本地可信根证书库里这一步的目的确认 leaf certificate 是可信 CA 签发的第二种. 验证数据签名Leaf Certificate 里的公钥 - 验证 data 的 signature这一步的目的确认 data 确实是 leaf private key 持有者签的总结1. 用证书链验证你的叶子证书可信不可信 2. 用叶子证书里的公钥验证你对数据/握手参数的签名⑦完整的申请过程假设证书层级是ROOT ↓ SUB1 ↓ SUB2 ↓ Leaf你现在要向直接上一级SUB2申请 Leaf 证书完整过程如下。第一步生成 Leaf 密钥对Leaf 私钥自己保存不能发送给 CA Leaf 公钥放进 CSR第二步生成 CSR你不需要分别发送“CSR、签名、公钥”因为一个完整的 CSR 已经包含CSR ├─ 申请者信息 ├─ Leaf 公钥 ├─ 申请的扩展和用途 ├─ 签名算法 └─ 使用 Leaf 私钥生成的签名可以理解为CSR 申请信息 Leaf 公钥 Leaf 私钥签名第三步SUB2 验证 CSRSUB2从 CSR 中取出 Leaf 公钥用它验证 CSR 签名。验签成功只能证明申请者持有与 CSR 公钥对应的私钥CSR 没有被修改。它不能单独证明申请者的真实身份。SUB2还要通过账户、设备身份、审批流程等方式判断是否允许签发。第四步SUB2 生成 Leaf 证书审核通过后SUB2生成证书正文Leaf 证书正文 ├─ Leaf 身份 ├─ Leaf 公钥 ├─ 签发者 SUB2 ├─ 序列号 ├─ 有效期 └─ 密钥用途然后Leaf 证书正文 ↓ 计算摘要 使用 SUB2 私钥签名 ↓ 生成 Leaf 证书注意签发完成后Leaf 证书里面是Leaf 公钥Leaf 证书上的签名由SUB2 私钥生成Leaf 证书里不包含 SUB2 私钥Leaf 私钥仍然只保存在你这里第五步SUB2 最后返回什么比较完整的返回应该是1. Leaf 证书 2. SUB2 证书 3. SUB1 证书通常不需要返回 ROOT因为 ROOT 应该提前放在你的可信证书库里CA 返回 [Leaf] [SUB2] [SUB1] 设备本地预置 [ROOT]不过具体接口也可能只返回 Leaf要求你已经保存或从其他可信渠道获取 SUB 证书。收到后你这样验证ROOT 公钥 ↓ 验证 SUB1 证书签名 SUB1 公钥 ↓ 验证 SUB2 证书签名 SUB2 公钥 ↓ 验证 Leaf 证书签名 Leaf 公钥 ↓ 验证 Leaf 私钥签名的业务数据最后你本地实际需要保存Leaf 私钥 你申请前生成的绝不能泄露 Leaf 证书 SUB2 返回 SUB2、SUB1 证书 CA 返回或预先配置 ROOT 证书 本地预置的可信根最关键的一句话你只向 CA 发送 CSR不发送 Leaf 私钥CA 返回 Leaf 证书和必要的中间证书链不会返回你的私钥。注SUB下发的leaf证书中包含了leaf的公钥的作用Leaf 证书 ├─ Leaf 身份 ├─ Leaf 公钥 └─ SUB2 的数字签名作用1从 Leaf 证书中取出 Leaf 公钥使用 Leaf 公钥验证业务数据的签名作用2CA 签名覆盖了整个证书正文其中包括 Leaf 公钥证书正文 ├─ Leaf 身份 ├─ Leaf 公钥 A ├─ 有效期 └─ 密钥用途 ↓ SUB 私钥签名如果把公钥 A 换成公钥 B原始身份 公钥 A 有效期 - CA 签名有效 修改身份 公钥 B 有效期 - CA 签名验证失败这些过程对应的报文会在另一篇文章讲解。2.对称加密和非对称加密对称加密和非对称加密区别核心就一句话对称加密加密和解密用的是同一把密钥非对称加密加密和解密用的是一对不同的密钥(公钥和私钥)现实里通常不是“只用一种”。因为对称加密快适合加密大量业务数据非对称加密方便适合密钥交换和身份认证所以常见做法是先用非对称加密或密钥交换机制安全地协商出一个对称密钥后续大量数据都用这个对称密钥加密这就是很多协议的常见方式比如 TLS。混合对称加密现代算法ECDHEECDHE 不负责加密大量数据它只负责协商共享秘密AES 才负责加密后续业务数据。①.ECDHE(基于椭圆曲线的临时密钥交换算法)让通信双方在不直接传输密钥的情况下协商出同一个共享密钥。这个共享密钥后续再用来派生 AES等对称加密密钥。假设客户端和服务器要建立加密通信后续数据需要用对称密钥加密。问题是双方怎么安全地得到同一把对称密钥不能直接这样发客户端 - 服务器这是密钥 K因为中间人抓包就能拿到 K。ECDHE 的做法是双方各自生成一部分秘密只在网络上传输公开参数最后双方都能算出同一个共享秘密但这个共享秘密从未在网络上传输。客户端与服务端各自生成自己的私钥这两个值是不能发出去的。(注意与leaf的私钥区分)客户端临时私钥a 服务器临时私钥b然后用私钥乘以椭圆曲线上的基点 G 得到各自的公钥。公钥是可以发出去的。客户端临时公钥A aG 服务器临时公钥B bG这里的G是椭圆曲线预定义的基点双方约定的椭圆曲线secp256r1 / prime256v1 / P-256。然后客户端用自己的私钥与服务端的公钥计算共享秘密S aB同理服务端用自己的私钥与客户端的公钥计算共享秘密S bA最后双方算出来的S值是相同的aB a(bG) abG bA b(aG) abG中间人虽然能看到G A aG B bG但因为不知道a和b所以算不出abG。用共享秘密派生会话密钥。实际协议不会直接拿S当 AES 密钥而是会经过 KDF(PRF)也就是密钥派生函数。例如会话密钥 KDF(S, client_random, server_random, handshake_hash)最终派生出- client_write_MAC_key - server_write_MAC_key - client_write_key - server_write_key - client_write_IV - server_write_IV后续数据再用这些密钥进行对称加密。这几个密钥材料是怎么通过key block派生出来的以 TLS 1.2 为例双方先计算足够长的key_blockkey_block PRF(master_secret, key expansion, ServerRandom || ClientRandom)需要的总长度是2 × MAC密钥长度 2 × 加密密钥长度 2 × 固定IV长度PRF 输出足够多的伪随机字节后TLS 按固定顺序从前向后切分key_block ├─ client_write_MAC_key ├─ server_write_MAC_key ├─ client_write_key ├─ server_write_key ├─ client_write_IV └─ server_write_IV不是分别调用六次 PRF而是生成一段足够长的key_block后按照密码套件规定的长度依次截取。------------------------------------------------------------------------------------------------------------------------------在 TLS 1.2 中PRF 实际上承担 KDF密钥派生函数的作用。概念上KDF 输入一个秘密和一些上下文信息 派生出一个或多个新密钥TLS 1.2 的 PRFPRF(secret, label, seed)输入包括secret基础秘密例如pre_master_secretlabel用途标识例如master secretseed上下文数据例如ClientRandom ServerRandom输出看起来像随机数据用作新的密钥材料master_secret PRF(pre_master_secret, master secret, ClientRandom ServerRandom)再继续派生key_block PRF(master_secret, key expansion, ServerRandom ClientRandom)TLS 1.2 的 PRF 内部通常基于 HMAC-SHA256 或 HMAC-SHA384基础秘密 label seed ↓ HMAC 反复计算 指定长度的伪随机密钥材料严格地说PRF描述一种伪随机函数。KDF描述“派生密钥”这个用途。TLS 1.2 使用 PRF 构造完成 KDF 的工作。pre_master_secretECDHE 共享秘密 master_secret会话的主密钥材料 write_key真正保护 TLS 业务数据的会话加密密钥因此可以理解为TLS 1.2 PRF 是 TLS 1.2 使用的 KDF 实现。TLS 1.3 不再使用 TLS 1.2 PRF而是改用更明确的HKDF-Extract和HKDF-Expand。------------------------------------------------------------------------------------------------------------------------------注意ECDHE 里的密钥对不是长期使用的而是每次连接都重新生成。例如第一次 TLS 连接生成 a1、b1 第二次 TLS 连接生成 a2、b2 第三次 TLS 连接生成 a3、b3临时密钥对很重要(就是前向安全性)因为即使服务器的长期私钥将来泄露了攻击者也不能解密以前抓到的通信内容。因为以前的会话密钥来自当时临时生成的这些临时私钥用完就丢了。攻击者即使后来拿到了服务器证书私钥也拿不到以前的 ECDHE 临时私钥b所以无法还原以前的会话密钥。一般TLS里不会只用ECDHE还要结合证书和签名。假设没有身份认证。客户端以为自己在和服务器协商密钥但中间人可以插进来客户端 - 中间人 - 服务器中间人分别和客户端、服务器做两次 ECDHE客户端 和 中间人 协商出密钥 K1 中间人 和 服务器 协商出密钥 K2这样中间人就能解密、修改、再加密转发。所以 TLS 中不会只用 ECDHE。而结合证书和签名这样中间人就不能随便替换服务器的 ECDHE 公钥。1. 服务器发送证书 2. 证书里有服务器长期公钥 3. 服务器生成 ECDHE 临时公钥 B 4. 服务器用自己的长期私钥对 B 和握手参数签名 5. 客户端用证书里的公钥验签 这个 ECDHE 临时公钥 B 确实是合法服务器发来的②数字签名和加密不是一回事很多人会把非对称加密和签名混在一起。加密解决的是别人看不懂内容签名解决的是别人知道内容是谁发的且内容没被篡改非对称密钥除了“公钥加密、私钥解密”还常用于私钥签名公钥验签这个作用不是保密而是证明消息确实是某人发的消息没被改过所以保密公钥加密私钥解密认证/签名私钥签名公钥验签3.数字签名用来证明“这条数据确实是我发的而且中途没被改过”的一种密码学机制。数字签名主要解决两个问题证明是谁发的接收方可以验证这份数据是不是持有某个私钥的人发出来的证明数据没被改如果数据中途被改了验签会失败。所以签名的重点不是保密而是身份认证完整性校验不可否认性数字签名通常和非对称密钥一起使用私钥自己保密用来签名公钥可以公开别人用来验签发送方 先对原文做哈希得到一个摘要 再用自己的私钥对这个摘要做签名 把“原文 签名”一起发出去 接收方 收到原文和签名 也对原文做同样的哈希 再用发送方的公钥去验证签名 如果验证通过说明 数据确实对应这个私钥持有者 数据没被改哈希运算(SHA-256)的目的是原文可能太大做非对称加密非常耗时。原文 -- 哈希 -- 摘要 -- 私钥签名①ECDSA(Elliptic Curve Digital Signature Algorithm)椭圆曲线数字签名算法它不是用来加密数据的而是用来做私钥签名公钥验签。ECDSA 需要这几个关键元素椭圆曲线参数比如 secp256r1 基点 G 曲线阶 n 私钥 d 公钥 Q 哈希算法比如 SHA-256 待签名数据 message其中私钥 d一个随机大整数自己保存不能泄露 公钥 Q由私钥计算出来可以公开公钥计算方式是Q dG这里的G是椭圆曲线上的基点dG表示椭圆曲线点乘运算。ECDSA 签名整体流程假设你要对一段数据message签名。签名端假设Leaf 私钥d Leaf 公钥Q d × G 业务数据M先对业务数据计算摘要M ↓ SHA-256 摘要 e然后生成一次性随机数k临时点 R k × G r R 的横坐标 mod n s k⁻¹ × (e r × d) mod n最终数字签名就是两个整数Signature (r, s)发送给接收方的是业务数据 M 数字签名 (r, s) Leaf 证书私钥d和临时随机数k都不能发送。验签端接收方先从验证通过的 Leaf 证书中取出公钥Q d × G然后对收到的业务数据重新计算摘要收到的数据 M ↓ SHA-256 摘要 e再根据公钥、摘要和签名计算w s⁻¹ mod n u1 e × w mod n u2 r × w mod n P u1 × G u2 × Q最后比较P 的横坐标 mod n r相等则验签成功。为什么能验证成功因为s k⁻¹ × (e r × d) Q d × G把它们代入验签公式P (e/s) × G (r/s) × Q ((e r×d)/s) × G k × G这正好重新得到了签名时的临时点R所以横坐标能与签名中的r相等。数据被修改会怎样如果攻击者修改业务数据原数据 M - 摘要 e 修改数据 M - 摘要 e验签端计算出的点就不再是原来的k × G最终计算出的横坐标 ! r验签失败。如果攻击者换了公钥公式中的Q发生变化同样会验签失败。在 TLS 1.2ECDHE_ECDSA中大致就是ClientRandom ServerRandom ECDHE 参数和临时公钥 ↓ SHA-256 使用 Leaf 私钥生成 ECDSA 签名 ↓ 客户端使用 Leaf 证书公钥验签注意ECDSA 最危险的点随机数 kECDSA 对随机数k非常敏感。如果k泄露了私钥可以被算出来。如果同一个k被重复用于两条不同消息私钥也可能被算出来。这是 ECDSA 里非常经典、非常严重的问题。为什么 k 重复会出事两次签名如果用了同一个ks1 k⁻¹(e1 r·d) s2 k⁻¹(e2 r·d)攻击者可以通过两个签名反推出k再反推出私钥d。所以工程上一定要保证每次签名的 k 不能重复。很多现代实现会用确定性 ECDSA根据私钥和消息摘要生成k避免随机数质量差导致私钥泄露。4.身份认证认证身份的核心机制通常就是“签名 验签”但完整身份认证不只是签名本身还要配合证书链验证。可以这样理解签名证明“对方确实拥有某个私钥”证书证明“这个私钥对应的公钥属于谁”。只验签不验证证书链行不行不行。因为如果攻击者自己生成一对密钥攻击者私钥 PriM 攻击者公钥 PubM他当然也能用PriM签名并用PubM验签成功。但这只能证明攻击者拥有 PriM不能证明攻击者就是合法服务器/合法设备所以必须验证证书链确认这个公钥是被可信 CA 认证过的。5.单向认证与双向认证单向认证客户端验证服务器身份但服务器不验证客户端证书。客户端 ---------------- 服务器证书 客户端验证服务器证书 客户端确认我连的确实是这个服务器客户端会验证服务器发来的证书证书链是否可信域名是否匹配证书是否过期证书用途是否正确证书是否被吊销如果验证通过客户端就相信我连接的是合法服务器不是冒充者。但服务器通常并不知道客户端是谁。客户端身份可能后续再通过用户名密码tokencookieOAuthAPI key来认证。双向认证客户端验证服务器身份服务器也验证客户端身份。服务器 --------服务器证书-------- 客户端 客户端验证服务器证书 客户端 --------客户端证书-------- 服务器 服务器验证客户端证书单向认证中服务端只知道有客户端连上来了而双向认证里服务器也要求客户端出示证书所以没有合法客户端证书的人即使知道服务器地址也无法通过认证。单向认证里有没有客户端身份认证可以有但不靠 TLS 客户端证书。比如普通网站TLS 单向认证浏览器确认服务器可信TLS 加密通道建立用户输入账号密码服务器通过账号密码识别用户。所以它也能认证客户端身份只是不是在 TLS 握手阶段通过证书认证。6.OCSPOCSP全称是Online Certificate Status Protocol中文常叫在线证书状态协议它的作用是实时查询一张证书有没有被吊销。证书不是只要没过期就一定有效。比如中途发生了这些情况私钥泄露 证书签发错误 证书持有者不再可信 设备/服务器被注销那么这张证书虽然还没过期也应该被废掉。证书吊销 RevocationOCSP 查询的是OCSP Responder也就是证书状态查询服务器。一般由 CA 或相关证书服务机构提供。证书里通常会带一个地址告诉客户端要查这张证书状态请去这个 OCSP 地址这个地址一般在证书扩展字段里属于Authority Information AccessAIA流程 假设客户端收到服务器证书。 第一步客户端验证证书链 服务器叶子证书 ↓ 中间证书 ↓ 根证书 第二步客户端向 OCSP 服务器查询 客户端问这张证书的序列号是 xxx它现在还有效吗 第三步OCSP 服务器返回状态 常见返回状态有 good 证书状态正常 revoked 证书已被吊销 unknown 不知道这张证书 如果返回 revoked客户端就应该认为这张证书不可用。OCSP 一般不是把整张证书发过去而是根据证书信息构造请求例如证书序列号 签发者名称 hash 签发者公钥 hashOCSP Responder 根据这些信息查询证书状态。OCSP也是有优缺点的。优点是查询更轻量更实时不需要下载整张 CRL(证书吊销列表)缺点是1. 依赖网络如果客户端必须在线查询 OCSP那么网络不好时可能会影响连接建立。2. 隐私问题如果客户端每次访问某个网站都去问 CA这个网站证书有效吗那么 OCSP 服务器可能知道用户正在访问哪些站点。3. 性能问题每次 TLS 握手都额外查询 OCSP会增加延迟。为了解决上面的问题有一种机制叫OCSP Stapling中文可以理解成服务器预先查询好 OCSP 结果然后在 TLS 握手时一起发给客户端。也就是服务器先向 OCSP Responder 查询自己的证书状态 拿到带签名的 OCSP 响应 TLS 握手时把这个响应一起发给客户端 客户端直接验证这个 OCSP 响应这样客户端就不用自己再去访问 OCSP 服务器了。优点减少客户端网络请求提高握手速度减少隐私泄露降低 OCSP Responder 压力OCSP 响应通常会被 OCSP Responder 签名。客户端会验证这个 OCSP 响应是不是可信机构签的 响应有没有过期 响应对应的是不是当前这张证书 状态是不是 good/revoked/unknown所以不是随便一个服务器说“我的证书是 good”客户端就信。完整证书验证大概包括1. 验证证书链 2. 检查证书有效期 3. 检查证书用途 4. 检查域名/身份是否匹配 5. 检查证书是否被吊销7.各密钥材料作用这几个密钥材料的作用是什么密钥材料发送方用途client_write_MAC_key客户端为客户端发送的记录计算 MACserver_write_MAC_key服务端为服务端发送的记录计算 MACclient_write_key客户端加密客户端发送的 TLS 数据server_write_key服务端加密服务端发送的 TLS 数据client_write_IV客户端构造客户端方向的 IV/Nonceserver_write_IV服务端构造服务端方向的 IV/NonceMAC 密钥在传统非 AEAD 密码套件中发送端计算HMAC( write_MAC_key, TLS记录序号 || 类型 || 版本 || 长度 || 明文 )大致发送明文 MAC ↓ 使用 write_key 加密 密文MAC 用于检查数据是否被修改记录顺序是否异常记录类型和长度是否被篡改加密密钥client_write_key和server_write_key是真正用于保护 TLS 业务数据的对称密钥。例如使用 AESclient_write_key 客户端加密服务端解密 server_write_key 服务端加密客户端解密两个方向使用不同密钥避免双向数据共用同一套密钥材料。AEAD 套件的区别如果使用AES-GCM、AES-CCM或ChaCha20-Poly1305client_write_MAC_key 长度 0 server_write_MAC_key 长度 0因为 AEAD 使用write_key同时完成加密 - 机密性 认证标签 - 完整性例如 TLS 1.2 的AES_128_GCMclient_write_key 16 字节 server_write_key 16 字节 client_write_IV 4 字节固定部分 server_write_IV 4 字节固定部分 MAC key 0 字节 key_block 总长度 16 16 4 4 40 字节双方之所以能得到完全相同的这些密钥是因为双方都拥有相同的master_secret ServerRandom ClientRandom这几个密钥材料与Finished报文有什么关系Finished 字段字段作用verify_data基于master_secret和之前所有握手消息摘要计算出来的校验值。verify_data 的意义客户端和服务器会分别计算client finishedserver finished如果对方收到后验算通过说明主密钥一致握手消息历史一致这次握手可以正式成立Finished的验证值不由这些密钥材料直接生成但Finished报文本身会使用这些密钥进行加密和完整性保护。如果使用传统的 CBCHMAC 套件客户端发送 client_write_MAC_key 计算 MAC client_write_key 加密 client_write_IV 提供 IV 服务端接收 使用相同材料验 MAC 和解密如果使用AES-GCM等 AEAD 套件client_write_MAC_key 和 server_write_MAC_key 不存在长度为 0 write_key IV/Nonce 同时完成加密和完整性认证与 Finished 的关系master_secret会分成两条派生路径master_secret / \ / \ 生成 key_block 生成 Finished ↓ ↓ write_key、MAC_key、IV verify_dataFinished中的verify_data大致这样计算客户端 Finished verify_data PRF(master_secret, client finished, Hash(之前所有握手报文))服务端使用不同标签服务端 Finished verify_data PRF(master_secret, server finished, Hash(之前所有握手报文))所以Finished的验证值直接依赖master_secret之前所有 TLS 握手报文的摘要client finished或server finished标签它不直接使用client_write_key等材料计算。Finished 如何发送客户端计算出verify_data后Finished 明文 ↓ 使用 client_write_* 保护 加密后的 TLS Record ↓ 发送给服务端服务端接收时分两步1. 使用 client_write_key、MAC_key、IV 解密并验证 TLS Record 完整性 2. 使用 master_secret 和握手报文摘要 重新计算 Finished.verify_data 并比较服务端发送自己的Finished时则使用server_write_*。因此Finished有两层检查Record 层检查 MAC 或 AEAD Tag 是否正确 证明当前 TLS 记录没有被修改 Finished 层检查 verify_data 是否正确 证明双方拥有相同 master_secret 并且看到的整个握手过程一致TLS 1.2 中Finished通常是切换到新密码参数后第一个受到新会话密钥保护的握手报文。只要 Record 校验或verify_data任意一个失败TLS 握手都会失败。注意服务端在收到客户端发送的finished报文后服务端也会计算一份“客户端应该生成的verify_data”然后与客户端发来的值比较。双方完成密钥协商后都拥有相同的master_secret双方也都保存了此前收到和发送的全部 TLS 握手报文因此应该得到相同的握手摘要Hash(ClientHello ServerHello Certificate ...)客户端计算客户端发送Finished前计算client_verify_data PRF(master_secret, client finished, Hash(客户端 Finished 之前的握手报文))然后把这个值放入Finished报文并使用client_write_*加密发送。服务端计算服务端收到并解密客户端的Finished后自己计算expected_client_verify_data PRF(master_secret, client finished, Hash(客户端 Finished 之前的握手报文))然后比较收到的 client_verify_data 服务端计算的 expected_client_verify_data比较成功说明客户端和服务端拥有相同的master_secret。双方看到的 TLS 握手报文完全一致。握手报文在传输过程中没有被修改。如果中间人修改了某个握手报文客户端握手摘要 ! 服务端握手摘要最终计算出的verify_data就不同。服务端 Finished 也是一样服务端随后计算自己的server_verify_data PRF(master_secret, server finished, Hash(服务端 Finished 之前的握手报文))客户端收到后也计算期望值并比较。所以双方都能计算对方的verify_data服务端验证客户端 Finished 客户端验证服务端 Finished这不会导致冒充因为网络攻击者没有master_secret无法计算正确的verify_data。Finished不是数字签名而是一种基于双方共享秘密的握手完整性校验和密钥确认机制。