正常情况,用户不能访问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秒多。效果理想。