我們在Oracle預存程序中所包含的商業秘密,有時不願意被第三方人員看到,可以通過對預存程序加密來實現。 有兩種加密預存程序的方法:使用wrap工具或dbms_ddl包。下面我們分解介紹一下。 1. wrap工具Wrap是Oracle所提供的作業系統級的命令,文法如下:
| wrap iname=input_file [oname=output_file] |
參數iname為要加密的檔案名稱,oname為加密後的檔案名稱。如果省略oname,那麼將會自動產生一個同名的加密檔案名稱,且尾碼為plb。 我們來示範一下wrap工具的用法。首先建立一個名稱為test1.sql的檔案:
CREATE OR REPLACE FUNCTION get_date_string RETURN VARCHAR2 AS BEGIN RETURN TO_CHAR(SYSDATE, ‘DD-MON-YYYY’); END get_date_string; / |
它儲存在D盤根目錄。現在我來將它加密:
| D:/>dir test* 磁碟機 D 中的卷是 D 卷的序號是 15C2-D261 D:/ 的目錄 2009-12-26 16:35 138 test1.sql 1 個檔案 138 位元組 0 個目錄 2,052,046,848 可用位元組 D:/>wrap iname=d:/test1.sql PL/SQL Wrapper: Release 10.2.0.1.0- Production on 星期六 12月 26 16:36:01 2009 Copyright (c) 1993, 2004, Oracle. All rights reserved. |
| Processing d:/test1.sql to test1.plb D:/>dir test* 磁碟機 D 中的卷是 D 卷的序號是 15C2-D261 D:/ 的目錄 2009-12-26 16:36 350 test1.plb 2009-12-26 16:35 138 test1.sql 2 個檔案 488 位元組 0 個目錄 2,052,046,848 可用位元組 |
看看加密後的檔案test1.plb中的內容:
CREATE OR REPLACE FUNCTION get_date_string wrapped a000000 354 abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd 8 71 ae P29RDhRZX0orO0ED/mMF8i12Glkwg8eZgcfLCNL+XlquYvSuoVah8JbRPpdHDOrnwLK9spte 58d0wDO4dGUJuHSLwMAy/tKGCamhAs7G1hohrO/WTHaEcTKOd0xx9RBzc/XvN2dM6+zZPXLp r1UqFBwU/Sx2010pwUjXpqZCvywG / |
現在你可以把檔案test1.plb發給客戶使用了,而不必擔心你的原始碼的暴露。 這個工具使用起來很簡單,也很方便,尤其可以批量產生預存程序的加密檔案。 2. dbms_ddl包從Oracle 10g Release 2開始,你可以使用DBMS_DDL包來動態加密預存程序。 如果你只想將寫完的原始碼進行加密,就像剛才我們講到的wrap工具那樣,你可以使用dbms_ddl.wrap函數;如果你想加密原始碼並在資料庫中建立它,那麼你可以使用dbms_ddl.create_wrapped。 2.1 wrap如果你不想建立這個過程,而只是想獲得這個加密後的建立指令碼,然後把指令碼發給客戶去執行,這可以使用函數dbms_ddl.wrap。它的功能和“wrap“工具相同。我們先看一下這個函數的定義:
| FUNCTION wrap(ddl VARCHAR2) RETURN VARCHAR2;FUNCTION wrap(ddl dbms_sql.varchar2s, lb PLS_INTEGER, ub PLS_INTEGER) RETURN dbms_sql.varchar2s; FUNCTION wrap(ddl dbms_sql.varchar2a, lb PLS_INTEGER, ub PLS_INTEGER) RETURN dbms_sql.varchar2a; |
它被重載了三次:第一個函數接收VARCHAR2類型的輸入,後兩個函數接收VARCHAR2集合的輸入(允許大的DDL語句的輸入)。 入參ddl要求文法為”create or replace…”的字串,用以建立包、包體、類型、類型體、函數和過程的程式單元的DDL語句。如果入參ddl所定義的程式單元不能被加密,或存在語法錯誤,則將拋出“MALFORMED_WRAP_INPUT”異常。 入參lb為密碼編譯集合的最低元素,ub為密碼編譯集合的最高元素。 傳回值即為加密後的代碼。你可以將它寫入一個檔案中,或者儲存在表中。 我們先使用第一個wrap函數實現一個簡單的應用。我想擷取如下過程加密後的代碼:
| create or replace procedure p2asbegin dbms_output.put_line(’yuechaotian’); end; |
使用wrap函數來實現它:
| SQL> select dbms_ddl.wrap(’create or replace procedure p2 as begin dbms_output.put_line(”yuechaotian”); end; ‘) 2 from dual; DBMS_DDL.WRAP(’CREATEORREPLACEPROCEDUREP2ASBEGINDBMS_OUTPUT.PUT_LINE(”YUECHAOTIAN”);END;’) ————————————————————————— create or replace procedure p2 wrapped a000000 354 abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd 7 41 7d ND2TI3IsThvViemtwIoHec7RMCMwg5nnm7+fMr2ywFwWfAm4dCu4wDL+0l64UpuySv4osr3n srMdBjAsriTqsoGXJXddX9vk8UfbejIu9kTqJB/2RDmmF3HTZA== |
所產生的密文,你可以直接拷貝過來在資料庫中執行:
| SQL> create or replace procedure p2 wrapped 2 a000000 3 354 4 abcd 5 abcd 6 abcd 7 abcd 8 abcd 9 abcd 10 abcd 11 abcd 12 abcd 13 abcd 14 abcd 15 abcd 16 abcd 17 abcd 18 abcd 19 7 20 41 7d 21 ND2TI3IsThvViemtwIoHec7RMCMwg5nnm7+fMr2ywFwWfAm4dCu4wDL+0l64UpuySv4osr3n 22 srMdBjAsriTqsoGXJXddX9vk8UfbejIu9kTqJB/2RDmmF3HTZA== 23 / 過程已建立。 SQL> exec p2 yuechaotian PL/SQL 過程已成功完成。 |
你也可以把它儲存到純文字檔案或表中,在以後執行。如果你產生的程式碼要在其它地方部署,並且必須要保證代碼的安全性,則這種方法很有用。 注意,這裡的原始碼是以VARCHAR2類型傳遞給wrap函數的。因為VARCHAR2類型的長度有限制(32767位元組),對於很長的過程(比如一個2w行的包體),就得使用另外兩個重載的wrap函數了:
| FUNCTION wrap(ddl dbms_sql.varchar2s, lb PLS_INTEGER, ub PLS_INTEGER) RETURN dbms_sql.varchar2s;FUNCTION wrap(ddl dbms_sql.varchar2a, lb PLS_INTEGER, ub PLS_INTEGER) RETURN dbms_sql.varchar2a; |
這裡的輸入輸出使用聯合數組(associative array)來實現。兩者的不同在於類型dbms_sql.varchar2s限制為每行256位元組,而類型dbms_sql.varchar2a為32767位元組。我們舉一個例子:
| SET SERVEROUTPUT ON SIZE UNLIMITEDDECLARE l_source DBMS_SQL.VARCHAR2A; l_wrap DBMS_SQL.VARCHAR2A; BEGIN l_source(1) := ‘CREATE OR REPLACE FUNCTION get_date_string RETURN VARCHAR2 AS ‘; l_source(2) := ‘BEGIN ‘; l_source(3) := ‘RETURN TO_CHAR(SYSDATE, ”DD-MON-YYYY”); ‘; l_source(4) := ‘END get_date_string;’; l_wrap := SYS.DBMS_DDL.WRAP(ddl => l_source, lb => 1, ub => l_source.count); FOR i IN 1 .. l_wrap.count LOOP DBMS_OUTPUT.put_line(l_wrap(i)); END LOOP; END; / CREATE OR REPLACE FUNCTION get_date_string wrapped a000000 b2 abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd 8 6f aa mV4eMSJ8EqqgErJT91l6UZ0pdDUwgyr6LZ5GfHSmUPiJfkEObQpeDb6D7glajI+ONulxdqC1 0HvOPP4eJpQs5zxsKXpj6XL1 fvieXyWCr3BTzXTqcGYhfXrtqDVPztR/o+9UZ8l5OijDSsRW ZPv6rISzFyqeEsCBweFUFyxd PL/SQL procedure successfully completed. |
2.2 create_wrapped除了wrap函數外,在dbms_ddl包中還包括三個重載的create_wrapped過程:
PROCEDURE create_wrapped(ddl VARCHAR2); PROCEDURE create_wrapped(ddl dbms_sql.varchar2s, lb PLS_INTEGER, ub PLS_INTEGER); PROCEDURE create_wrapped(ddl dbms_sql.varchar2a, lb PLS_INTEGER, ub PLS_INTEGER); |
它們擁有和wrap一樣的入參和出參。使用方法也和wrap函數類似,不過要注意它們是過程(而不是函數)。 與函數wrap不同,過程create_wrapped不但加密原始碼,而且還會在資料庫中執行加密後的密文。我們先看一個例子,我要在資料庫中以加密的方式建立這個過程:
| create or replace procedure p1asbegin dbms_output.put_line(’yuechaotian’); end; |
我可以藉助過程dbms_ddl.create_wrapped:
| SQL> begin 2 dbms_ddl.create_wrapped 3 (’create or replace procedure p1 4 as 5 begin 6 dbms_output.put_line(”yuechaotian”); 7 end;’); 8 end; 9 / PL/SQL 過程已成功完成。 SQL> set serveroutput on SQL> exec p1 yuechaotian PL/SQL 過程已成功完成。 |
看看加密後的代碼:
| – 1. 從視圖 user_source 中查詢SQL> set pagesize 1000SQL> col text format a100 SQL> set line 2000 SQL> SELECT TEXT FROM user_source WHERE NAME = ‘P1′; TEXT ————————————————————————– procedure p1 wrapped a000000 354 abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd 7 4a 81 aVtlrmIdKjyE1rQszxgCZ9Z7+YEwg5nnm7+fMr2ywFwWfF0Jabh0OdPc4rlBP2ejNa+V+nhX GSQhFMohoijjhhB6c3Eqd9UMXreC004MZbZOdyo7N55Mc3HyiKbboLaf – 2. 使用 get_ddl 查詢 SQL> set long 10000 SQL> select dbms_metadata.get_ddl(’PROCEDURE’,'P1′) from dual; DBMS_METADATA.GET_DDL(’PROCEDURE’,'P1′) ———————————————————————— CREATE OR REPLACE PROCEDURE “TEST”.”P1″ wrapped a000000 354 abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd 7 4a 81 aVtlrmIdKjyE1rQszxgCZ9Z7+YEwg5nnm7+fMr2ywFwWfF0Jabh0OdPc4rlBP2ejNa+V+nhX GSQhFMohoijjhhB6c3Eqd9UMXreC004MZbZOdyo7N55Mc3HYiKbboLaf |
這樣,你就在資料庫中建立了一個加密原始碼的預存程序。 |