Ошибка при использовании функции DBMS_CRYPTO для расшифровки данных CLOB
Мне поручено выполнить две функции Oracle, которые позволяют достичь следующего:
- Возьмите CLOB в качестве входных данных и зашифруйте его с помощью AES-256 и верните зашифрованный CLOB
- Возьмите зашифрованный CLOB как вход, расшифруйте его с помощью AES-256 и верните расшифрованный CLOB
Данные CLOB огромны, и функции должны позаботиться об этом.
Я могу выполнить первую функцию, и она хорошо работает, т.е. шифрует CLOB с огромными данными:
create or replace
function F_ENCRYPT_CLOB (ac_input IN CLOB) return CLOB is
l_clob CLOB;
lb_variable BLOB;
v_key RAW (320);
v_encryption_type PLS_INTEGER := DBMS_CRYPTO.AES_CBC_PKCS5;
v_iv RAW (320);
l_dest_offset PLS_INTEGER := 1;
l_src_offset PLS_INTEGER := 1;
l_lang_context PLS_INTEGER := DBMS_LOB.default_lang_ctx;
l_warning PLS_INTEGER;
l_step PLS_INTEGER := 1998;
begin
SELECT VALUE
INTO v_key
FROM algparameters
WHERE name = 'key';
SELECT VALUE
INTO v_iv
FROM algparameters
WHERE name = 'iv';
dbms_lob.createtemporary(lb_variable, true);
sys.DBMS_CRYPTO.ENCRYPT(
dst => lb_variable,
src => ac_input,
typ => v_encryption_type,
key => v_key,
iv => v_iv
);
DBMS_LOB.createTemporary(
lob_loc => l_clob,
cache => TRUE);
FOR i IN 0 .. TRUNC((DBMS_LOB.getlength(lb_variable) - 1 )/l_step) LOOP
l_clob := l_clob || UTL_RAW.cast_to_varchar2(UTL_ENCODE.base64_encode(DBMS_LOB.substr(lb_variable, l_step, i * l_step + 1)));
END LOOP;
RETURN l_clob;
end F_ENCRYPT_CLOB;
Но я столкнулся с проблемами при расшифровке ранее зашифрованного значения с помощью аналогичных шагов:
create or replace
function F_DECRYPT_CLOB (ac_input IN CLOB) return CLOB is
lb_variable CLOB;
l_blob BLOB;
v_key RAW (320);
v_encryption_type PLS_INTEGER := DBMS_CRYPTO.AES_CBC_PKCS5;
v_iv RAW (320);
l_dest_offset PLS_INTEGER := 1;
l_src_offset PLS_INTEGER := 1;
l_lang_context PLS_INTEGER := DBMS_LOB.default_lang_ctx;
l_warning PLS_INTEGER;
l_raw RAW(32767);
l_amt NUMBER := 7700;
l_offset NUMBER := 1;
l_temp VARCHAR2(32767);
l_step PLS_INTEGER := 7700;
begin
SELECT VALUE
INTO v_key
FROM algparameters
WHERE name = 'key';
SELECT VALUE
INTO v_iv
FROM algparameters
WHERE name = 'iv';
dbms_lob.createtemporary(l_blob, true);
FOR i IN 0 .. TRUNC((DBMS_LOB.getlength(ac_input) - 1 )/l_amt) LOOP
DBMS_LOB.read(ac_input, l_amt, l_offset, l_temp);
l_offset := l_offset + l_amt;
l_raw := UTL_ENCODE.base64_decode(l_temp);
DBMS_LOB.append (l_blob, TO_BLOB(l_raw));
END LOOP;
dbms_lob.createtemporary(lb_variable, true);
sys.DBMS_CRYPTO.DECRYPT(
dst => lb_variable,
src => l_blob,
typ => v_encryption_type,--dbms_crypto.des_cbc_pkcs5,
key => v_key,
iv => v_iv
);
return lb_variable;
end F_DECRYPT_CLOB;
Ошибка, которую он выдает:
ORA-06502: PL/SQL: числовая ошибка или ошибка значения: ошибка преобразования шестнадцатеричного в необработанное
ORA-06512: в "SN_PRE_STAGE_415.F_DECRYPT_CLOB", строка 33
06502. 00000 - "PL/SQL: числовая ошибка или ошибка значения% s"
* Причина:
* Действие:
1 ответ
Ошибка исходит из строки 33, а именно:
l_raw := UTL_ENCODE.base64_decode(l_temp);
В base64_decode
функция ожидает аргумент RAW, поэтому вы можете преобразовать имеющуюся строку:
l_raw := UTL_ENCODE.base64_decode(UTL_RAW.cast_to_raw(l_temp));
т.е.
FOR i IN 0 .. TRUNC((DBMS_LOB.getlength(ac_input) - 1 )/l_amt) LOOP
DBMS_LOB.read(ac_input, l_amt, l_offset, l_temp);
l_offset := l_offset + l_amt;
l_raw := UTL_ENCODE.base64_decode(UTL_RAW.cast_to_raw(l_temp));
DBMS_LOB.append (l_blob, TO_BLOB(l_raw));
END LOOP;
Теперь он бросает это
ORA-28817: функция PL/SQL вернула ошибку.
ORA-06512: в "SYS.DBMS_CRYPTO_FFI", строка 110
ORA-06512: в "SYS.DBMS_CRYPTO", строка 64
ORA-06512: в "STACKOVERFLOW.F_DECRYPT_CLOB", строка 39
В вашей строке base-64 есть разрывы строк; те выбрасывают декодирование. Вы можете использовать меньший размер блока, сl_amt = 64
, и пропустить разрывы строк:
FOR i IN 0 .. TRUNC((DBMS_LOB.getlength(ac_input) - 1 )/(l_amt + 2)) LOOP
DBMS_LOB.read(ac_input, l_amt, l_offset, l_temp);
l_offset := l_offset + l_amt + 2;
но, вероятно, проще и эффективнее удалить их все за один присест с помощью нового l_clob
переменная:
create or replace
function F_DECRYPT_CLOB (ac_input IN CLOB) return CLOB is
lb_variable CLOB;
l_clob CLOB;
l_blob BLOB;
...
begin
...
dbms_lob.createtemporary(l_blob, true);
l_clob := replace(replace(ac_input, chr(13), null), chr(10), null);
FOR i IN 0 .. TRUNC((DBMS_LOB.getlength(l_clob) - 1 )/l_amt) LOOP
DBMS_LOB.read(l_clob, l_amt, l_offset, l_temp);
l_offset := l_offset + l_amt;
l_raw := UTL_ENCODE.base64_decode(utl_raw.cast_to_raw(l_temp));
DBMS_LOB.append (l_blob, TO_BLOB(l_raw));
END LOOP;
...
end F_DECRYPT_CLOB;
/
В полном объеме:
create or replace
function F_DECRYPT_CLOB (ac_input IN CLOB) return CLOB is
lb_variable CLOB;
l_clob CLOB;
l_blob BLOB;
v_key RAW (320);
v_encryption_type PLS_INTEGER := DBMS_CRYPTO.AES_CBC_PKCS5;
v_iv RAW (320);
l_dest_offset PLS_INTEGER := 1;
l_src_offset PLS_INTEGER := 1;
l_lang_context PLS_INTEGER := DBMS_LOB.default_lang_ctx;
l_warning PLS_INTEGER;
l_raw RAW(32767);
l_amt NUMBER := 7700;
l_offset NUMBER := 1;
l_temp VARCHAR2(32767);
l_step PLS_INTEGER := 7700;
begin
SELECT VALUE
INTO v_key
FROM algparameters
WHERE name = 'key';
SELECT VALUE
INTO v_iv
FROM algparameters
WHERE name = 'iv';
dbms_lob.createtemporary(l_blob, true);
l_clob := replace(replace(ac_input, chr(13), null), chr(10), null);
FOR i IN 0 .. TRUNC((DBMS_LOB.getlength(l_clob) - 1 )/l_amt) LOOP
DBMS_LOB.read(l_clob, l_amt, l_offset, l_temp);
l_offset := l_offset + l_amt;
l_raw := UTL_ENCODE.base64_decode(utl_raw.cast_to_raw(l_temp));
DBMS_LOB.append (l_blob, TO_BLOB(l_raw));
END LOOP;
dbms_lob.createtemporary(lb_variable, true);
sys.DBMS_CRYPTO.DECRYPT(
dst => lb_variable,
src => l_blob,
typ => v_encryption_type,--dbms_crypto.des_cbc_pkcs5,
key => v_key,
iv => v_iv
);
return lb_variable;
end F_DECRYPT_CLOB;
/