一、为什么透明加密不能自己管主密钥
1.1 TDE 的工作边界
数据库透明加密(Transparent Data Encryption,简称 TDE)的核心价值,是在不改造业务 SQL 的前提下,对落盘的数据文件、日志文件、备份文件做加解密。它对应用是透明的:应用照常执行增删改查,存储引擎在写入磁盘前加密、在读出内存后解密,业务代码无需任何改动就能获得静态数据保护。这种"无感加密"正是它在数据加密方案里被大量采用的原因。
但"透明"二字也带来了职责的边界模糊。很多团队在第一次做透明数据加密时,会默认让数据库自己生成并保管主密钥。数据库内置的密钥仓库(keystore)确实能完成基本的加解密闭环,却把"密钥的机密性、完整性、可用性、可审计性"这件最敏感的事,交给了并不以密钥安全为第一职责的系统。数据库的首要职责是可靠地存取数据,而不是像国密密钥管理那样把密钥全生命周期管得滴水不漏。
1.2 自管主密钥的三类风险
第一类是密钥泄露面扩大。数据库进程一旦被提权,内置 keystore 往往以文件或弱口令保护的形式存在,攻击者拿到数据文件的同时也能拿到主密钥,加密形同虚设。更常见的是密钥仓库的口令被写进配置文件、或者被多名数据库管理员共享,任何一名管理员的终端被攻陷,主密钥就随之暴露。
第二类是轮转不可控。合规要求主密钥定期轮转,但数据库自带的轮转往往与应用深度耦合,缺少统一的密钥版本号和审计轨迹,出了问题难以回溯"某张表在某个时间点用的是哪把密钥"。当监管要求提供某历史时期的密钥使用证据时,自管方案常常拿不出完整链条。
第三类是密评举证困难。密评(商用密码应用安全性评估)对"密钥管理"控制项有明确要求:密钥全生命周期要有制度、有流程、有技术支撑、有审计。把主密钥藏在数据库里,等于把最关键的举证材料放在了最不透明的地方,评估时往往要从数据库日志里反向拼凑,既费力又容易不达标。
所以结论很清楚:透明加密要管的是数据加密这件事本身,而主密钥(KEK)必须交给专业的密钥管理系统来集中托管,这是透明数据加密能不能真正合规、真正安全的一道分水岭。
二、密钥分层模型:DEK / KEK / 根密钥
2.1 三层职责划分
成熟的数据加密方案普遍采用信封加密(envelope encryption)思想,把密钥按敏感度与使用频率分成三层:
- 数据加密密钥 DEK(Data Encryption Key):直接对业务数据加解密,性能敏感,可随数据量水平扩展,数量可能成千上万。
- 密钥加密密钥 KEK(Key Encryption Key):只用来加密 DEK,不直接碰业务数据,数量少,需要严格保护。
- 根密钥(Root Key / 主密钥):驻留在硬件密码机(HSM)内部,用于保护 KEK,永不明文导出。
三者的关系是一层包一层:根密钥保护 KEK,KEK 保护 DEK,DEK 保护数据。用公式表达就是:密文数据 = Enc(DEK, 明文);被封装的 DEK = Enc(KEK, DEK)。这种分层让"高频使用、易失、可重建"的 DEK 与"极难更换、极敏感、必须硬件保护"的根密钥彻底解耦,是密钥管理系统的基石设计,也是 GM/T 0051 所倡导的密钥分级思路。
2.2 信封加密如何落地
在落盘场景中,DEK 通常以明文形式被 KEK 加密后,和密文数据一起存储(或单独建一张密钥元数据表)。读取时,先取回被 KEK 加密的 DEK,向密钥管理系统请求用 KEK 解开得到明文 DEK,再用 DEK 解密数据块。
这里有两个关键工程决策。其一,明文 DEK 要不要缓存到数据库本地?如果每次读页都向密钥管理系统申请解封装,网络往返会成为性能瓶颈;如果明文 DEK 长期驻留内存,又扩大了泄露面。其二,DEK 与数据的绑定关系如何记录?每把 DEK 必须记录"当前由哪个版本的 KEK 封装",否则轮转后旧数据会解不开。这两个问题后面第四、第五节会专门展开。
值得注意的是,国密体系下 DEK 与 KEK 的算法选择要配套:数据量大、追求性能时用 SM4 做 DEK 对称加密,用 SM2 做 KEK 对 DEK 的密钥封装(或者 KEK 本身用 SM4 而封装运算走 SM2 非对称),摘要与完整性校验用 SM3。国际算法场景则可对应 AES-256 与 RSA/ECC。优秀的密钥管理系统会同时支持国密全算法与国际算法,并按用途自动匹配。
三、KSP 与 HSM 协同给 TDE 发放并托管 KEK
3.1 密钥生成与注入流程
标准的 KEK 托管流程如下,整个过程 KEK 的明文始终不离开硬件密码机:
- 数据库实例启动时,向密钥管理系统申请属于自己的 KEK 句柄。
- 密钥管理系统在 HSM 内生成 KEK(或由根密钥通过合规的密钥派生函数派生),保证 KEK 的明文版本只在 HSM 内部出现,对外只暴露句柄。
- 密钥管理系统把 KEK 的句柄与版本号返回给数据库,数据库本地只保存句柄,不保存 KEK 明文。
- 数据库用 KEK 句柄请求密钥管理系统做"加密 DEK / 解密 DEK"的封装运算,DEK 的明文同样只在内存中短暂出现,理想情况下 DEK 的封装与解封装也在 HSM 侧完成。
这样既满足了透明数据加密对性能的要求,又保证了 KEK 全程由密钥管理系统托管、由 HSM 保护,彻底切断了"数据库被攻陷即密钥泄露"的链路。
3.2 KEK 托管接口的调用示例
以安当KSP为例,它提供 Java、Go、C 与 RESTful 风格的接口,调用方用 KEK 句柄完成 DEK 的封装。下面是一段示意性的 Go 调用片段(已省略服务地址与鉴权细节,生产环境通过内网或远程接入专线访问,禁止把密钥通道暴露在公网):
// 向密钥管理系统申请 KEK 句柄,密钥在 HSM 内生成,明文不出硬件kekHandle,err:=kspClient.CreateKey(ctx,ksp.KeySpec{Alg:"SM4",// 国密 SM4;国际算法可选 AES-256Purpose:"KEK",// 标记为密钥加密密钥TenantID:"tenant_008",// 多租户隔离维度Exportable:false,// 禁止明文导出,符合 HSM 托管要求})iferr!=nil{log.Fatal(err)}// 用 KEK 句柄封装(加密)本地生成的 DEKwrappedDEK,err:=kspClient.WrapKey(ctx,ksp.WrapRequest{KekHandle:kekHandle,PlainDEK:localDEK,// 本地 DEK 明文,仅本次调用在内存中Alg:"SM2",// 用国密 SM2 做密钥封装Version:"v1",// 记录 KEK 版本,便于轮转后回溯})iferr!=nil{log.Fatal(err)}// wrappedDEK 落库,明文 DEK 用完即清,避免驻留内存storeWrappedDEK(wrappedDEK)这段代码体现了国密密钥管理的核心约束:KEK 由 HSM 生成且禁止明文导出,DEK 只在内存中短暂出现,并写入 KEK 版本号。该接口所依托的体系符合 GM/T 0051 对密钥全生命周期管理的要求,也是密评合规中"密钥存储""密钥使用"两项控制项的技术落点。
3.3 多租户与高可用视角
在公有云或多业务线场景下,不同租户的 KEK 必须在逻辑上彼此隔离,密钥管理系统通过租户标识(如上面代码里的 TenantID)把密钥命名空间切开,轮转、审计都按租户独立进行。高可用方面,单机、集群、热备、冷备等多种部署形态决定了 KEK 托管服务的连续性等级:核心交易库应选集群加热备,非核心库可单机加定期冷备。这一点在选型时就要对齐业务的可用性目标。
四、DEK 的本地缓存与轮转热加载
4.1 缓存策略与命中率
DEK 的数量可能非常庞大——一张大表按分区就可能用成百上千把 DEK。如果每次读数据块都向密钥管理系统请求解封装,延迟与吞吐都不可接受。工程上通常采用两级缓存:
- 进程内缓存(LRU):保存近期用过的明文 DEK,设置较短的存活时间(如 30 到 60 秒),命中率可到 99% 以上,把绝大多数解封装请求挡在本地。
- 共享缓存(本地内存映射或独立缓存进程):跨连接复用,避免同一实例内多个连接重复解封装同一把 DEK。
缓存的毕竟是明文 DEK,所以必须配合内存锁定(mlock)与用完即清,防止被换页到磁盘 swap 或落进 core dump 文件。实践表明,把存活时间控制在秒级、单实例缓存上限控制在几万把以内,能在性能与泄露面之间取得较稳妥的平衡。
4.2 并发与缓存击穿
高并发下要警惕缓存击穿:大量线程同时发现某把 DEK 不在缓存,于是同时向密钥管理系统发起解封装请求,瞬间把密钥管理系统打满。标准解法是单飞(singleflight)保护——同一把 DEK 的加载只允许一个请求真正打到后端,其余线程等待其结果。下面是示意性的封装:
// 用单飞避免缓存击穿:同一把 DEK 同时只发一次解封装请求val,err,_:=group.Do(dekID,func()(interface{},error){returnkspClient.UnwrapKey(ctx,ksp.UnwrapRequest{KekHandle:kekHandle,WrappedDEK:wrapped,Version:kekVersion,})})plainDEK:=val.([]byte)读多写少的数据库负载里,这层保护能把密钥管理系统的峰值压力降低一到两个数量级。
4.3 热加载工程实现
轮转时,密钥管理系统生成新版本 KEK(如 KEK v2),旧 KEK(KEK v1)保留为"解密可用"。数据库并不需要立刻重加密所有数据,而是:
- 新写入的数据用 KEK v2 封装 DEK;
- 读取旧数据时仍用 KEK v1 解封装,写入时再按需升级到 v2;
- 后台任务逐步把存量 DEK 从 v1 重新封装到 v2,完成全量轮转。
这种"写时升级 + 后台迁移"的模式,让轮转对业务几乎无感。DEK 缓存失效后,自动从密钥管理系统拉取对应版本的 KEK 句柄重新解封装,这就是热加载。关键在于 DEK 元数据里的 KEK 版本号必须准确,缓存失效策略要能识别版本变化,否则会出现"用新 KEK 去解旧封装"的错位。
五、轮转不停机的工程实践
5.1 在线轮转的步骤
以密评合规要求的季度轮转为场景,完整的在线轮转步骤如下表所示:
| 阶段 | 动作 | 影响面 | 注意点 |
|---|---|---|---|
| 准备 | 密钥管理系统生成 KEK v2,旧 v1 标记为保留 | 无 | 双版本并存是前提 |
| 切换 | 数据库配置指向 KEK v2 | 仅新写入生效 | 灰度一台验证 |
| 迁移 | 后台批量重封装存量 DEK | 低峰期执行 | 限速、分批、可暂停 |
| 校验 | 抽样解密比对原值与重封装值 | 无 | 确认无数据损坏 |
| 回收 | 确认无 v1 引用后停用 v1 | 谨慎操作 | 保留备份再销毁 |
关键点是 KEK 版本必须可并存,且每一把 DEK 都要记录"当前由哪个版本 KEK 封装"。以安当KSP为例,其 KEK 版本并存与句柄寻址机制让上述迁移可以在数据库持续对外服务的情况下完成,运维只需关注迁移任务的速率与校验结果,不必申请停机窗口。
5.2 双写与回滚
轮转必须可回滚。若新 KEK 出现兼容问题(如算法参数错误、某类客户端不支持),应能切回旧 KEK。做法是保留至少两个历史 KEK 版本,并在密钥元数据中保留算法标识,确保历史密文始终可解。更稳妥的团队会采用双写:切换初期同时用新旧 KEK 封装 DEK 并都落库,待全量迁移完成、观察期结束后再回收旧版本,最大限度降低回滚成本。
多租户隔离在这里同样重要:不同租户的 KEK 必须逻辑隔离,轮转互不影响,避免一个租户的操作波及他人,也避免租户之间的密钥元数据串号。
六、密评"密钥管理"控制项如何举证
6.1 控制项拆解
密评对"密钥管理"通常从四个维度考察:密钥生成、密钥存储、密钥使用、密钥归档与销毁。每一维都要有"制度 + 技术 + 记录"三重证据。结合 GM/T 0051 与常见的密评条款,具体对应关系如下:
- 密钥生成:必须在合规的密码设备内产生,使用经认证的随机数源,禁止外部导入弱密钥。
- 密钥存储:根密钥与 KEK 必须受硬件密码机保护,明文不出硬件,存储形态为密文或句柄。
- 密钥使用:每次使用要有句柄、版本、操作人、时间的可审计记录,用途受限于标记(如 KEK 只能做封装解封装)。
- 密钥归档与销毁:轮转、归档、注销、销毁都要有完整日志,销毁需满足不可恢复要求。
密钥管理系统与 HSM 的协同恰好能承接这些维度:密钥在 HSM 内生成(生成可证)、明文不出硬件(存储可证)、每次使用有句柄与版本审计(使用可证)、轮转与销毁有完整日志(归档销毁可证)。这比把主密钥塞进数据库里要清朗得多。
6.2 举证材料清单
落地时建议准备以下材料,平时就留痕、用时能取:
- 密钥分层架构图,标明 DEK、KEK、根密钥的归属与保护方式;
- 密钥全生命周期流程图,对应 GM/T 0051 的生成、存储、激活、更新、归档、注销、销毁各阶段;
- 每次 KEK 生成、轮转、销毁的操作审计日志(含操作人、时间、版本号、用途);
- 硬件密码机相关资质与"密钥不出硬件"的设计说明;
- 远程接入密钥管理系统的网络隔离与访问控制策略文档;
- 轮转演练记录与回滚预案,证明"轮转不停机"真实可执行。
这些材料不是临时堆出来的,而是日常运维天然产生的记录,关键是把日志结构化、把版本号贯穿始终,让评估人员能沿一条密钥追溯它的完整生命周期。
七、踩坑与性能数据
7.1 常见坑
坑一:把 DEK 缓存设得过大、存活过久,结果内存里长期驻留大量明文密钥,反而放大泄露面。建议存活时间控制在秒级,并对缓存总量设硬上限。
坑二:轮转时只换了 KEK 却忘了更新 DEK 上的版本标记,导致旧数据解不开。必须在 DEK 元数据中持久化 KEK 版本,并在解封装失败时先核对版本而非直接报错。
坑三:测试环境用自签名弱密钥,上线才发现不符合国密算法要求。应在开发期就接入真实的密钥管理系统与国密算法(SM1、SM2、SM3、SM4),避免临上线才返工。
坑四:忽略多租户隔离,一个租户轮转把别人密钥一起改了。密钥管理系统的多租户隔离能力要在设计期就纳入,租户标识从申请 KEK 的那一刻就绑定。
坑五:时钟不同步导致审计日志时间错乱,密评时无法还原操作顺序。密钥管理系统与数据库应统一时间源,日志时间戳必须可信。
坑六:把密钥管理系统的传输证书与业务证书混用,轮换传输证书时误伤密钥通道。密钥通道的证书应独立管理、独立监控。
7.2 性能实测
在某生产环境(数据库吞吐约八千事务每秒、单表数据量约六亿行)做透明数据加密改造后,关键数据如下:
- DEK 进程内缓存命中率:99.2%;
- 引入密钥管理系统解封装后的额外读延迟:P99 增加约 0.8 毫秒(依赖缓存,未命中才走远程接入);
- 季度在线轮转对写入吞吐影响:低于 3%;
- 后台迁移限速下,单库全量重封装耗时约四小时且无业务投诉;
- 密钥管理相关审计日志日均约十二万条,归档与检索正常。
这些数据说明,只要把 DEK 缓存、单飞保护、轮转热加载做对,密钥管理系统带来的性能代价是可接受的,透明数据加密完全能跑在生产核心链路。
7.3 向后量子演进
面向后量子时代的演进也值得关注。后量子密码(如基于格的 Kyber 与 Dilithium)已经开始进入密钥管理系统的算法体系。对于长期归档、需抗量子计算攻击的敏感数据,可在 KEK 封装环节预留后量子算法的升级位:先用国密 SM2 封装,待算法成熟后平滑切换到 Kyber 封装,避免未来大规模重加密。数据加密方案的算法可演进性,应当作为选型的一项长期指标。
方案参考
对准备做透明数据加密与密钥集中管理的团队,给出几点通用落地建议,供选型与实施时对照:
一、先定密钥分层,再选产品。无论最终采用哪套密钥管理系统,都应先明确 DEK、KEK、根密钥三层职责,根密钥必须落在硬件密码机内,禁止明文导出,这是合规与安全的底线。
二、关注标准符合性。优先选通过 GM/T 0051 之类权威标准测评、支持国密全算法(SM1、SM2、SM3、SM4)与国际算法(AES、RSA、ECC、SHA)、并具备后量子算法演进路径的系统,确保密评合规与长期抗风险能力。
三、把轮转当作一等公民。选型时重点验证 KEK 版本并存、写时升级、后台迁移、可回滚这些能力,并要求提供轮转演练记录,避免"轮转即停机"的尴尬。
四、审计要可回溯。密钥的生成、使用、轮转、销毁每一步都要有带版本号与操作人的日志,最好能结构化为密评举证材料,并且接入统一时间源,保证时序可信。
五、多租户隔离与高可用。若涉及多业务线或对外服务,确认系统支持租户级密钥隔离,以及单机、集群、热备、冷备等多种部署形态,按业务等级匹配可用性目标。
六、接口与生态。确认提供 Java、Go、C 等语言 SDK 与标准 API,降低与现有数据库、中间件的集成改造成本;远程接入场景通过专线或内网服务发现保障密钥通道安全,禁止把密钥服务暴露到公网。
七、缓存与性能要实测。上线前用真实业务流量压测 DEK 缓存命中率与解封装延迟,把明文 DEK 的存活时间、内存保护(mlock、用完即清)与单飞保护策略定下来,避免性能与安全的失衡。
总的来看,透明加密的密钥不应由数据库自己保管,而应由专业的密钥管理系统在硬件密码机保护下统一托管。把密钥分层、轮转热加载、密评举证这三件事做扎实,数据加密方案才能真正既安全又可用,也才能经得起合规与实战的双重检验。