Oracle中如何使用dbms_crypto进行数据加密与解密?

来源:Nodejs教程作者:清原小日向头衔:网络博主
导读:本期聚焦于清原小日向创作的《Oracle中如何使用dbms_crypto进行数据加密与解密?》,敬请观看详情。数据库中的敏感字段一旦被拖库,明文存储会造成难以挽回的损失。Oracle 内置的 DBMS_CRYPTO 包提供了一套完整的加密解密与哈希运算接口,支持 AES、DES、3DES 等对称算法,以及 SHA-1、SHA-256 等摘要算法。调用 ENCRYPT 和 DECRYPT 函数时需要传入 RAW 类型的数据,并指定算法、链式模式和填充方式,很多报错正是源自类型转换或参数组合不当。该包还支持 HMAC 消息认证码和随机数生成,能够满足字段级加密、数据完整性校验等场景。不过使用前需要单独授权,密钥管理也有不少讲究。本文从函数调用层面展开,结合哈希与 MAC 的用法,分析常见错误和最佳实践,让读者能够安全落地数据加密需求。

Oracle 数据库的 DBMS_CRYPTO 包是一个内置的加密工具集,提供了对称加密、哈希摘要、消息认证码以及随机数生成等功能。相比早期常用的 DBMS_OBFUSCATION_TOOLKIT,DBMS_CRYPTO 支持更丰富的算法和更灵活的调用方式,且自 Oracle Database 10g 起就可以直接使用。需要特别留意的是,默认情况下普通用户并没有该包的执行权限,需要由 DBA 或具有授权能力的用户执行类似 GRANT EXECUTE ON SYS.DBMS_CRYPTO TO app_user; 的授权语句。本文将围绕加密与解密函数的核心用法、哈希与消息认证码的应用场景,以及密钥管理和常见错误排查展开说明。

Oracle中如何使用dbms_crypto进行数据加密与解密?

一、加密与解密函数的基础调用

DBMS_CRYPTO 包中最常用的两个函数是 ENCRYPT 和 DECRYPT。它们都要求输入参数和输出结果为 RAW 类型,因此如果业务表中存储的是 VARCHAR2 字段,就必须先完成类型转换。常见的做法是使用 UTL_I18N.STRING_TO_RAW 并显式指定字符集,比如 AL32UTF8,这样可以避免数据库字符集与客户端字符集不一致造成的乱码问题。反向操作则使用 UTL_I18N.RAW_TO_CHAR 还原字符串。很多初学者在这里忽略字符集参数,直接使用 UTL_RAW.CAST_TO_RAW,结果在不同环境下解密后得到乱码,排查起来非常耗时。

加密类型参数 typ 由三部分组成,分别是加密算法、链式模式和填充模式,三者通过加法组合成一个整型常量。常用的加密算法有 ENCRYPT_AES128、ENCRYPT_AES192、ENCRYPT_AES256、ENCRYPT_DES 和 ENCRYPT_3DES。链式模式推荐使用 CHAIN_CBC,因为它能隐藏数据块之间的关系,安全性高于 CHAIN_ECB。填充模式中 PAD_PKCS5 最常用,它会自动把明文补齐到加密算法要求的块大小;如果选择 PAD_NONE,则明文长度必须刚好是块大小的整数倍,否则会抛出异常。

下面通过一个完整的 PL/SQL 块演示 AES256 加密和解密的过程。示例中使用了固定密钥和初始化向量,实际生产环境不应硬编码这些值,这里只是为了展示调用语法。

DECLARE
  v_plain_text    RAW(2000) := UTL_I18N.STRING_TO_RAW('敏感数据', 'AL32UTF8');
  v_key           RAW(32) := UTL_I18N.STRING_TO_RAW('0123456789ABCDEF0123456789ABCDEF', 'AL32UTF8');
  v_iv            RAW(16) := UTL_I18N.STRING_TO_RAW('1234567890123456', 'AL32UTF8');
  v_encrypted     RAW(2000);
  v_decrypted     RAW(2000);
  v_decrypted_str VARCHAR2(2000);
BEGIN
  v_encrypted := DBMS_CRYPTO.ENCRYPT(
                   src => v_plain_text,
                   typ => DBMS_CRYPTO.ENCRYPT_AES256 + DBMS_CRYPTO.CHAIN_CBC + DBMS_CRYPTO.PAD_PKCS5,
                   key => v_key,
                   iv => v_iv
                 );
  DBMS_OUTPUT.PUT_LINE('密文: ' || RAWTOHEX(v_encrypted));

  v_decrypted := DBMS_CRYPTO.DECRYPT(
                   src => v_encrypted,
                   typ => DBMS_CRYPTO.ENCRYPT_AES256 + DBMS_CRYPTO.CHAIN_CBC + DBMS_CRYPTO.PAD_PKCS5,
                   key => v_key,
                   iv => v_iv
                 );
  v_decrypted_str := UTL_I18N.RAW_TO_CHAR(v_decrypted, 'AL32UTF8');
  DBMS_OUTPUT.PUT_LINE('解密后: ' || v_decrypted_str);
END;

这段代码先使用 UTL_I18N.STRING_TO_RAW 将中文字符串转换成 RAW 类型,然后调用 DBMS_CRYPTO.ENCRYPT 生成密文。解密时使用完全相同的算法、密钥和 IV,最终还原出原始字符串。如果加密和解密时任何一个参数不匹配,都会导致解密失败或抛出 ORA-28817 错误。此外,IV 的长度必须与加密算法的块大小一致,AES 的块大小为 16 字节,因此示例中使用了 RAW(16) 的 IV。

