December 28 2015

DBMS_CRYPTO

在我的博客里已经写过了使用md5、tde等来加密数据,也写过用WRAP来加密包和函数。今天我们再来看看如何使用dbms_crypto这个包来加密数据。

DBMS_CRYPTO提供了一个加密和解密存储数据的接口,并可用于结合PL / SQL程序运行的网络通信。它提供了支持几个行业标准加密和散列算法,包括高级加密标准(AES)加密算法。AES已通过国家标准与技术研究所(NIST)替换数据加密标准(DES)

DBMS_CRYPTO包含基本的加密函数和过程。正确地使用这个包可以使你的数据达到一般安全水平。

DBMS_CRYPTO包支持加密和解密为常见的Oracle数据类型,包括原料和大对象(lob),如图片和声音。具体地说,它支持blob和clob。另外,它还提供了全球化对加密数据在不同的数据库字符集的支持。

支持加密算法如下:

Data Encryption Standard (DES), Triple DES (3DES, 2-key and 3-key)
Advanced Encryption Standard (AES)
MD5, MD4, and SHA-1 cryptographic hashes
MD5 and SHA-1 Message Authentication Code (MAC)

DBMS_CRYPTO用来取代DBMS_OBFUSCATION_TOOLKIT,提供更大的方便使用和支持一系列算法,以适应新的和现有的系统。

使用DBMS_CRYPTO和其它的加密算法不同的是:当对一段数据进行加密时,算法不会对数据整体加密,通常会分成8个字节的小块,对每个小块进行加密;如果数据恰好不够8位时,这时,需要进行填充,补齐8字节。当数据被拆分成小块加密后,需要将其相邻的小块进行连接起来。

 

首先我们先来创始一个用户并给他可以执行dbms_crypto这个包的权限

[codesyntax lang=”sql” lines=”normal”]

SQL> create user ucjmh identified by oracle;

User created.

SQL> grant dba to ucjmh;

Grant succeeded.

SQL> grant execute on dbms_crypto to ucjmh;

Grant succeeded.

[/codesyntax]

 

 

我们接下来来创始加密的函数来方面我们使用然后对照这个函数我们来看一下这个包的一些可调用的方法。

[codesyntax lang=”sql” lines=”normal”]

create or replace function f_Encrypt_number(number_in in varchar2) return raw is
number_in_raw RAW(128):=UTL_I18N.STRING_TO_RAW(number_in,'AL32UTF8');
key_number number(32):=32432432343243279898;
key_raw RAW(128):=UTL_RAW.cast_from_number(key_number);
encrypted_raw RAW(128);
begin
encrypted_raw:=dbms_crypto.Encrypt(src=>number_in_raw,typ=>DBMS_CRYPTO.DES_CBC_PKCS5,key=>key_raw);
return encrypted_raw;
end;
/

[/codesyntax]

在函数的返回值里我指点一个raw转变 是因为encrypt函数不但需要raw型数据,而且还需要使用专门的字符集——AL32UTF8,这里如果使用utl_raw.cast_to_raw,则会出现“ORA-06502”

[codesyntax lang=”sql”]

06502, 00000, "PL/SQL: numeric or value error%s"
// *Cause: An arithmetic, numeric, string, conversion, or constraint error
// occurred. For example, this error occurs if an attempt is made to
// assign the value NULL to a variable declared NOT NULL, or if an
// attempt is made to assign an integer larger than 99 to a variable
// declared NUMBER(2).
// *Action: Change the data, how it is manipulated, or how it is declared so
// that values do not violate constraints.

[/codesyntax]

 

 

可以看到上面我们用到了dbms_crypto.Encrypt这个函数

This function encrypts RAW data using a stream or block cipher with a user supplied key and optional IV (initialization vector).

这个函数加密原始数据使用一个流或第四块密码与用户提供的关键字和可选iv(初始化向量)。

This package includes both ENCRYPT and DECRYPT procedures and functions. The procedures are used to encrypt or decrypt LOB datatypes (overloaded for CLOB and BLOB datatypes). In contrast, the ENCRYPT and DECRYPT functions are used to encrypt and decrypt RAW datatypes. Data of type VARCHAR2 must be converted to RAW before you can use DBMS_CRYPTO functions to encrypt it.

 

全量语法如下:

DBMS_CRYPTO.ENCRYPT(
   src IN RAW,
   typ IN PLS_INTEGER,
   key IN RAW,
   iv  IN RAW          DEFAULT NULL)
 RETURN RAW;

src RAW data to be encrypted.
typ Stream or block cipher type and modifiers to be used.
key Encryption key to be used for encrypting data.
iv Optional initialization vector for block ciphers. Default is NULL.

 

官方也建议定义自己的包访问常量来表示你用于加密和解密的密码套件来改善可读性,。例如,下面的例子定义了一个使用DES密码套件,密码块链接模式,没有填充:

DES_CBC_NONE CONSTANT PLS_INTEGER := DBMS_CRYPTO.ENCRYPT_DES
                                     + DBMS_CRYPTO.CHAIN_CBC
                                     + DBMS_CRYPTO.PAD_NONE;

