Oracle的列级加密是TDE(Transparent Data Encryption,透明数据加密)最早提供的加密能力,从10g R2版本就已经引入,经过多个大版本的演进,目前依然是保护敏感字段最常用的手段。它的核心价值在于:数据在写回到数据文件、UNDO段、临时表空间之前就被加密,即便有人直接拷贝了数据文件或者备份介质丢失,拿到的也只是一堆密文。而对应用程序来说,查询语句完全不变,插入更新照常执行,解密过程在SGA的内存层自动完成,这就是“透明”二字的含义。

一、列级加密的工作原理与适用范围
TDE列级加密采用双层密钥体系。最外层是主加密密钥(Master Key),存储在Oracle Wallet钱包中;每个启用了加密的表会对应一个表密钥(Table Key),表密钥本身用主密钥加密后存放在数据字典里。当会话访问加密列时,Oracle先打开钱包取出主密钥,解密出表密钥,再用表密钥解密列数据,整个过程对上层完全不可见。
正因为这种结构,钱包的管理就成了整个方案的安全基石。钱包文件一旦丢失且没有备份,加密数据将永久无法恢复,这一点在规划阶段就必须想清楚。列级加密适合字段数量相对集中的场景,比如一张用户表里只有身份证号、手机号、薪资金额三四个敏感列,加密这些列带来的开销可控。如果整张表80%以上的列都需要保护,那更适合考虑19c推荐的表空间加密方案。
列级加密支持的加密算法包括3DES168、AES128、AES192、AES256,其中AES256是目前的默认算法,安全性和性能的平衡也最好。此外列级加密有一个经常被忽略的附加能力:Salt和BMAC完整性校验。加了Salt之后,相同明文在不同行的密文不同,可以防止通过密文统计推断数据的攻击,代价是该列无法直接建索引。
二、钱包的创建与基础配置
使用列级加密前必须先配置钱包。从12c开始推荐使用Keystore统一术语,配置参数主要是WALLET_ROOT和TDE_CONFIGURATION,而11g及以前用的是ENCRYPTION_WALLET_LOCATION。以19c为例,先在spfile或pfile中指定钱包目录:
ALTER SYSTEM SET WALLET_ROOT='/u01/app/oracle/wallet/tde' SCOPE=SPFILE; ALTER SYSTEM SET TDE_CONFIGURATION='KEYSTORE_CONFIGURATION=FILE' SCOPE=SPFILE; -- 重启实例后创建钱包并设置主密钥 ADMINISTER KEY MANAGEMENT CREATE KEYSTORE '/u01/app/oracle/wallet/tde' IDENTIFIED BY "WalletPass#2024"; ADMINISTER KEY MANAGEMENT SET KEYSTORE OPEN IDENTIFIED BY "WalletPass#2024"; ADMINISTER KEY MANAGEMENT SET KEY IDENTIFIED BY "WalletPass#2024" WITH BACKUP;
主密钥设置时的WITH BACKUP子句会在钱包目录下生成一个备份文件,这个备份和ewallet.p12主文件都要妥善离线保存。钱包打开后可以查询确认状态:
SELECT con_id, status FROM v$encryption_wallet; -- STATUS列应为 OPEN,表示钱包已打开可用 SELECT * FROM v$wallet;
生产环境建议把IDENTIFIED BY换成本地密码文件存储方式(EXTERNAL STORE),这样密码不会出现在SQL脚本或shell历史记录里。另外要注意自动登录钱包(AUTO LOGIN)的取舍:开启后数据库重启时钱包自动打开,运维方便,但拿到服务器文件系统权限的攻击者也能直接读取密钥。安全要求高的系统建议采用延迟自动登录钱包,设定一个密码重试间隔来抵御离线暴力破解。
三、加密列的添加、修改与索引处理
钱包就绪后就可以对表进行加密。新表可以在建表时直接声明加密,已有表则通过ALTER TABLE在线添加。下面是一个完整的示例:
-- 建表时直接声明加密列 CREATE TABLE cust_info ( cust_id NUMBER PRIMARY KEY, cust_name VARCHAR2(60), id_card VARCHAR2(30) ENCRYPT USING 'AES256', mobile VARCHAR2(20) ENCRYPT NO SALT, salary NUMBER(10,2) ENCRYPT USING 'AES256' ); -- 对已存在的表在线添加加密列 ALTER TABLE cust_info ADD (bank_no VARCHAR2(30) ENCRYPT); -- 把已有明文列改为加密列 ALTER TABLE cust_info MODIFY (id_card ENCRYPT USING 'AES256' NO SALT); -- 取消加密,还原为明文列 ALTER TABLE cust_info MODIFY (id_card DECRYPT);
这里有个非常关键的限制需要反复强调:带Salt的加密列不能直接创建B-tree索引。因为Salt是在加密前附加的随机值,导致相同值的密文不同,索引无法比较。如果业务上需要对手机号、身份证号做等值查询或关联查询,就必须声明NO SALT。而NO SALT又会带来密文重复的统计风险,这是一个需要在安全和性能之间做出的权衡决策。
对千万级以上的大表执行MODIFY ENCRYPT时要特别注意UNDO和锁的问题。11g中这个操作会对全表加排他锁,业务必须停写窗口。12c以后可以加ONLINE子句实现在线转换,但仍会产生大量redo和归档,建议在业务低峰期分批执行,并提前评估归档空间。转换进度可以通过查询V$ENCRYPTION_WALLET和相关数据字典视图跟踪,也可以估算为全表数据量的一次重写。
另一个实际开发中容易踩的坑是字符集问题。加密后的VARCHAR2列无法直接修改字符集,如果系统未来计划从ZHS16GBK迁移到AL32UTF8,必须先DECRYPT再迁移再加密。此外加密会额外占用存储空间,每行每个加密列大约多出几十字节的开销,规划表空间容量时要留出余量。
四、性能影响评估与密钥管理建议
列级加密的性能开销主要来自加解密运算,实测情况大约在3%到8%之间,具体取决于加密列的数量、Salt设置以及是否有索引。有两个因素会放大开销:一是加密列出现在WHERE条件中但用了Salt导致索引失效,触发全表扫描;二是加密列上创建了B-tree索引后,范围查询必须改写。对于需要模糊查询的场景,可以考虑把查询列冗余一份密文哈希值做等值匹配。
密钥管理是整个方案的命门,以下几条经验值得落实:第一,钱包密码要满足复杂度要求并定期轮换,轮换主密钥使用ADMINISTER KEY MANAGEMENT SET KEY,该操作不会重写全部数据,只重新加密各表的表密钥,耗时很短;第二,钱包文件和备份必须至少两份异地存放,权限限制为Oracle软件属主可读;第三,RMAN备份要确认加密列在备份集中保持密文状态,这是TDE天然保证的,无需额外配置;第四,Data Guard环境下主备库共用同一个钱包主密钥,备库需要同步配置钱包目录和密码文件。
-- 主密钥轮换,建议每年执行一次 ADMINISTER KEY MANAGEMENT SET KEY USING TAG 'rotate_2024q4' IDENTIFIED BY "WalletPass#2024" WITH BACKUP; -- 查看当前主密钥信息 SELECT key_id, tag, activation_time FROM v$encryption_keys;
最后做方案选型时可以这样判断:如果只是个别敏感字段需要保护,列级加密部署成本最低、改造量最小,直接上TDE列加密即可;如果整库或整表空间都需要加密,12c以后的表空间加密在性能和索引支持上都更优。两种方式可以并存,按列按需选择,逐步推进,才是兼顾合规要求和系统稳定性的务实做法。
Oracle列级加密TDE透明数据加密wallet钱包管理修改时间:2026-09-03 05:54:43