1. 程式人生 > >AES加解密程式的實現

AES加解密程式的實現

AES加解密程式的實現
正常情況,使用者不能訪問sys.dbms_crypto,需要DBA授權:grant execute on dbms_crypto to crm;
建立加解密的PKG_AES包:
CREATE OR REPLACE PACKAGE PKG_AES IS
FUNCTION F_ENCRYPT(I_INPUT_STRING VARCHAR2) RETURN VARCHAR2;
FUNCTION F_DECRYPT(I_INPUT_STRING VARCHAR2,I_KEY_STRING VARCHAR2) RETURN VARCHAR2;
END;
/

CREATE OR REPLACE PACKAGE BODY PKG_AES IS
--加密
FUNCTION F_ENCRYPT(I_INPUT_STRING VARCHAR2) RETURN VARCHAR2 IS
V_KEY_STRING RAW(128) := UTL_RAW.CAST_TO_RAW('abcdefgh123456781234567812345678'); --加密串,同時也是解密串
V_ENCRYPTED_RAW RAW(200);
BEGIN
V_ENCRYPTED_RAW := DBMS_CRYPTO.ENCRYPT(SRC => UTL_RAW.CAST_TO_RAW(I_INPUT_STRING) --被加密的字串
,TYP => DBMS_CRYPTO.AES_CBC_PKCS5 --加密演算法,演算法有很多
,KEY => V_KEY_STRING); --加密串

RETURN(RAWTOHEX(V_ENCRYPTED_RAW));
END F_ENCRYPT;
--解密
FUNCTION F_DECRYPT(I_INPUT_STRING VARCHAR2,I_KEY_STRING VARCHAR2) RETURN VARCHAR2 IS
V_KEY_STRING RAW(128) := UTL_RAW.CAST_TO_RAW(I_KEY_STRING); --加密串,同時也是解密串
V_DECRYPTED_RAW RAW(200);
BEGIN
V_DECRYPTED_RAW := DBMS_CRYPTO.DECRYPT(SRC => HEXTORAW(I_INPUT_STRING)
,TYP => DBMS_CRYPTO.AES_CBC_PKCS5
,KEY => V_KEY_STRING);

RETURN(UTL_RAW.CAST_TO_VARCHAR2(V_DECRYPTED_RAW));
END F_DECRYPT;
END PKG_AES;
/


測試:
SQL> select PKG_AES.F_ENCRYPT('123456789') from dual;
PKG_AES.F_ENCRYPT('123456789')
--------------------------------------------------------------------------------
AAF86856CE79BFCE484B9B4DD686C92E

