4、TDE密钥体系:数据库加密密钥(DEK)、证书/主密钥(CMK)、密钥存储与保护机制
TDE 的密钥体系,说白了就是一把钥匙锁着一把钥匙。最底层那把锁住数据页的钥匙叫 DEK,上面一层用 CMK 来保护它。你想想看,如果所有密钥都明文存着,那加密还有什么意义?
我记得刚接手第一个 TDE 项目时,文档里画了个三层密钥塔,我当时觉得嵌套解密会影响性能。后来才发现,这个分层设计是为了让密钥轮换和权限分离变得可行。下面我拆开来说。
4.1 数据库加密密钥(DEK)
DEK 是直接加密数据页的对称密钥。长度通常 128 位或 256 位,AES 算法。它的特点是:
- 数据库级别唯一:每个启用了 TDE 的数据库都有一份独立的 DEK。
- 自动生成:在启用 TDE 时由 SQL Server 内部创建,不需要你手动指定。
- 存储在数据文件中:DEK 本身会被 CMK 加密后写到数据库的 boot page 里。所以数据库文件即使被复制走,没有 CMK 也读不出 DEK。
这里有个坑:DEK 轮换不是直接替换 DEK,而是用新的 CMK 重新加密现有的 DEK。我在项目中遇到过,有人以为 DEK 轮换需要重建整个数据库——其实只要 ALTER DATABASE ENCRYPTION KEY 重新加密就行。
4.2 证书 / 主密钥(CMK)
CMK 是保护 DEK 的那把“主钥匙”。它可以是一个证书(包含非对称密钥对),也可以是一个存储在外部密钥管理器中的对称密钥。SQL Server 默认使用 CREATE CERTIFICATE 创建的证书作为 CMK。
CMK 的职责很简单:
- 加密 DEK 的明文副本
- 解密 DEK 到内存中用于数据页的加解密操作
CMK 本身又受数据库主密钥(DMK)或服务主密钥(SMK)保护。这个链条是这样的:
| 层级 | 密钥 | 保护对象 | 存储位置 |
|---|---|---|---|
| Level 1 | 服务主密钥 SMK | DMK / CMK | 操作系统 DPAPI |
| Level 2 | 数据库主密钥 DMK | 证书私钥 | master 数据库 |
| Level 3 | 证书 CMK | DEK | master 数据库 |
| Level 4 | 数据库加密密钥 DEK | 数据页 | 用户数据库 boot page |
如果你用的是 Azure Key Vault 或硬件安全模块(HSM),CMK 可以托管在外部,SQL Server 只保留一个指针。这种方式的好处是:即便整个数据库服务器被攻破,CMK 仍安全地待在 HSM 里。我在一次审计中遇到过,客户要求 CMK 必须由第三方密钥管理系统生成,我们就是通过扩展密钥管理(EKM)接口对接的。
4.3 密钥存储与保护机制
密钥存储不能“所有鸡蛋放一个篮子里”。实际部署中,我建议至少考虑三个存储位置:
- 本地加密存储:SMK 通过 Windows DPAPI 加密,存放在本机。这只能做第一道防线。
- 备份文件:证书和私钥必须备份到安全位置(比如离线磁带或加密U盘)。备份命令如:
BACKUP CERTIFICATE TDE_Cert TO FILE = 'C:\Backup\TDE_Cert.cer' WITH PRIVATE KEY (FILE = 'C:\Backup\TDE_Cert.pvk', ENCRYPTION BY PASSWORD = 'Str0ng!Passw0rd'); - 外部密钥存储:HSM(如 Thales、Gemalto)或云密钥服务(AWS KMS、Azure Key Vault)。适合需要合规审计的场景。
保护机制还包括访问控制:只有具备 CONTROL SERVER 权限的账号才能操作 CMK。而且 CMK 的私钥默认不能导出(除非指定 WITH PRIVATE KEY 并且知道密码)。
4.4 密钥体系全景图
下面这张图把上面讲的层级、关系和保护路径串起来了。你可以把它当作一个速查手册:
4.5 实践中的避坑指南
- 证书过期:SQL Server 证书默认有效期一年。如果证书过期,DEK 解密会失败,数据库无法正常启动。我在项目里遇到过一次半夜报警,就是因为证书到期,而自动轮换脚本还没写好。建议用
CREATE CERTIFICATE ... WITH EXPIRY_DATE = '9999-12-31'或配置自动续期。 - 丢失私钥:恢复数据库时需要 CMK 的私钥。如果只备份了公钥 (.cer),DEK 解不出来。备份命令一定要带
WITH PRIVATE KEY,并且记住你设置的加密密码。 - 跨服务器恢复:你不能把启用了 TDE 的数据库备份直接恢复到另一台服务器上,必须在那台服务器上创建相同的证书(基于备份文件)。
有的团队会在同一个服务器上用同一个证书保护多个数据库的 DEK,你想想看,这样虽然管理方便,但一个数据库的泄露可能让攻击者间接获得解密其他数据库的能力。所以我个人倾向每个数据库使用独立的证书,通过命名区分,比如 TDE_Cert_DB_Sales、TDE_Cert_DB_HR。