AES-256加密:PL/SQL dbms_crypto与JS CryptoJS输出不匹配排查
AES-256-CBC互操作:PL/SQL与CryptoJS加密结果匹配方案
问题概述
需实现JavaScript CryptoJS加密、PL/SQL DBMS_CRYPTO解密的AES-256-CBC互操作,但当前两者加密结果不一致,无法正常互解。
现有代码与结果
PL/SQL加密代码
FUNCTION cl_encrypt (p_string VARCHAR2, p_key VARCHAR2) RETURN VARCHAR2 AS lv_whats_my_name VARCHAR2 (100) := 'cl_utilities.cl_encrypt'; v_encrypted_raw RAW (32000); v_key RAW (320); v_encryption_type PLS_INTEGER := DBMS_CRYPTO.encrypt_aes256 + DBMS_CRYPTO.chain_cbc + DBMS_CRYPTO.pad_pkcs5; BEGIN v_key := RAWTOHEX (UTL_I18N.string_to_raw (p_key, 'AL32UTF8')); v_encrypted_raw := DBMS_CRYPTO.encrypt (src => UTL_I18N.string_to_raw (p_string, 'AL32UTF8'), typ => v_encryption_type, key => v_key,iv=>utl_raw.cast_to_raw(1)); RETURN UTL_RAW.cast_to_varchar2 (UTL_ENCODE.base64_encode (v_encrypted_raw)); END;
JavaScript加密代码
const CryptoJS = require('crypto-js'); const key = 'some key phrase to 0129034676438' const message = 'bob is your uncle' var encrypted = CryptoJS.AES.encrypt( message, CryptoJS.enc.Base64.parse( Buffer.from(key).toString('hex')), { iv: CryptoJS.enc.Utf8.parse(1), mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); console.log("encrypted vaule :: " + encrypted );
输入参数
- 密钥:
some key phrase to 0129034676438 - 明文:
bob is your uncle - IV:
1
加密结果
- PL/SQL输出:
UiJjrdgefVCcY1aJNuIt+oPPAPNOtnVTsE1g3sOwxiU= - JavaScript输出:
3UcXtyd9amnyjCYXKqi4ZrqB48ZerohDON80i1h4v2U=
问题根源
两者加密结果不一致的核心原因是密钥、IV处理逻辑不统一,且PL/SQL存在两处规范错误:
PL/SQL密钥处理逻辑错误
v_key := RAWTOHEX(UTL_I18N.string_to_raw(p_key, 'AL32UTF8'));存在逻辑错误:UTL_I18N.string_to_raw将密钥转成UTF8字节数组(RAW类型)。RAWTOHEX将该RAW转成十六进制字符串(VARCHAR2类型)。- 直接将VARCHAR2赋值给RAW类型的
v_key时,Oracle会把字符串的每个字符转成对应的ASCII字节(而非解析十六进制),导致密钥长度翻倍且完全不符合预期。
IV不符合AES-CBC规范
AES-CBC要求IV长度必须等于块大小(128位=16字节),原代码中仅用1字节IV,Oracle会自动补全0字节,但未显式处理易导致与JS逻辑不一致。JavaScript密钥解析错误
JS中错误地使用CryptoJS.enc.Base64.parse()解析十六进制格式的密钥,应使用CryptoJS.enc.Hex.parse()(或直接用UTF8解析密钥字符串)。IV字节值不匹配
PL/SQL中utl_raw.cast_to_raw(1)生成单字节0x01,而JS中CryptoJS.enc.Utf8.parse(1)生成单字节0x31(字符串'1'的UTF8编码),两者IV字节值完全不同。
修正方案
统一双方密钥、IV的处理逻辑,并修正不符合规范的代码:
修正后的PL/SQL加密代码
FUNCTION cl_encrypt (p_string VARCHAR2, p_key VARCHAR2) RETURN VARCHAR2 AS lv_whats_my_name VARCHAR2 (100) := 'cl_utilities.cl_encrypt'; v_encrypted_raw RAW (32000); v_key RAW (32); -- AES-256密钥固定32字节 v_iv RAW (16); -- AES-CBC IV固定16字节 v_encryption_type PLS_INTEGER := DBMS_CRYPTO.encrypt_aes256 + DBMS_CRYPTO.chain_cbc + DBMS_CRYPTO.pad_pkcs5; BEGIN -- 密钥处理:直接将密钥转成UTF8字节数组(密钥长度刚好32字节,符合AES-256要求) v_key := UTL_I18N.string_to_raw (p_key, 'AL32UTF8'); -- IV处理:显式生成16字节IV,首字节为'1'的UTF8编码(0x31),其余补0 v_iv := UTL_I18N.string_to_raw(RPAD('1', 16, CHR(0)), 'AL32UTF8'); v_encrypted_raw := DBMS_CRYPTO.encrypt ( src => UTL_I18N.string_to_raw (p_string, 'AL32UTF8'), typ => v_encryption_type, key => v_key, iv => v_iv ); RETURN UTL_RAW.cast_to_varchar2 (UTL_ENCODE.base64_encode (v_encrypted_raw)); END;
修正后的JavaScript加密代码
const CryptoJS = require('crypto-js'); const key = 'some key phrase to 0129034676438'; const message = 'bob is your uncle'; // 密钥处理:直接将密钥转成UTF8字节数组(与PL/SQL逻辑一致) const cryptoKey = CryptoJS.enc.Utf8.parse(key); // IV处理:生成16字节IV,与PL/SQL完全一致(首字节0x31,其余为0x00) const ivHex = '31' + '00'.repeat(15); const cryptoIv = CryptoJS.enc.Hex.parse(ivHex); const encrypted = CryptoJS.AES.encrypt( message, cryptoKey, { iv: cryptoIv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 } ); console.log("encrypted value :: " + encrypted.toString());
验证结果
修正后,双方加密结果将完全一致,PL/SQL可正常解密JavaScript加密的内容。
内容的提问来源于stack exchange,提问作者Larry G
相关产品推荐
相关产品推荐