二、哈希与消息认证码的用法

除了加密,DBMS_CRYPTO 还提供哈希函数 HASH,用于生成数据摘要。哈希是不可逆的,相同的输入始终得到相同的输出,但无法从摘要反推出原文。这一特性非常适合存储密码或者校验文件内容是否被篡改。支持的哈希算法包括 HASH_MD5、HASH_SH1、HASH_SH256、HASH_SH384 和 HASH_SH512,其中 SHA-256 目前被广泛采用,安全性高于 MD5 和 SHA-1。调用时同样传入 RAW 类型的数据,返回固定长度的 RAW 值。

DECLARE
  v_data  RAW(2000) := UTL_I18N.STRING_TO_RAW('要计算摘要的文本', 'AL32UTF8');
  v_hash  RAW(32);
BEGIN
  v_hash := DBMS_CRYPTO.HASH(
              src => v_data,
              typ => DBMS_CRYPTO.HASH_SH256
            );
  DBMS_OUTPUT.PUT_LINE('SHA-256哈希值: ' || RAWTOHEX(v_hash));
END;

哈希结果通常用 RAWTOHEX 转成十六进制字符串后存入数据库,以便比较和展示。需要注意的是,哈希函数本身不带密钥,任何人都可以计算同一段数据的摘要。如果攻击者知道原文,就能提前算出哈希值,因此单纯哈希无法防止伪造。此时需要引入带密钥的消息认证码 MAC。

DBMS_CRYPTO.MAC 函数与哈希类似,但多了一个密钥参数,用于防止数据在传输或存储过程中被恶意替换。它实现的 HMAC 算法要求收发双方共享同一个密钥,任何一方修改数据都无法生成匹配的认证码。下面是一个使用 HMAC-SHA256 的示例。

DECLARE
  v_data  RAW(2000) := UTL_I18N.STRING_TO_RAW('需要防篡改的数据', 'AL32UTF8');
  v_key   RAW(32) := UTL_I18N.STRING_TO_RAW('0123456789ABCDEF0123456789ABCDEF', 'AL32UTF8');
  v_mac   RAW(32);
BEGIN
  v_mac := DBMS_CRYPTO.MAC(
             src => v_data,
             typ => DBMS_CRYPTO.HMAC_SH256,
             key => v_key
           );
  DBMS_OUTPUT.PUT_LINE('HMAC-SHA256值: ' || RAWTOHEX(v_mac));
END;

在实际项目中,MAC 常用于 API 签名校验和数据库字段的完整性保护。例如将关键业务字段和 MAC 值一起存储,读取时重新计算 MAC 并与存储值比较,不一致则说明数据可能被篡改。结合加密和 MAC,可以实现数据的机密性和完整性双重保障。不过要注意 MAC 和加密使用的密钥最好分开管理,避免一个密钥泄露导致两个防线同时失效。

三、密钥管理与常见错误排查

密钥管理是数据库加密中最容易被忽视却又最关键的一环。把密钥硬编码在 PL/SQL 包、存储过程或配置表里,等于把锁和钥匙放在一起。一旦代码泄露或者数据库被拖库,加密就形同虚设。更稳妥的做法是使用 Oracle Wallet 或外部密钥管理系统来存储主密钥,应用层只持有密钥的引用或别名。对于必须存放在数据库中的场景,也要将密钥表单独放在受限表空间,并严格控制查询权限。

权限不足是使用 DBMS_CRYPTO 时最常见的错误之一。如果没有授予执行权限,调用时会抛出 ORA-01031: insufficient privileges 或 PLS-00201: identifier DBMS_CRYPTO must be declared。解决方法是让 DBA 执行授权语句,并且注意不要使用 GRANT EXECUTE ON DBMS_CRYPTO,而要指定 SYS.DBMS_CRYPTO,否则可能因同义词解析问题导致授权失败。另一个常见错误是 ORA-28234: key length too short,这通常是因为密钥长度不满足算法要求,比如 AES256 需要 32 字节密钥,而传入的 RAW 长度只有 16 字节。排查时可以用 UTL_RAW.LENGTH 检查密钥实际长度。

字符集和类型转换问题也不容小觑。如果加密时使用 UTL_I18N.STRING_TO_RAW 指定了 AL32UTF8,解密时却使用 UTL_RAW.CAST_TO_RAW 依赖数据库默认字符集,那么一旦数据库字符集不是 UTF8 或与客户端不一致,解密结果就会变成乱码。建议所有转换操作都显式指定 AL32UTF8,并在数据库初始化参数中统一字符集。另外,DBMS_CRYPTO.ENCRYPT 的输入参数不能为 NULL,否则会抛出异常,因此在封装函数时需要先进行空值判断。

针对性能和安全策略,应尽量避免对超长字段或高频查询列直接使用 DBMS_CRYPTO 做全量加密,因为这样会增加 CPU 开销并影响索引效率。更合理的做法是只加密核心敏感列,并在应用层完成加解密后再传入数据库。如果确实需要加密大字段,可以考虑使用 Oracle 的透明数据加密 TDE,它在存储层面自动完成加解密,对应用透明。对于必须使用 DBMS_CRYPTO 的场景,推荐采用 CBC 模式并生成随机 IV,每次加密前重新生成 IV 并连同密文一起存储,能有效防止相同的明文产生相同的密文,从而抵抗模式分析攻击。

Oracle dbms_crypto加密解密DBMS_CRYPTO修改时间:2026-09-24 13:48:17

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0924/61335.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。