KMSEnvelope Encryption
目录
- 引子
- KMS 的核心思想:信封加密(Envelope Encryption)
- 信封加密
- 工作原理
- CA机构算不算一个KMS
- 🧾 一、CA 做的事情:建立「身份信任」
- 🔐 二、KMS 做的事情:管理「密钥本身」
- 🧩 三、TLS 握手中两者的关系
- ⚖️ 四、在大型系统中它们如何协同
- 加密算法层面
- KMS 内部通常使用两类算法
- 🔐 非对称加密场景(RSA 模式)
- ⚙️ 对称加密场景(AES-KW 模式)
- 🧱这两种模式在 KMS 内部如何共存
- 现代 KMS 设计的核心演化逻辑
- HSM 是什么?
- 为什么要这么麻烦?
- 延伸概念
- 业界知名的KMS
- 云厂商KMS
- 本地部署KMS
- 轻量级别KMS
- Vault 快速开始
- DEK密钥轮换
引子
在许多工程团队里,密钥管理常常从最简单也最危险的做法开始:把密钥硬编码在源代码里。看似方便——代码跑得起来、调试也简单——但一旦代码库被复制、打包或泄露,密钥也随之裸奔。于是,大家又学会了把密钥从代码里抽离,改成在 Dockerfile 或 Kubernetes 的 YAML 里通过环境变量注入:ENV SECRET=...
或在 Pod spec 里写 env:
。表面上安全了些,实际上问题并没有根本解决——构建历史、镜像层、CI 日志以及节点的进程环境都可能泄露这些“运行时”秘密。
接着出现的“改进方案”通常是用 ConfigMap 或 Secret 把配置和凭据集中管理,并在容器中以文件或环境变量挂载。这确实把管理变得更集中、也方便滚动更新,但它并不能免疫基础设施的几个关键弱点:Kubernetes 的 Secret 在默认情况下以 base64 存储在 etcd(可能被备份或导出)、节点磁盘或容器里可能留下明文副本、访问控制配置不当或日志意外记录都能让秘密泄露。此外,密钥轮换、审计谁在什么时候解密了什么,也变得很难做得彻底和一致。
所以问题来了:如何既保持系统的可用性与性能(大量数据需要快速加解密),又把密钥的 长期保管、访问控制与审计 做到位?答案不是把密钥埋在某个配置文件里,而是把“密钥的管理”交给专门的服务——KMS(Key Management Service)。KMS 不直接替你加密所有数据,而是通过一种称作 信封加密(Envelope Encryption) 的模式,把短期使用的“数据密钥”与长期受保护的主密钥分离,从而在保持性能的同时显著降低主密钥暴露与管理成本。
KMS 的核心思想:信封加密(Envelope Encryption)
在现代密码学中,密钥的分类方式主要有三种:按算法类型、按功能类型、以及按使用场景。
-
按算法分类
最常见的分类方式是根据算法来区分密钥类型:
-
对称密钥(Symmetric Key)
加密和解密使用同一个密钥。典型算法有 AES、DES、ChaCha20 等。 -
非对称密钥(Asymmetric Key)
实际上是一对密钥(Key Pair):- 公钥(Public Key):可以公开,用于加密或验证签名;
- 私钥(Private Key):必须保密,用于解密或生成签名。
常见算法包括 RSA、ECC、Ed25519 等。
-
-
按功能分类
从功能角度来看,密钥还可以分为不同用途:
- 加密密钥:用于保障数据的机密性(Confidentiality)。
- 签名密钥:用于生成或验证数字签名,保障数据的完整性与不可抵赖性。
- 认证密钥:用于消息认证码(MAC),确保通信双方身份的真实性。
-
按使用场景分类
本文重点介绍的是按场景分类的密钥。
以 HTTPS 通信 为例:
在浏览器与服务器之间建立安全连接时,双方通过 SSL/TLS 协议 使用非对称密钥完成密钥交换,最终协商出一个用于通信的对称密钥。这个对称密钥有几个特征:
- 它是一次性的;
- 仅在当前会话中使用;
- 会话结束后立即销毁。
这种密钥被称为 会话密钥(Session Key)。
它的优势在于:即使会话密钥泄露,影响的范围也仅限于当前会话,极大降低了潜在的风险。与之相对的是 主密钥(Master Key),它在系统中被长期保存并反复使用,用于生成、加密或保护其他密钥。
知道了会话密钥和主密钥的概念,咱们来看个例子吧!为什么我们需要会话密钥
想象一下,在战争时期,Alice 和 Bob 需要就军事行动进行秘密通信。
他们事先在线下碰过面,约定好了一个共享的对称密钥 S。
某天,Alice 想给 Bob 发一条加密电报,通知内容 C 是:
“老大决定今晚 11 点动手。”
按照最朴素的加密思路:
- Alice 使用对称密钥 S 加密消息 C,得到密文;
- 她将密文通过电报发给 Bob;
- Bob 收到密文后,用同样的密钥 S 解密,就能还原出明文内容。
看起来一切完美无缺,对吧?其实不然。
假设敌方一直在监听 Alice 和 Bob 的通信。
第一次,他们不知道密文代表什么;
但如果后来又看到一条完全相同的密文,就能推断:
“哦,这次他们又要在晚上 11 点动手了。”
因为 同一个明文 + 同一个密钥 → 产生的密文是完全相同的。
久而久之,敌人甚至不需要解密,就能根据密文模式推测出行动计划。
要解决这个问题,唯一的办法就是:
不要一直使用同一个密钥。
也就是说,每次通信都生成一个新的临时密钥,只用于当前会话,用完即废。
这种“只用一次”的密钥就叫 会话密钥(Session Key)。
这也是现代网络通信(例如 HTTPS / TLS)所采用的思路。
在 TLS v3 这样的协议中,客户端和服务器会通过非对称加密安全地协商出一个新的会话密钥,然后仅用它来加密当前连接的数据。
连接断开,密钥即销毁——从根本上避免了“密文模式泄露”的风险。
信封加密
刚才我们看到,会话密钥能有效防止密文模式泄露。但它也带来了一个新的问题:
如果每次通信都要生成新的密钥,那接收方该如何拿到这个密钥呢?
让我们继续刚才的故事。
这一次,Alice 换了一种更安全的做法:
她在准备发送消息前,临时生成了一个新的对称密钥 R,
然后用这个密钥 R 加密了通知内容 C。
这样,即使敌人截获了密文,也无法直接分析出内容或模式。
问题是,Bob 收到这条密文后,手上只有当初他们约定的长期密钥 S,
却没有 Alice 临时生成的密钥 R。
那他该怎么解密呢?
聪明的 Alice 当然想到了这一点。
于是她又做了一步:
用他们共享的密钥 S 再次加密了这个临时密钥 R。
这样,她在发消息时,除了发送用 R 加密的密文,还附上了“被 S 加密过的 R”。
Bob 收到后,操作顺序就非常清晰了:
- 先用共享密钥 S 解密出临时密钥 R;
- 再用 R 解密真正的消息内容 C。
在上面的过程中,在网络上传输的是加密后的R和加密后的S。如果Alice和Bob再次传输相同的内容C,因为使用的临时对称密钥R是不同的,因此加密后的C也是不同的,即便加密前的明文C还是同一个。
在这个场景中,对称密钥R实际上就是一个会话密钥,对称密钥S就是一个主密钥。因为对称密钥R每次都不一样,但是对称密钥S是一直被重复使用的。
这正是信封加密(Envelope Encryption)的核心思想:内层密钥(R)只负责加密数据,外层密钥(S)负责保护内层密钥。
当系统规模扩大、密钥种类繁多时,我们只需安全地管理外层密钥,而临时生成、频繁更换的内层密钥则能灵活地承担加密任务。
我们来定下信封加密(Envelop Encryption):
KMS(Key Management Service)并不是自己直接“加密你的数据”,而是负责管理密钥,并通过“信封加密”机制安全地实现数据加密。
信封加密是一种分层加密策略(Layered Encryption)。它的核心理念是:
“用一个数据密钥加密数据,再用一个主密钥加密这个数据密钥。”
此外,这个过程是可以嵌套的。指在会话密钥和主密钥之间可以再加多次会话密钥。但是当你这么做时,要想清楚为什么。通常情况下,主密钥加密会话密钥就够了。
在一些文档或者书籍中,我们会看到一种叫法:内容加密密钥(Content Encrypting Key,CEK)和密钥加密密钥(Key Encrypting Key,KEK)。
所谓内容加密密钥,是指被加密的内容是要传输的明文信息。在上面的案例中,对称密钥R就是CEK。
所谓密钥加密密钥,是指被加密的内容是密钥。在上面的案例中,对称密钥S就是KEK。
工作原理
在 Alice 与 Bob 的例子中,我们看到了信封加密的雏形:
消息内容由临时密钥 R 加密,临时密钥再由共享密钥 S 加密。
这种两层结构已经能显著提升安全性——即使敌人截获通信,也无法直接还原出明文。
但如果我们把视角从两个人的通信扩展到一个现代的分布式系统,就会发现新的问题:
- 系统里不止 Alice 和 Bob。可能有上百个服务、上千个实例,都需要安全地生成、使用、轮换密钥。
- 共享密钥 S 无法线下约定。服务实例是自动扩缩容的,不可能让每个节点都人工分发同一把密钥。
- 密钥生命周期太长。一旦 S 泄露,就意味着所有被它保护过的 R、所有被 R 加密过的数据都不再安全。
- 缺乏审计与访问控制。我们希望能知道“谁、在什么时候、出于什么目的”解密过密钥。
于是,现代系统引入了一个“密钥管理服务(Key Management Service, KMS)”作为第三层。
可以这样理解整个结构:
层级 | 密钥名称 | 用途 | 存储位置 |
---|---|---|---|
第1层 | Data Encryption Key (DEK) | 临时生成的对称密钥,用来加密具体数据(文件、字段、消息) | 不存储明文,一般连同加密数据一起保存 |
第2层 | Key Encryption Key (KEK) | 用于加密 DEK,也就是前面例子里的“共享密钥 S” | 由应用持有或由 KMS 生成 |
第3层 | Customer Master Key (CMK) | 由 KMS 内部的硬件安全模块(HSM)保护,用来加密 KEK 或直接派生 KEK | 存放在 KMS/HSM 中,不可导出 |
这样一来,KMS 成了整个加密体系的“信任根”。
应用不再直接保存主密钥,而是通过安全的 API 请求 KMS 来生成或解密密钥。
每一次调用都能被审计、授权、限速,甚至可以随时吊销访问权限。
在这个三层结构下:
- 数据加密 仍然用高效的对称算法(DEK 层);
- 密钥保护 由上层密钥完成(KEK、CMK 层);
- 安全信任 则交由 KMS/HSM 来保障(物理隔离、审计、轮换、权限控制)。
这种方式延续了信封加密的思想,但通过服务化与硬件信任根,把它扩展到了云时代的规模。
现代流程是:
-
应用请求 KMS 生成一个 DEK。
-
KMS 返回:
- 明文 DEK(仅供你临时使用来加密数据)
- 加密后的 DEK(由 KMS 的 CMK 加密的版本)
-
应用用明文 DEK 加密数据后,立刻销毁明文 DEK。
-
保存加密后的数据 + 加密的 DEK(一起存储,相当于“信封”)。
-
之后要解密数据时,发送加密的 DEK 给 KMS,请求解密回明文 DEK,再用它解密数据。
这就好比:
KMS 是保险柜,你只带着保险柜的钥匙副本(加密的 DEK),需要开锁时就回保险柜申请授权。
CA机构算不算一个KMS
这时候就有人问了,CA机构算不算一个KMS?
答案是:CA(Certificate Authority)不是 KMS,但两者属于信任体系中不同层次的“信任根”角色。
我们先回顾一下HTTPS中的场景:
我自己生成了一对非对称密钥(私钥 + 公钥),然后让 CA 机构对我的公钥签名,生成了一张数字证书。
这张证书用于 HTTPS/TLS 握手,在握手过程中协商出临时的对称密钥(Session Key)来加密通信。
这整个过程确实包含了“密钥管理”与“信任验证”,但 CA 和 KMS 的职责截然不同。
🧾 一、CA 做的事情:建立「身份信任」
CA(证书颁发机构)的职责是:
-
证明“公钥属于谁”
它对你的公钥签名,等于在全世界范围内告诉别人:“这个公钥确实是某个合法实体(网站、公司、组织)的”。 -
解决信任传播问题
你信任浏览器厂商,浏览器信任内置的根证书(Root CA),于是它可以验证任何由该根证书签发的子证书。
这种信任链保证了你访问的网站确实是“它自己”。
简单理解:
CA 是“身份证管理局”——帮你证明你是谁。
它管理的是身份与公钥的可信性,而不是公钥的使用和生命周期。
🔐 二、KMS 做的事情:管理「密钥本身」
KMS 的职责完全不同:
- 它不证明你是谁,而是安全地保存、生成、轮换、销毁密钥。
- 它关心的是密钥的机密性、使用授权与审计,不是它是否属于某个合法身份。
简单理解:
KMS 是“保险柜 + 保安系统”——负责保管钥匙、授权取用、记录谁用过。
它管理的是密钥的生命全周期,而不是密钥的合法身份。
🧩 三、TLS 握手中两者的关系
在 TLS(尤其是 TLS 1.3)中:
-
证书阶段(CA作用)
- 服务器把 CA 签发的证书发给客户端。
- 客户端验证证书的签名链,以确定服务器的身份。
-
密钥交换阶段(KMS作用)
- 双方通过证书里的公钥或临时密钥(Ephemeral Key)进行密钥交换(通常是 ECDHE)。
- 握手完成后,协商出一个临时对称密钥(Session Key),用于后续通信加密。
所以:
- CA 解决“你是谁?”
- KMS 解决“你用哪把钥匙?”
⚖️ 四、在大型系统中它们如何协同
在企业或云环境里,CA 与 KMS 通常协同存在:
模块 | 职责 | 示例 |
---|---|---|
CA / PKI | 负责身份认证与证书签发 | 内网 CA、Let’s Encrypt、CFSSL |
KMS | 负责密钥生成、加密、轮换、访问控制 | AWS KMS、HashiCorp Vault KMS、Aliyun KMS |
HSM | 提供物理安全保障,CA 与 KMS 都可能依赖它 | Thales Luna、AWS CloudHSM |
有时候,CA 背后也会用 KMS 或 HSM 来保护自己的根密钥。
所以可以说:
KMS 是加密体系的“安全根”,
CA 是信任体系的“身份根”。
加密算法层面
KMS 内部通常使用两类算法:
层级 | 算法示例 | 说明 |
---|---|---|
DEK 层 | 对称加密(AES-256-GCM / AES-256-CBC) | 快速、高效,适合大数据量加密 |
KEK/CMK 层 | 非对称加密(RSA-2048/3072)、或对称主密钥(AES-KW) | 用于安全地加密小块密钥数据 |
其中,DEK(Data Encryption Key)是临时生成的,它非常短(一般 256 bit),加密数据时性能极高,但不能明文存储,必须被上层密钥(KEK/CMK)加密保存。
所以 KMS 在生成 DEK 时通常会返回两部分:
- Plaintext DEK:明文密钥(应用使用它加密数据后立即销毁)
- Encrypted DEK:被 CMK 加密的密钥(你保存它,用于以后解密)
这一步的「加密 DEK」就要选择算法了。
- 如果上层 CMK 是 非对称密钥(RSA),就使用 RSA 的加密算法;
- 如果上层 CMK 是 对称密钥(AES),则使用 AES-KW(AES Key Wrap)算法。
KMS 内部通常使用两类算法
🔐 非对称加密场景(RSA 模式)
在这种模式下,KMS 里保存着一对密钥对:
- 公钥(用于加密 DEK)
- 私钥(用于解密 DEK)
加密过程:
Encrypted_DEK = RSA_Encrypt(DEK, CMK_PublicKey)
解密过程:
DEK = RSA_Decrypt(Encrypted_DEK, CMK_PrivateKey)
特点:
- 优点:公私钥分离、适合多客户端分发公钥加密;
- 缺点:RSA 运算慢,密钥长度有限,通常只适合加密几十到几百字节的数据(正好够一个 DEK)。
典型用法:
- AWS KMS 的 “Asymmetric CMK” 模式
- GCP Cloud KMS / Azure Key Vault 都支持 RSA-OAEP 来加密 DEK
- 自建系统中常用 RSA-2048/3072/4096 + OAEP padding
⚙️ 对称加密场景(AES-KW 模式)
在很多云厂商的默认实现中,CMK 其实本身也是一个对称密钥。
那就没必要用 RSA 了,直接用 AES Key Wrap(AES-KW) 算法对 DEK 进行“密钥包裹”。
加密过程:
Encrypted_DEK = AES_KeyWrap(CMK, DEK)
解密过程:
DEK = AES_KeyUnwrap(CMK, Encrypted_DEK)
AES-KW 是一种专门为“加密密钥本身”设计的对称算法(见 RFC 3394)。
它与普通 AES-CBC 不同,强调两点:
- 无需随机 IV(更便于可重复验证)
- 内部结构能检测包装/解包过程是否出错(完整性校验)
优点:
- 性能比 RSA 高得多;
- 算法更简单,容易在硬件(HSM)中实现。
典型用法:
- AWS KMS 默认 CMK 就是 256 位对称密钥,用 AES-KW 加密 DEK;
- Google Cloud KMS / Aliyun KMS 同理。
🧱这两种模式在 KMS 内部如何共存
实际上,KMS 并不会让用户决定到底用 RSA 还是 AES-KW。
一般的工作流程是这样的:
-
当你创建 CMK 时,可以选择类型:
- 对称 CMK(默认)
- 非对称 CMK(RSA 或 ECC)
-
KMS 内部会根据 CMK 类型自动选择合适的算法(AES-KW 或 RSA-OAEP)。
-
所有这些操作都在 HSM(硬件安全模块) 中完成,密钥本身永远不会离开硬件边界。
换句话说,RSA/AES-KW 只是 KMS “包信封”的两种不同实现形式,
实际上它们都承担同样的角色:安全地加密 DEK。
再总结一下三层加密的算法关系:
层级 | 名称 | 常用算法 | 加密对象 | 目的 |
---|---|---|---|---|
第1层 | DEK | AES-256-GCM | 数据 | 高速数据加密 |
第2层 | KEK / CMK | AES-KW 或 RSA-OAEP | DEK | 保护 DEK |
第3层 | KMS / HSM | 内部硬件保护 | CMK | 防止泄露、支持审计与轮换 |
现代 KMS 设计的核心演化逻辑
有人这时候会有疑问——为什么以前三层(DEK→KEK→CMK),现在很多云厂商的文档里都只提两层(DEK→CMK)。
没错!
在现代 KMS(比如 AWS、GCP、Azure)中,你调用 “GenerateDataKey” 时,确实会收到这两样东西:
{"Plaintext": "DEK 明文(base64 编码)","CiphertextBlob": "被 CMK 加密的 DEK(二进制 blob)"
}
应用层的流程是:
-
拿到明文 DEK(
Plaintext
)
→ 加密你的数据,然后立刻从内存中擦除。 -
拿到密文 DEK(
CiphertextBlob
)
→ 存起来(比如和加密数据放一起)。 -
未来要解密数据时
→ 把CiphertextBlob
发回 KMS,KMS 用 CMK 解出明文 DEK,返回给你。
这一来一回,就实现了完整的「信封加密(Envelope Encryption)」流程。
那 KEK 去哪了?
实际上,KEK 这个角色并没有消失,而是被 CMK 内化了。
在传统模型里,企业可能会这么分层:
层级 | 名称 | 存放位置 |
---|---|---|
DEK | 加密数据 | 文件或数据库 |
KEK | 加密 DEK | 应用服务器或密钥管理系统 |
CMK | 加密 KEK | 硬件安全模块(HSM) |
但在现代 KMS 架构中:
- CMK 本身就负责加密 DEK;
- 它实际上扮演了“KEK + CMK”双重角色;
- 而 HSM 就是 CMK 的守护容器。
所以现在常见的结构是两层:
层级 | 名称 | 加密对象 | 存储位置 |
---|---|---|---|
第1层 | DEK | 数据 | 应用层存储(被加密) |
第2层 | CMK(内部相当于 KEK+CMK) | DEK | KMS/HSM 内部 |
换句话说:
现代 KMS 把 KEK 的功能“收编”到 CMK 里了。
为什么要简化成两层?原因有三:
1️⃣ HSM 硬件隔离足够安全
传统三层结构是为了隔离信任边界:
- 应用不可信 → 不直接接触 KEK
- 密钥管理系统不可信 → 不直接接触 CMK
- 最底层 HSM 可信
而现在云厂商用 FIPS 140-2/3 认证的 HSM 集群 做支撑,
整个密钥生命周期都在 HSM 内完成,
安全性足以合并 KEK 与 CMK 的角色。
2️⃣ 管理复杂度大幅降低
以前三层要管理 KEK 轮换、备份、分发;
现在只需管理 CMK(KMS 自动帮你轮换和保护密钥)。
3️⃣ API 设计更简洁
KMS 统一用一对接口搞定:
GenerateDataKey
Decrypt
内部自动用 CMK 封装、解封 DEK,不需要显式传 KEK。
那是不是再也用不到 KEK 了?
不完全是。
在一些更高安全场景(比如多主权、跨区域、跨云架构),
你仍然可以手动加一层 KEK,形成“显式三层模型”:
应用 → DEK → KEK(本地主控) → CMK(KMS内)
比如:
- 企业内部要求“云厂商不能单独解密数据”,就用自己控制的 KEK;
- 云上的 CMK 再去加密 KEK(也就是“Bring Your Own Key”模式,BYOK)。
在 AWS、Azure、GCP 都有这个选项:
BYOK = 自带 KEK,让 KMS 只管最底层 CMK。
模式 | 层级 | KEK 是否存在 | 谁负责加密 DEK | 特点 |
---|---|---|---|---|
传统自建 | DEK → KEK → CMK | 显式存在 | KEK | 三层信任隔离 |
现代 KMS | DEK → CMK | 内化到 CMK | CMK | 安全、简洁 |
BYOK | DEK → KEK → CMK | 显式存在 | KEK + CMK | 云上隔离控制 |
实际上,CMK 自己从来不直接“拿出来”加密 DEK,而是 用于派生或生成一个一次性 KEK(Key Encryption Key)。
这个 KEK 通常是通过一种**密钥派生函数(KDF, Key Derivation Function)**生成的,带有上下文信息(比如 key ID、nonce、时间戳等),确保:
-
不同会话、不同调用生成的 KEK 完全不同;
-
即使有人截获加密数据,也无法反推出 CMK。
AWS 官方白皮书中提到的内部逻辑:
调用 GenerateDataKey(CMK):1. KMS 在 HSM 内加载 CMK2. 使用 KDF( CMK, context ) → 临时 KEK3. 生成随机 DEK4. 用 KEK 加密 DEK → Encrypted_DEK5. 返回:- 明文 DEK(给客户端加密数据用)- 加密的 DEK(Encrypted_DEK,用于存储)
其中:
-
CMK 永远不出 HSM
-
KEK 只存在于内存中、只用一次
-
DEK 明文只存在于响应中瞬间
下次你调用 Decrypt(CiphertextBlob) 时,流程反过来:
1. KMS 用 CMK 再次派生出相同 KEK
2. 用 KEK 解密 DEK
3. 把 DEK 明文返回(或在 HSM 内解密数据)
整个过程中,外部永远看不到 CMK,也无法截获 KEK。
HSM 是什么?
HSM(Hardware Security Module,硬件安全模块)是一个专门用于密钥生成、存储和加密操作的物理硬件设备,它在现代 KMS 和信封加密体系中起到核心的“信任根”作用。
- 硬件设备:独立的物理设备,不是普通软件。
- 专门用于加密操作:生成密钥、加密/解密、签名/验证。
- 高安全性:密钥永远存储在 HSM 内部,不会被导出到外部系统。
简单理解:
HSM 就像一个“加密保险柜 + 加密处理器”,钥匙永远锁在里面,你可以用它开锁或加密,但永远拿不出钥匙本体。
HSM 的主要功能:
功能 | 说明 |
---|---|
密钥生成 | 生成 CMK、DEK 或其他密钥,随机性和安全性高 |
密钥保护 | 主密钥(CMK)永远存储在 HSM 中,不允许导出 |
加密/解密操作 | 内部执行信封加密:生成 KEK、加密 DEK 等 |
数字签名/验证 | 支持非对称算法(RSA/ECC)签名和验证 |
审计与访问控制 | 记录每次密钥使用日志,可与 KMS API 联动 |
防篡改设计 | 硬件自带防拆、防攻击措施,保障密钥物理安全 |
HSM 在 KMS 中的作用:
在 KMS 内部,HSM 扮演“安全根”的角色:
-
CMK 永远在 HSM 内
- 外部无法直接访问 CMK 的明文。
-
派生 KEK、加密 DEK 全在 HSM 内完成
- DEK 明文可以短暂返回给应用,但 CMK 和 KEK 永远在 HSM 内。
-
支持审计和密钥轮换
- HSM 内记录密钥使用日志,保证操作可追溯。
换句话说:
如果把信封加密比作邮寄信件,HSM 就是那把锁着“主密钥保险箱”的银行金库。
你只能通过授权的接口去“开小信封”,但主钥匙永远安全无虞。
HSM 的常见形式:
类型 | 说明 |
---|---|
本地 HSM | 企业内部自建硬件,如 Thales Luna、Utimaco 等 |
云 HSM | 云厂商提供托管服务,如 AWS CloudHSM、Azure Dedicated HSM、GCP Cloud HSM |
虚拟 HSM | 软件模拟 HSM,但安全性不如物理 HSM(一般用于开发/测试) |
在 HashiCorp Vault 或类似开源 KMS 中:
- Vault 本身是软件系统,运行在你自己的服务器或容器上。
- 它也有密钥保护机制(如密钥分封、存储加密、自动轮换)。
- HSM 可以是物理设备,也可以是软件模拟,关键在于“密钥操作和存储的安全性”。
软件模式 vs 硬件 HSM:
类型 | 说明 | 对应安全等级 |
---|---|---|
软件 HSM(Soft HSM) | 纯软件实现密钥保护、KDF、加密操作,密钥存在操作系统内存或磁盘上(可能加密) | 安全性比物理 HSM 低,适合测试或低敏环境 |
物理 HSM | 专用硬件设备(如 Thales、Utimaco、AWS CloudHSM) | 高安全级别,密钥永远在硬件里,防物理篡改 |
云 HSM / 托管 HSM | 云厂商提供的硬件安全模块,通过 API 调用 | 介于二者之间,管理便捷且硬件隔离 |
Vault 默认部署的 HSM:
-
如果你直接用 Kubernetes 部署 Vault,默认情况下 Vault 内部的 CMK 是软件托管的:
- Vault 的“密钥根(root key)”保存在内存或磁盘(经过 Shamir 分片加密)
- 这是软 HSM,只能防止直接读文件,但不能防止物理攻击或内存抓取
-
如果需要真正 HSM 保护,可以配置 Vault 集成外部 HSM:
- PKCS#11 接口:连接 Thales、Utimaco 等硬件
- 云 HSM:Vault 支持 AWS CloudHSM / Azure Dedicated HSM 作为密钥存储后端
换句话说,K8s 上的 Vault 默认没有物理 HSM,它只是模拟了 HSM 的角色,安全级别取决于你的主机/容器安全。
软件 HSM vs 真正 HSM 区别:
特性 | 软件 HSM(Vault 默认) | 物理 HSM |
---|---|---|
密钥导出 | 可以导出(被加密或未加密) | 永远不可导出 |
防物理攻击 | 无法防护 | 防拆卸、防侧信道 |
性能 | 完全受 CPU 内存限制 | 高性能硬件加速 |
使用成本 | 免费 / 容器部署即可 | 硬件采购或云托管费用 |
物理 HSM 本质上也是一台特殊的冯·诺依曼计算机,只是它的设计目标和约束条件和普通电脑完全不同。我们可以从几个方面来看:
- CPU / 控制器:HSM 内部有专用处理器,负责执行加密算法(AES、RSA、ECC 等)和逻辑控制。
- 内存:有专用内存,用于临时存储密钥、随机数、运算中间数据。
- 程序逻辑:HSM 内部运行固件或微程序(Firmware / Microcode),控制密钥生成、加解密、签名、访问控制、审计日志等操作。
- 输入/输出接口:通过 PKCS#11、KMIP 或厂商 API 与外部系统通信。
换句话说,它是“计算机 + 加密专用指令集 + 高安全保护”。
HSM 与普通电脑的区别
特性 | 普通电脑 | HSM |
---|---|---|
CPU | 通用 CPU | 专用或通用 CPU + 加密加速指令集 |
存储 | 可随意访问(硬盘、内存) | 密钥存储在受保护闪存 / 非易失性安全内存 |
操作系统 | Windows / Linux / macOS | 极简固件 / RTOS,不可随意运行第三方程序 |
安全防护 | 软件安全措施 | 防拆、防篡改、防侧信道攻击、防物理提取密钥 |
程序更新 | 任意安装/修改 | 固件受签名验证,限制更新,严格控制 |
HSM 内部程序作用
HSM 的固件/程序主要负责:
-
密钥管理
- 生成 CMK/KEK/DEK
- 派生或封装密钥
- 密钥生命周期管理(轮换、销毁)
-
加密计算
- 对称加密(AES-GCM / AES-KW)
- 非对称加密(RSA / ECC)
- 数字签名 / 验证
-
访问控制和审计
- 认证调用者(API key / token)
- 记录每次密钥使用日志
- 防止未授权操作
-
安全防护
- 检测物理拆卸 → 清零密钥
- 检测异常电压/温度 → 阻断操作
- 抵御侧信道攻击
简单理解:HSM 就是“一台精简、安全、专用的计算机”,专门用于密钥管理和加密运算。
为什么必须是“计算机”?
因为 HSM 不仅要存储密钥,还要执行复杂算法:
- 加密算法(AES、RSA、ECC)是数学计算,需要 CPU 或硬件加速器
- 信封加密、密钥派生、签名验证都是逻辑处理流程
- 审计和访问控制也需要计算能力
所以没有计算能力的“纯保险柜”无法完成 KMS 所需的功能。
上层 KMS 与 HSM 的基本关系
- KMS(Key Management Service):管理密钥生命周期、提供 API 给应用,做“业务逻辑 + 权限控制 + 调度”。
- HSM(Hardware Security Module):在硬件里安全生成和操作密钥,是 KMS 的“安全根”,保证密钥不泄露。
- 交互核心:KMS 通过安全 API 调用 HSM 的加密、解密、签名、密钥派生等操作,而 CMK 从不离开 HSM。
换句话说:KMS 是“密钥调度中心”,HSM 是“密钥保险柜 + 加密计算单元”。
典型交互流程:
以“生成 DEK 并返回给应用”举例:
应用 ---> 调用 KMS API ---> KMS 内部逻辑 ---> HSM
详细步骤:
-
应用调用 KMS API
- 请求:生成数据密钥 DEK 或解密密文 DEK
-
KMS 校验权限
- 检查调用者身份、访问策略、审计要求
-
KMS 向 HSM 发送指令
-
如果是生成 DEK:
- HSM 内部生成随机 DEK
- 用 CMK 派生 KEK
- 用 KEK 加密 DEK → 得到 Encrypted_DEK
-
如果是解密 DEK:
- HSM 用 CMK 派生 KEK
- KEK 解密 DEK → 返回明文
-
-
HSM 返回结果给 KMS
- DEK 明文(临时给应用)
- DEK 密文(存储)
-
KMS 返回给应用
- 应用用 DEK 加密数据
- DEK 密文随加密数据一起存储
交互特点:
特性 | 描述 |
---|---|
CMK 永远不出 HSM | KMS 只能调用接口,不直接拿到 CMK 明文 |
所有加密运算在 HSM 内完成 | 包括 DEK 封装、密钥派生、数字签名 |
安全接口 | KMS 与 HSM 通常用 PKCS#11、KMIP 或厂商 API 交互 |
审计与控制 | HSM 可记录所有操作日志,KMS 可二次整合到审计系统 |
可扩展 | HSM 可以支持多租户、多 CMK、并行操作,KMS 调度统一管理 |
图示逻辑(文字版):
应用││ 1. GenerateDataKey / Decrypt API▼
KMS (逻辑层 / 调度 / 权限控制)││ 2. 指令调用 HSM▼
HSM (硬件)├─ 随机数生成 TRNG → 生成 DEK├─ CMK 派生 KEK├─ KEK 加密 DEK → 返回 Encrypted_DEK└─ 审计日志记录││ 3. 返回 DEK 明文 + 加密 DEK▼
KMS│▼
应用
为什么要这么麻烦?
主要是出于 安全与性能的平衡:
问题 | 信封加密的解决方式 |
---|---|
大量数据加密性能太低 | 只用对称加密加速数据加密 |
密钥泄露风险 | 数据密钥(DEK)都是临时生成、一次性使用 |
主密钥轮换问题 | 只需重新加密 DEK 而非所有数据 |
审计与访问控制 | 所有解密请求都需经过 KMS 授权与审计 |
延伸概念
- KMS 通常依托 HSM(Hardware Security Module) 硬件加密模块来确保主密钥安全,哪怕云提供商也无法直接访问明文主密钥。
业界知名的KMS
云厂商KMS
直接把密钥放在环境变量里(尤其在容器或云平台上)是有安全隐患的,因为环境变量在以下情况下容易被泄露:
- 程序崩溃产生 core dump
- CI/CD 日志或进程列表(
ps
,/proc/<pid>/environ
)被访问 - 容器或节点被攻陷
业界主流的密钥管理方式:
方案类别 | 代表产品 / 服务 | 核心特性 | 安全性 |
---|---|---|---|
🔒 专用密钥管理服务 (KMS) | AWS KMS、Azure Key Vault、GCP KMS、阿里云 KMS、腾讯云 KMS | 密钥托管 + 自动加解密 + 权限控制 + 审计 | ⭐⭐⭐⭐⭐ |
🔐 Secrets 管理系统 | HashiCorp Vault、1Password Secrets Automation、Infisical、doppler、Bitwarden Secrets Manager | 支持版本控制、动态凭证、加密存储、审计 | ⭐⭐⭐⭐ |
🧩 容器原生方案 | Kubernetes Secrets(配合 SealedSecrets、External Secrets Operator)、Docker Swarm Secrets | 与集群集成方便,可挂载到 Pod 文件系统 | ⭐⭐⭐ |
🧾 配置管理系统集成 | Ansible Vault、Chef Encrypted Data Bag、SaltStack Pillar Encryption | 适合 IaC 场景,部署时自动解密 | ⭐⭐⭐ |
⚙️ 本地安全模块 (HSM) | 云厂商 HSM、YubiHSM、Thales Luna | 硬件防篡改、FIPS 认证,用于极高安全要求 | ⭐⭐⭐⭐⭐ |
云原生架构常见推荐
-
密钥存放:
存放在云厂商的 KMS 或 Secrets Manager(如 AWS Secrets Manager、阿里云凭据服务)。 -
访问方式:
服务启动时通过 IAM 角色 / STS 临时凭证 动态拉取密钥,不写死任何凭证。 -
应用读取:
- SDK 调用(如
boto3.client('secretsmanager')
); - 或通过 sidecar agent(如 Vault Agent Injector)自动挂载到内存文件。
- SDK 调用(如
-
全程审计:
每次访问密钥都会被记录(调用时间、调用方、来源IP等)。
推荐安全架构示例(K8s 场景)
- Pod 启动时由 Agent 动态拉取密钥;
- 密钥存储在 Vault / Secrets Manager;
- 底层由 KMS 负责加密;
- 应用仅通过文件或内存读取密钥,不经磁盘、不写日志。
简单安全层次建议(按优先级)
安全等级 | 建议方案 |
---|---|
⭐⭐⭐⭐⭐ | 云厂商 KMS + Secrets Manager(推荐) |
⭐⭐⭐⭐ | HashiCorp Vault + 动态凭证 |
⭐⭐⭐ | K8s External Secrets + RBAC 限制 |
⭐⭐ | Ansible Vault / 加密配置文件 |
⭐ | 环境变量 + 加密内容(仍然不推荐) |
本地部署KMS
很多团队在考虑用云厂商的 KMS(Key Management Service)时,第一个顾虑就是“出网依赖”,因为:
-
内网高安全环境(例如金融、政企、内控网络)通常禁止出网;
-
但云厂商的 KMS 一般是云端服务(AWS、阿里云、腾讯云等),需要公网 API 调用;
-
这时候就得考虑:
“能不能自己搭一个 KMS?既安全,又不依赖外网。”
云厂商的 KMS 是否必须出网?(公有云环境(如 AWS、阿里云、腾讯云))
-
默认是 SaaS 型 KMS,走厂商 API,需要访问公网或专线;
-
例如:
https://kms.<region>.amazonaws.com
https://kms.cn-hangzhou.aliyuncs.com
-
即使你在云上(VPC 内),KMS 通常也不是你自己网络中的服务,是厂商托管的;
-
不过云厂商一般提供:
- 私网访问 (PrivateLink / 内网Endpoint);
- 免公网出站访问(通过私有通道直接访问 KMS)。
👉 所以:不一定要“出公网”,但要有云厂商的内网通路。
当然,这里主讲本地部署的KMS:
方案 | 类型 | 特点 | 适合场景 |
---|---|---|---|
HashiCorp Vault (推荐) | 开源软件 | 最成熟的自建 Secrets / KMS 方案,支持加密 API、动态凭证、密钥轮换 | 企业私有部署、K8s、本地机房 |
Barbican (OpenStack 项目) | 开源 | 符合 KMS API 标准,适合 OpenStack 环境 | 私有云场景 |
Google Tink + HSM 后端 | 开源库 | 提供统一加密接口,可挂本地密钥管理逻辑 | 内嵌系统、自研平台 |
HashiCorp Boundary + Vault 集成 | 开源 | 用于零信任访问 + 密钥管控 | 大型分布式系统 |
Thales CipherTrust / Luna HSM | 商业硬件方案 | FIPS 认证,提供 KMS API 接口 | 银行级或国家级保密场景 |
Fortanix DSM / AWS CloudHSM | 硬件 / 虚拟 HSM | 混合云支持,可自托管 | 高安全行业 |
企业自建 Vault 方案示意
-
Vault 负责:
- 加密 / 解密 API(相当于 KMS 的
Encrypt/Decrypt
接口) - 生成临时密钥(Dynamic Secrets)
- 审计访问日志
- 加密 / 解密 API(相当于 KMS 的
-
密钥主存储 可以是:
- HSM 硬件;
- 或者软件加密后存在数据库里。
Vault 的简单使用示例:
# 初始化 Vault(一次性)
vault operator init# 启动服务
vault server -config=vault.hcl# 存储密钥
vault kv put secret/db password="my_secret_pwd"# 读取密钥
vault kv get secret/db
在应用层中可以用官方 SDK(Python、Go、Java等)安全地拉取密钥。
你的环境 | 推荐方案 |
---|---|
公有云部署(允许内网访问云服务) | 使用云厂商 KMS + 内网 Endpoint |
私有云 / 本地服务器 | HashiCorp Vault(开源) |
高安全行业(金融、政府) | Vault + HSM(Thales、YubiHSM等) |
轻量级开发环境 | Doppler / Infisical / Ansible Vault 等 |
HashiCorp Vault: https://www.hashicorp.com/en/products/vault
轻量级别KMS
HashiCorp Vault 是业界标准没错,但它确实“很重”:
- 组件多(storage backend、unseal、tokens、policies、audit、plugins);
- 对运维要求高(高可用、TLS、恢复、rotation);
- 对小团队或非金融级系统来说有点“杀鸡用牛刀”。
Vault 之外的主流替代品对比
方案 | 类型 | 安全性 | 部署复杂度 | 说明 |
---|---|---|---|---|
🔒 Infisical | 开源 SaaS / 自托管 | ⭐⭐⭐⭐ | ⭐⭐ | 新一代轻量 Secrets 管理,支持 KMS 集成、版本、RBAC |
⚙️ Doppler | SaaS(有自托管代理) | ⭐⭐⭐⭐ | ⭐ | 现代团队常用,开发体验好、支持密钥自动注入环境 |
🧰 Mozilla SOPS + Git + KMS | 开源、文件级 | ⭐⭐⭐⭐ | ⭐⭐ | 把加密配置放 Git 里,支持 GPG/AWS KMS/GCP KMS 等,超轻量 |
🔐 Secrethub / 1Password Secrets Automation | SaaS / 自托管 | ⭐⭐⭐⭐ | ⭐⭐ | 适合中小规模服务端 |
🪶 GopenPGP / MiniKMS | 纯开源轻量库 | ⭐⭐⭐ | ⭐ | 适合嵌入式或边缘节点 |
🧱 Keywhiz(Square 出品) | 开源、自托管 | ⭐⭐⭐⭐ | ⭐⭐⭐ | 大型组织使用,但比 Vault 轻一半 |
🗝 CyberArk Conjur OSS | 开源 / 企业版 | ⭐⭐⭐⭐ | ⭐⭐⭐ | 与 K8s 集成好,支持 JWT/OIDC Auth |
针对不同需求的推荐
场景 | 推荐方案 | 特点 |
---|---|---|
小型团队 / 单服务 / 容器化 | Infisical | Web UI + CLI + API,轻量、现代、Docker 一键自托管 |
DevOps 配置安全同步(GitOps) | Mozilla SOPS + GPG/AWS KMS | 无服务依赖,密钥放在 CI/CD 或 KMS,开发者友好 |
中型企业内网 | Keywhiz 或 Conjur OSS | 支持多租户、策略、K8s Secret 注入 |
纯离线环境 | GPG + SOPS + 文件分发 | 无网络依赖,可离线轮换密钥 |
云原生环境(K8s) | External Secrets Operator + KMS | 原生整合,配置简单、安全可靠 |
最推荐的轻量方案:Infisical(开源版)
它是 Vault 的现代替代品之一,支持:
- 🔐 自带密钥轮换与版本控制;
- 🧑💼 RBAC、团队权限;
- 💡 API / CLI / SDK(Node、Python、Go 全支持);
- 🧰 与 Kubernetes、Docker、CI/CD(GitHub Actions、GitLab CI)无缝集成;
- ☁️ 可以使用云厂商 KMS 做“主密钥”,自己托管服务。
⚙️ 自托管部署示例
docker run -d \--name infisical \-p 8080:8080 \-e ENCRYPTION_KEY=$(openssl rand -hex 32) \infisical/self-hosted
之后访问 http://localhost:8080
配置组织、项目、环境、密钥;
应用侧用 API 拉取密钥:
infisical secrets pull --env=prod --path=/myapp
支持自动注入 .env
或直接通过 SDK:
from infisical import InfisicalClient
client = InfisicalClient(token="my_token")
secrets = client.get_all_secrets(env="prod")
安全性上:
- 数据在客户端加密后上传;
- 服务器端只存密文;
- 可接入云 KMS / HSM 作 master key;
- 可审计与轮换。
最轻量“无服务方案”:Mozilla SOPS + KMS
若你只想安全保存配置/密钥,不想起服务:
- 用 YAML/JSON 格式存配置;
- 用 SOPS 加密敏感字段;
- 加密密钥可绑定 GPG、AWS KMS、Azure KeyVault 等;
- CI/CD 或应用启动时自动解密。
示例:
sops.yaml
api_key: ENC[AES256_GCM,data:abcdef,iv:...,tag:...,type:str]
db_password: ENC[AES256_GCM,data:qwerty,iv:...,tag:...,type:str]
加密命令:
sops --encrypt --kms arn:aws:kms:... secrets.yaml > secrets.enc.yaml
解密:
sops -d secrets.enc.yaml > secrets.yaml
优点:
- 无需额外服务;
- 可放入 Git;
- 支持密钥轮换;
- 与 Terraform / Ansible / CI 系统集成良好。
如果你仍想具备 Vault 的“安全等级”但更轻:
可以采用 轻量三层设计(建议性架构):
- 应用通过 SDK/API 拉取密钥(短期缓存);
- 后端系统(Infisical 或 SOPS)负责存储与轮换;
- 底层主密钥仍由 KMS/HSM 加密;
- 无需复杂的 Vault policies 或 token 管理;
- 部署运维量可降到 Vault 的 20%。
Vault 快速开始
vault 是一款 HCP 推出的密钥管理引擎,用来集中存储集群运行过程中所需要的秘密信息,例如数据库的访问凭证、密码、密钥等。它保证了存储与通信过程的保密性,这对于我们无处不在的敏感信息的数据安全显然是十分必要的。
与此同时,vault 拥有一系列可插拔功能扩展,可以支持将 vault 的实际数据存储到内存、文件系统、google cloud、AWS、etcd 等多种存储介质中,满足不同的集群部署需求,可谓是非常灵活。
在不同的平台上,vault 安装略有不同,可以参考实际的文档:https://developer.hashicorp.com/vault/install#windows
我们这里简单使用windows的二进制文件进行调试开发:
解压后就只有一个可执行文件:
我们启动他的开发模式:
下面这段示例是一个Python 应用侧调用 HashiCorp Vault Transit 引擎进行加密 / 解密 / DEK 轮换的业务逻辑。
它演示了如何:
- 与 Vault 建立连接
- 调用 Transit 生成临时 DEK
- 使用返回的 DEK 加密用户文件内容
- 支持定期轮换 DEK
- 从 Vault 解密数据
安装依赖:
pip install hvac cryptography
假设你已经在 Vault 中启用了 Transit 引擎并创建了一个密钥:
vault secrets enable transit
vault write -f transit/keys/my-app-key
示例代码:Vault KMS + 应用层加密逻辑
import hvac
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import base64
import osVAULT_ADDR = "http://127.0.0.1:8200"
VAULT_TOKEN = "hvs.xxxxxxxx"
TRANSIT_KEY_NAME = "my-app-key"# 初始化 Vault 客户端
client = hvac.Client(url=VAULT_ADDR, token=VAULT_TOKEN)def generate_data_key():"""从 Vault 动态生成一个 DEK(Data Encryption Key)Vault 返回明文 DEK(用于加密数据)与密文 DEK(存库)"""result = client.secrets.transit.generate_data_key(name=TRANSIT_KEY_NAME,key_type="aes256-gcm96")plaintext_key = base64.b64decode(result["data"]["plaintext"])ciphertext_key = result["data"]["ciphertext"]return plaintext_key, ciphertext_keydef encrypt_data(plaintext: bytes):"""使用 Vault 动态生成的 DEK 加密数据"""dek_plain, dek_cipher = generate_data_key()aesgcm = AESGCM(dek_plain)nonce = os.urandom(12)ciphertext = aesgcm.encrypt(nonce, plaintext, None)return {"ciphertext": base64.b64encode(ciphertext).decode(),"nonce": base64.b64encode(nonce).decode(),"dek_cipher": dek_cipher # 存储在数据库中}def decrypt_data(ciphertext_b64: str, nonce_b64: str, dek_cipher: str):"""从 Vault 解密 DEK,然后使用该 DEK 解密数据"""result = client.secrets.transit.decrypt_data(name=TRANSIT_KEY_NAME,ciphertext=dek_cipher)dek_plain = base64.b64decode(result["data"]["plaintext"])aesgcm = AESGCM(dek_plain)plaintext = aesgcm.decrypt(base64.b64decode(nonce_b64),base64.b64decode(ciphertext_b64),None)return plaintext.decode()if __name__ == "__main__":# 模拟业务数据data = b"Top Secret Document: The launch is at 2300 hours."# 加密流程enc = encrypt_data(data)print("Encrypted Data:", enc)# 解密流程dec = decrypt_data(enc["ciphertext"], enc["nonce"], enc["dek_cipher"])print("Decrypted Data:", dec)
🧠 逻辑说明
阶段 | Vault 动作 | 应用动作 |
---|---|---|
1️⃣ 生成 DEK | Vault 用 CMK 加密生成一个新的 DEK | 应用获取明文 DEK(仅内存使用) |
2️⃣ 数据加密 | - | 应用使用 DEK 加密实际文件内容 |
3️⃣ 存储 | - | 存储密文文件 + Vault 返回的加密 DEK |
4️⃣ 解密 | Vault 用 CMK 解密 DEK | 应用用明文 DEK 解密数据 |
5️⃣ 轮换 | 应用定期请求新 DEK | Vault 自动生成新 DEK |
💡 亮点
- Vault 永远不暴露 CMK
- 应用不保存明文 DEK,只暂存在内存中
- 可以定期调用
generate_data_key()
实现DEK 自动轮换 - Vault 审计日志可完整记录每次密钥操作
DEK密钥轮换
Vault 每次调用
generate_data_key()
生成的 DEK(Data Encryption Key)都是全新的、随机的。
generate_data_key
做了什么?
当你调用:
client.secrets.transit.generate_data_key(name=TRANSIT_KEY_NAME,key_type="aes256-gcm96"
)
Vault 内部执行以下逻辑:
-
从系统级随机数源(
/dev/urandom
或 HSM RNG)生成一个 随机 DEK(对称密钥); -
使用你指定的 CMK(
my-app-key
) 对该 DEK 进行加密(封装); -
返回结果:
{"plaintext": "<base64 DEK 明文>","ciphertext": "vault:v1:<密文>" }
也就是说,每次生成的明文 DEK 是随机的!
即使你调用同一个 TRANSIT_KEY_NAME
,Vault 也不会重用旧密钥。
你可以理解成:
调用次数 | 明文 DEK (base64) | 密文 DEK | 结果相同? |
---|---|---|---|
第 1 次 | wJs3...AB | vault:v1:abcd... | ❌ |
第 2 次 | xfT9...KP | vault:v1:efgh... | ❌ |
第 3 次 | 9zLm...X1 | vault:v1:ijkl... | ❌ |
💡 每次都是全新的随机 256-bit 对称密钥。
这是信封加密(Envelope Encryption)的关键安全优势:
- 每次加密使用独立的 DEK(每条数据一个密钥)
- 即使某个 DEK 泄露,也只影响极小范围(对应的数据块)
- 所有 DEK 都由 同一个 CMK 安全地加密存储
- Vault 通过 CMK 封装,确保你不需要暴露 CMK 自身
既然 Vault 每次生成的 DEK(Data Encryption Key)都是随机的,那我怎么在未来正确解密旧数据?
✅ 必须把「密文 + 加密后的 DEK」一起保存(通常存在数据库或对象存储的元数据里)。
你可以把整个逻辑看作三层结构:
层级 | 名称 | 由谁管理 | 是否持久化 | 说明 |
---|---|---|---|---|
第 1 层 | CMK(主密钥) | Vault | ✅(保存在 Vault 内部/HSM) | 永不暴露明文,由 Vault 安全保存 |
第 2 层 | DEK(数据密钥) | 应用调用 Vault 生成 | ❌(明文仅短暂存在内存中) | 每次生成不同、随机 |
第 3 层 | 业务数据 | 应用层 | ✅(数据库 / 对象存储) | 被 DEK 加密后的密文数据 |
实际应用做法(标准信封加密)
1️⃣ 上传数据时
应用调用 Vault:
plaintext_dek, encrypted_dek = vault.generate_data_key()
然后:
- 用
plaintext_dek
加密文件内容 → 得到ciphertext_data
- 丢弃
plaintext_dek
- 把以下两样东西存到数据库或对象存储的元信息中:
{"ciphertext_data": "<加密后的文件数据>","encrypted_dek": "vault:v1:abcd...","nonce": "<AES GCM 随机向量>"
}
⚠️ 注意:你永远不直接存明文 DEK,只保存 Vault 返回的「被 CMK 加密后的 DEK」。
2️⃣ 下载 / 解密数据时
- 从数据库读出
encrypted_dek
和ciphertext_data
- 调用 Vault:
resp = client.secrets.transit.decrypt_data(name="my-app-key",ciphertext=encrypted_dek
)
plaintext_dek = base64.b64decode(resp["data"]["plaintext"])
- 再用
plaintext_dek
对ciphertext_data
解密,拿回明文。
3️⃣ 为什么要这样设计
设计点 | 目的 |
---|---|
每个数据独立的 DEK | 防止密钥复用、减少泄露影响面 |
只保存加密后的 DEK | 确保即使数据库泄露,Vault 不被攻破也无法解密数据 |
Vault 管理 CMK | 统一的信任根,支持轮换和审计 |
应用存储密文 + 加密DEK | 实现“有状态解密”,旧数据仍可被解 |
在 Vault 的 Transit 引擎 中:
name="my-app-key"
其实就是 你服务的主密钥(CMK)的逻辑标识符)- Vault 内部会为这个名字维护 一套 CMK 的密钥版本、属性和元数据
概念 | Vault 表现 | 作用 |
---|---|---|
CMK(Customer Master Key) | 逻辑名字 my-app-key 对应的密钥条目 | 作为信封加密根,用来加密所有 DEK |
密钥版本 | Vault 内部生成的 v1, v2, … | 支持轮换,解密旧数据时可选旧版本 |
属性 | 是否允许导出、轮换、签名能力等 | 控制密钥使用策略 |
存储位置 | Vault 安全存储或 HSM | 明文 CMK 永远不暴露给应用 |
当你调用 generate_data_key(name="my-app-key")
:
-
Vault 会随机生成一个 DEK
-
使用
my-app-key
对 DEK 进行加密 → 得到 encrypted DEK -
返回给应用:
- 明文 DEK → 内存中临时使用
- 加密 DEK → 持久化存储,用于后续解密
不管你生成多少 DEK,它们都是通过同一个 逻辑 CMK (
my-app-key
) 来加密封装的。
🔹 小结
name="my-app-key"
= 服务的 CMK 逻辑身份- 所有 DEK 都是由这个 CMK 加密的
- Vault 内部管理 CMK 的版本和轮换,但应用只知道名字,不接触 CMK 明文
所以,从应用角度看:
“我只需要记住 CMK 的名字
my-app-key
,然后每次调用 Vault 生成 DEK 或解密 DEK 就够了。”