使用DBMS_CRYPTO.ENCRYPT时触发ORA-01890 NLS错误问题咨询
错误根因
你代码的核心逻辑错误出现在加密后的结果转换步骤:
DBMS_CRYPTO.ENCRYPT返回的是无规律的二进制RAW数据,不存在符合任何字符集编码规则的约束- 你直接调用
UTL_I18N.RAW_TO_CHAR(v_raw_row, 'AL32UTF8')尝试将随机二进制强制转为UTF-8字符串,一旦加密结果中出现不符合UTF-8编码规范的字节序列,就会触发NLS校验错误 - 这就是为什么部分输入报错、部分输入正常,修改密钥/加密算法仅能临时规避当前报错输入,后续必然会遇到其他触发报错的输入,和Oracle版本无关
解决方案
加密后的二进制结果不能直接转普通字符串,必须通过无损编码转换为可打印字符串,常用两种实现方式:
- 转换为十六进制字符串:使用Oracle内置函数
RAWTOHEX,直接将RAW类型的二进制转为全大写的十六进制可打印字符串 - 转换为Base64字符串:使用
UTL_ENCODE.BASE64_ENCODE进行编码,体积比十六进制小33%,适合存储传输场景
修正后可运行代码示例
declare -- v_value VARCHAR2 (4000) := '9Ab2Ov1Bd4'; -- 运行正常 -- v_value VARCHAR2 (4000) := '7Md4Mt7Gk0'; -- 运行正常 -- v_value VARCHAR2 (4000) := '3Vf8Fi2Pa5'; -- 运行正常 -- v_value VARCHAR2(4000) := '5Vq2Dc4Cq9'; -- 运行正常 v_value VARCHAR2(4000) := '2Cq0Yh3Vb2'; -- 原报错取值,修改后可正常运行 v_result VARCHAR2 (4000); v_raw_row RAW (2000); -- 存储加密二进制文本 v_enc_type PLS_INTEGER := DBMS_CRYPTO.ENCRYPT_AES256 + DBMS_CRYPTO.CHAIN_CBC + DBMS_CRYPTO.PAD_ZERO; -- 加密算法组合 v_def_k VARCHAR2 (32) := 'mMbSmSMr_!_uAlns9asG5_a_AfhS4_3a'; begin v_raw_row := DBMS_CRYPTO.ENCRYPT ( src => UTL_I18N.STRING_TO_RAW (v_value, 'AL32UTF8'), typ => v_enc_type, key => UTL_RAW.CAST_TO_RAW (v_def_k)); -- 修正:将加密后的二进制转为十六进制字符串,不会触发编码错误 v_result := RAWTOHEX(v_raw_row); -- 如果需要用Base64编码,替换为以下两行即可 -- v_result := UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(v_raw_row)); dbms_output.put_line(v_result); end; /
补充说明
解密时对应做逆向转换即可:如果加密后存的是十六进制字符串,解密前先调用HEXTORAW转回二进制再传给DBMS_CRYPTO.DECRYPT;如果存的是Base64字符串,先调用UTL_ENCODE.BASE64_DECODE转回二进制即可。
内容的提问来源于stack exchange,提问作者Dantel35
相关产品推荐
相关产品推荐