在我创建的方法中我使用的中typ=>DBMS_CRYPTO.DES_CBC_PKCS5
你也可以使用typ=>你的密码套件  DES_CBC_NONE
还有关于key的话其实也是有要求的 我这里用的是cs5的算法 申明了32但是只用了20没关第 但是如果你使用了128位的加密算法,每8位进行加密的话,那么128除以8,正好是16位,所以密钥长度必须是16位,不能太长或太短,否则会出现“ORA-28234”

28234, 00000, "key length too short"
// *Cause: The key specified is too short for the algorithm. DES
// requires a key of at least 8 bytes. Triple DES requires a
// key of least 16 bytes in two-key mode and 24 bytes in three-key
// mode.
// *Action: Specify a longer key.

下面我们列出一些dbms_crypto包加密算法的算法常量:
ENCRYPT_DES:标准数据加密。有效的键长度为56位,
ENCRYPT_3DES_2KEY:修改过的3DES,用两个密钥对每个数据块加密3次。有效的键长度为112位。
ENCRYPT_3DES:对每一个数据块加密3次。有效的键长度为156位。
ENCRYPT_AES128:高级加密标准。有效的键长度为128位。
ENCRYPT_AES192:高级加密标准。有效的键长度为192位。
ENCRYPT_AES256:高级加密标准。有效的键长度为256位。
ENCRYPT_RC4:唯一一个流加密,它被用于加密数据流,而不是离散数据或是表态数据。
DBMS_CRYPTO包的填充常量:
PAD_PKCS5:用PKCS#5填充。
PAD_ZERO:用零填充。
PAD_NONE:不进行填充,如果假设数据块的长度正好是8个字节,则可以使用这个方法。
DBMS_CRYPT0包的连接常量:
CHAIN_CBC:密码块连接,是最常用的方法。
CHAIN_CFB:加密反馈模式。
CHAIN_ECB:电子源码书格式。
CHAIN_OFB:输入回馈模式。
创建好了加密的方法我们来使用一下:
SQL> SELECT f_Encrypt_number('UCJMH') FROM DUAL;

F_ENCRYPT_NUMBER('UCJMH')
--------------------------------------------------------------------------------
E2CF813996C1F1CB

果然变成了乱七八燥的东西,达到了加密的目的。
接下来我们再创始解密的方法:
1 create or replace function f_decrypt_number (encrypted_raw IN RAW)
 2 return varchar2 is
 3 decrypted_raw raw(48);
 4 key_number number(32):=32432432343243279898;
 5 key_raw RAW(128):=UTL_RAW.cast_from_number(key_number);
 6 begin
 7 decrypted_raw := DBMS_CRYPTO.DECRYPT(src => encrypted_raw,
 8 typ => DBMS_CRYPTO.DES_CBC_PKCS5,
 9 key => key_raw
 10 );
 11 return UTL_I18N.RAW_TO_CHAR (decrypted_raw, 'AL32UTF8');
 12* END;
SQL> /

Function created.

把刚刚加密的东西解出来看看可以发现果然是对的
SQL> select f_decrypt_number('E2CF813996C1F1CB') from dual;

F_DECRYPT_NUMBER('E2CF813996C1F1CB')
--------------------------------------------------------------------------------
UCJMH

上面要注意你的key_number一定要对应 是什么不重要 重要的加密和解密的要一样。

在解密函数里我们使用了DBMS_CRYPTO.DECRYPT 这个函数的语法和加密里的类似。但是要注意的是如果VARCHAR2之前转换为原始数据加密,那么它必须被转换回适当的数据库字符集利用UTL_I18N包。
在你使用的过程中可能会遇到的错误有:
Exception Code Description
CipherSuiteInvalid 28827 The specified cipher suite is not defined.
CipherSuiteNull 28829 No value has been specified for the cipher suite to be used.
KeyNull 28239 The encryption key has not been specified or contains a NULL value.
KeyBadSize 28234 DES keys: Specified key size is too short. DES keys must be at least 8 bytes (64 bits).

AES keys: Specified key size is not supported. AES keys must be 128, 192, or 256 bits in length.

DoubleEncryption 28233 Source data was previously encrypted.

好了 我们再来做最后一个实验把刚刚的函数应用到表里去

[codesyntax lang=”sql”]

SQL> create table test_crypt ( name varchar2(50));

Table created.
SQL> insert into test_crypt values(f_Encrypt_number('binbin'));

1 row created.

SQL> commit;

Commit complete.

SQL> select * from test_crypt;

NAME
--------------------------------------------------
D0D368A6C34CEB7B

SQL> select name,f_decrypt_number(name) name2 from test_crypt;

NAME                                                       NAME2
--------------------------------------------------------------------------------
D0D368A6C34CEB7B                            binbin

[/codesyntax]

 



Copyright 2019. All rights reserved.

Posted 2015年12月28日 by ucjmh in category "oracle

Leave a Reply

Your email address will not be published. Required fields are marked *