You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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存在两处规范错误:

  1. 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字节(而非解析十六进制),导致密钥长度翻倍且完全不符合预期。
  2. IV不符合AES-CBC规范
    AES-CBC要求IV长度必须等于块大小(128位=16字节),原代码中仅用1字节IV,Oracle会自动补全0字节,但未显式处理易导致与JS逻辑不一致。

  3. JavaScript密钥解析错误
    JS中错误地使用CryptoJS.enc.Base64.parse()解析十六进制格式的密钥,应使用CryptoJS.enc.Hex.parse()(或直接用UTF8解析密钥字符串)。

  4. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 19:35:53