DBMS_CRYPTO不是开箱即用的加密开关,必须显式处理RAW类型转换、密钥长度、IV生成及BASE64编码,缺一环节即导致解密失败;src需UTL_I18N.STRING_TO_RAW转RAW,typ须组合常量如ENCRYPT_AES256+CHAIN_CBC+PAD_PKCS5,key长度严格匹配(AES-128为16字节),CBC模式下IV必填且须DBMS_CRYPTO.RANDOMBYTES生成,加密后必须UTL_ENCODE.BASE64_ENCODE再存VARCHAR2,解密前须BASE64_DECODE还原。
dbms_crypto 不是开箱即用的“加密开关”,它要求你显式处理 raw 类型、密钥长度、iv 生成和编码转换——漏掉任一环节,解密必然失败。
DBMS_CRYPTO.ENCRYPT 必须传全四个参数,缺一不可
常见错误是只传 src 和 key,结果报 ORA-28239: no encryption key specified 或 ORA-28233: invalid input to encrypt/decrypt。根本原因是 typ 和 iv 都不是可选占位符:
-
src必须是RAW,不能直接传VARCHAR2;用UTL_I18N.STRING_TO_RAW('xxx', 'AL32UTF8')显式转,避免隐式转换导致中文乱码 -
typ是算法+模式+填充的组合常量,例如DBMS_CRYPTO.ENCRYPT_AES256 + DBMS_CRYPTO.CHAIN_CBC + DBMS_CRYPTO.PAD_PKCS5;写成DBMS_CRYPTO.AES_CBC_PKCS5会报错——这不是 Oracle 的合法常量名 -
key长度必须严格匹配:AES-128 要RAW(16),AES-256 要RAW(32);用字符串生成时,UTL_I18N.STRING_TO_RAW('mykey', 'AL32UTF8')的字节数必须等于要求,否则触发ORA-28234: key length too short -
iv在 CBC 模式下必填且必须随机;ECB 模式虽不需iv,但已被视为不安全,Oracle 官方不推荐
加密输出是 RAW,存进表里前必须 BASE64 编码
直接把 DBMS_CRYPTO.ENCRYPT 返回的 RAW 存入 VARCHAR2 字段,看似成功,实则已损坏——Oracle 会隐式调用 UTL_RAW.CAST_TO_VARCHAR2,遇到非 ASCII 字节就截断或替换为 ?,解密时得到空值或乱码。
- 正确做法:加密后立刻用
UTL_ENCODE.BASE64_ENCODE转成 Base64 字符串,再存入VARCHAR2;解密前先用UTL_ENCODE.BASE64_DECODE还原为RAW - 别用
RAWTOHEX()——虽然能存,但十六进制字符串体积翻倍(2 字节表示 1 字节原始数据),且 Java/.NET 端默认不按 HEX 解析 - 更别用
UTL_RAW.CAST_TO_VARCHAR2(),它依赖数据库字符集,对二进制数据无意义
CBC 模式下 IV 必须每次加密都重新生成
硬编码 iv(比如全零或固定字符串)会让相同明文产生相同密文,完全暴露数据规律——这等于放弃了 CBC 的核心安全特性。
- 生成方式:用
DBMS_CRYPTO.RANDOMBYTES(16)(AES-128-CBC)或DBMS_CRYPTO.RANDOMBYTES(32)(AES-256-CBC);SYS_GUID()不是密码学安全随机源,禁用 - 存储方式:IV 不需要保密,但必须和密文绑定保存;常见做法是拼接在密文前(如
iv || ciphertext),或单独建字段存 - 解密时:从存储中取出原始
iv,原样传给DBMS_CRYPTO.DECRYPT的iv参数;漏传或传错长度会直接报错
跨语言解密失败,90% 出在三处对齐问题
Java 或 Python 解不出 Oracle 加密的数据,通常不是算法本身不兼容,而是参数没对齐。
- 模式必须一致:Oracle 用
CHAIN_CBC,Java 就得用"AES/CBC/PKCS5Padding",不能写成"AES/ECB/PKCS5Padding"或"AES/GCM/NoPadding" - 填充必须一致:Oracle 的
PAD_PKCS5等价于 PKCS#7 对 8 字节块,但很多 Java 库默认叫PKCS5Padding;确认 Java 使用的是PKCS5Padding,而非PKCS7Padding(部分库两者混用) - 密钥和 IV 编码必须一致:Oracle 用
UTL_I18N.STRING_TO_RAW(key, 'AL32UTF8'),Java 就必须用key.getBytes(StandardCharsets.UTF_8),不能用key.getBytes()(依赖系统默认编码)
最易被忽略的点是:加密后的 RAW 必须经过 BASE64_ENCODE 才能安全落库,而解密前必须用 BASE64_DECODE 还原——这个步骤一旦跳过,所有后续操作都是徒劳。


