SQL> select PKG_AES.F_DECRYPT('AAF86856CE79BFCE484B9B4DD686C92E','abcdefgh123456781234567812345678') from dual;
PKG_AES.F_DECRYPT('AAF86856CE7
--------------------------------------------------------------------------------
123456789

之前的AES和DES加解密程式雖然實現了字串的加解密,但如果接觸到plsql程式碼就可以看到加解密串,使用這個加解密串就可以進行解密。
為了避免這種情況的發生,可以用oracle提供的wrap工具對pl/sql程式碼進行加密。
wrap是Oracle所提供的作業系統級的命令,語法如下語法如下:
wrap iname=input_file [oname=output_file]
iname 關鍵字用於指定輸入檔案的路徑名稱 oname關鍵字用於指定輸出加密後文件的路徑名稱,如果省略oname,則會自動生成一個同名的加密檔名,且字尾為plb。
下面是wrap的用法:
在D盤根目錄下建立一個名稱為pkg_aes.sql的檔案,檔案內容就是上面包的程式碼,具體為:
CREATE OR REPLACE PACKAGE PKG_AES IS
FUNCTION F_ENCRYPT(I_INPUT_STRING VARCHAR2) RETURN VARCHAR2;
FUNCTION F_DECRYPT(I_INPUT_STRING VARCHAR2,I_KEY_STRING VARCHAR2) RETURN VARCHAR2;
END;
/

CREATE OR REPLACE PACKAGE BODY PKG_AES IS
--加密
FUNCTION F_ENCRYPT(I_INPUT_STRING VARCHAR2) RETURN VARCHAR2 IS
V_KEY_STRING RAW(128) := UTL_RAW.CAST_TO_RAW('abcdefgh123456781234567812345678'); --加密串,同時也是解密串
V_ENCRYPTED_RAW RAW(200);
BEGIN
V_ENCRYPTED_RAW := DBMS_CRYPTO.ENCRYPT(SRC => UTL_RAW.CAST_TO_RAW(I_INPUT_STRING) --被加密的字串
,TYP => DBMS_CRYPTO.AES_CBC_PKCS5 --加密演算法
,KEY => V_KEY_STRING); --加密串

RETURN(RAWTOHEX(V_ENCRYPTED_RAW));
END F_ENCRYPT;
--解密
FUNCTION F_DECRYPT(I_INPUT_STRING VARCHAR2,I_KEY_STRING VARCHAR2) RETURN VARCHAR2 IS
V_KEY_STRING RAW(128) := UTL_RAW.CAST_TO_RAW(I_KEY_STRING); --加密串,同時也是解密串
V_DECRYPTED_RAW RAW(200);
BEGIN
V_DECRYPTED_RAW := DBMS_CRYPTO.DECRYPT(SRC => HEXTORAW(I_INPUT_STRING)
,TYP => DBMS_CRYPTO.AES_CBC_PKCS5
,KEY => V_KEY_STRING);

RETURN(UTL_RAW.CAST_TO_VARCHAR2(V_DECRYPTED_RAW));
END F_DECRYPT;
END PKG_AES;
/


使用wrap命令將以上程式碼加密:
在cmd命令視窗中執行
D:\>wrap iname=d:\pkg_aes.sql
用記事本開啟加密後的pkg_aes.PLB檔案,可看到檔案內容為:
CREATE OR REPLACE PACKAGE PKG_AES IS
FUNCTION F_ENCRYPT(I_INPUT_STRING VARCHAR2) RETURN VARCHAR2;
FUNCTION F_DECRYPT(I_INPUT_STRING VARCHAR2,I_KEY_STRING VARCHAR2) RETURN VARCHAR2;
END;
/
CREATE OR REPLACE PACKAGE BODY PKG_AES wrapped
a000000
367
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
b
438 1c6
O/zFOzXyHW28E7xVeonfs7nb1Jgwgw3DTCDWyo5Vx6qVENUULutFMvbJRTb3owY5NksTrpGa
+LWdcHAtDdRiTCVVW5Q0dnIAwaLpEaGeOVhH9mkB7ueC3dcRNrzs2UXTd8A+cM11DkUrb1LG
AuXYgivHzN2nEUWoe68OWDugtCA2yuMNMmwKBba8IUKiTpH07p9Eje2/PwBj21QXATBghsGc
scT+0HJkQsBCDVO8kzO7C27mWwDAC0JXVQ5JA4jcE0pflHHktB69XTHWUtdBbmGEqy44MniF
zTA/zw5Soy5+O+H1/gqHn4CC/MOh1wZyqYeDprQ9U1wXKA1OOUL5K0i0tvz6qYwrWV1bsOvu
dl2KsXZZMLEeqqFbLHX1B7a+kInWhhmHy4OoC8s0kSvLdapIu3Lgcw5OEKS6LGmu44YU2+oi
vTiktS8plXgBmjr+
/

把加密後的內容放到庫的命令視窗執行,即可編譯通過。
儲存過程加密前後的使用方式沒有任何區別。
由於加密後的內容無法解密,也就不用擔心原始碼的暴露問題了。
這個工具使用起來簡單方便,尤其是可以批量生成儲存過程的加密檔案。


效能測試:
drop table t purge;
create table t as select * from dba_objects;
alter table T add phone VARCHAR2(50);
update t set phone=to_char(rownum+13012345678);
73257 rows updated
commit;
加密效能測試:
set timing on
update t set phone=PKG_AES.F_ENCRYPT(phone,'');
73257 rows updated
Executed in 6.266 seconds
commit;
select phone from T where rownum<=10;
PHONE
--------------------------------------------------
2A168C06FF70340C656238ADA69E78E3
39785A5583587C6979EA46615928C139
7272FB647D7D4065238CD253F6062AED
0323CECA5BA5E1229D5133570B19CAFE
510C4B9FD3228F0B3DC1DD8C1E4D6783
9E3FA4BCD4AB3B257C6981091A564CFC
955CE3C5E30D6CEF08575F53EAAEF6A9
42503F4F21D9CDCF4D468E094A6E4FD5
B6BC3B8C74E9B5D5D9B233EE223EB09B
FE42348C7FEE46750401F0DAF5EE47B6

解密效能測試:
set timing on
update t set phone=PKG_AES.F_DECRYPT(phone,'abcdefgh123456781234567812345678');
73257 rows updated
Executed in 5.219 seconds
commit;
select phone from T where rownum<=10;
PHONE
--------------------------------------------------
13012345679
13012345680
13012345681
13012345683
13012345684
13012345685
13012345687
13012345688
13012345689
13012345690
結論:
對7萬行的小表,加密用時6秒多,解密用時5秒多。效果理想。

把解密的解密串拿出包後,對大表加解密測試:
構建大表:
insert into t select * from t;
insert into t select * from t;
... ...
commit;
select count(*) from t;
COUNT(*)
----------
2344224
加密效能測試:
update t set phone=PKG_AES.F_ENCRYPT(phone);
2344224 rows updated
Executed in 212.578 seconds
commit;

解密效能測試:
update t set phone=PKG_AES.F_DECRYPT(phone,'abcdefgh123456781234567812345678');
2344224 rows updated
Executed in 196.297 seconds
結論:
對230萬行的大表,加密用時212秒多,解密用時196秒多。效果理想。