Oracle数据库中的数据加密 / Oracle数据库DES解密
有个需求,需要对某些敏感信息字段进行脱敏,要求在数据库中不能显示明文。如果目的侧重保护数据而非脱敏的话,可以考虑用表空间透明数据加密(TDE),不过现在是需要对字段内容脱敏,则读取和存储都要是加密后的密文数据。

增量数据好说,后台代码实现即可。但存量数据如果也要后台与数据库交互进行加密的话,且不说IO的消耗,就先查再改,无论单条还是分批,在数十、百万的数据量下估计需要很长时间。那就要考虑在数据库里用PL/SQL做存量数据的加密操作。

Oracle中的加密函数
Oracle的PL/SQL包中有提供相关的函数。

PL/SQL包类官方文档(10.2版本):Database PL/SQL Packages and Types Reference - Contents (oracle.com)

版本在10g之前的话,只能使用DBMS_OBFUSCATION_TOOLKIT这个包来加密,这个包只支持DES和3DES算法,且只有CBC模式,还要手动补位;而在10g以及更高的版本中,则能使用DBMS_CRYPTO包,这个包的加密算法更加丰富,DES、3DES、 AES、RC4、3DES_2KEY等算法和CBC、CFB、ECB、OFB模式,也提供补位,使用起来更简单。

DBMS_CRYPTO和DBMS_OBFUSCATION_TOOLKIT的特性对比

Package Feature    DBMS_CRYPTO    DBMS_OBFUSCATION_TOOLKIT
Cryptographic algorithms    DES, 3DES, AES, RC4, 3DES_2KEY    DES, 3DES
Padding forms    PKCS5, zeroes    none supported
Block cipher chaining modes    CBC, CFB, ECB, OFB    CBC
Cryptographic hash algorithms    MD5, SHA-1, MD4    MD5
Keyed hash (MAC) algorithms    HMAC_MD5, HMAC_SH1    none supported
Cryptographic pseudo-random number generator    RAW, NUMBER, BINARY_INTEGER    RAW, VARCHAR2
Database types    RAW, CLOB, BLOB    RAW, VARCHAR2
DBMS_OBFUSCATION_TOOLKIT
使用该包的加密函数需要手动实现补位,同时也只有CBC模式。以使用DES加密为例,自定义一个函数实现输入明文字符串,输出DES/CBC/PKCS5加密并Base64编码后的密文字符串。

根据文档提供的函数定义,选用第一种。

DBMS_OBFUSCATION_TOOLKIT.DESEncrypt(
   input         IN  RAW,
   key           IN  RAW)
  RETURN RAW;

DBMS_OBFUSCATION_TOOLKIT.DESEncrypt(
   input_string  IN  VARCHAR2,
   key_string    IN  VARCHAR2)
  RETURN VARCHAR2;
1
2
3
4
5
6
7
8
9
因为加密的数据含中文,一个汉字占两个字节,而数字英文占一个字节,VARCHAR2类型直接进行长度判断补位不对,需要转为RAW类型按字节长度判断,所以直接以RAW类型传参。

Oracle中可以使用UTL_I18N.STRING_TO_RAW或UTL_RAW.CAST_TO_RAW来将VARCHAR2类型转为RAW类型。

将明文字符串转成二进制数据类型RAW,然后判断其长度进行PKCS5方式的补位(该补位方法是缺几位就补几位的“几”以满足八的倍数结果,如果一开始已满足,也要加八位的八),密钥也需要转换为RAW格式。DES密钥为64位,即需要8字节的输入,小于8字节会报错,大于的部分会被忽略。

密钥这里我在函数中定义了(应该提出来作为入参,提高安全性)。因为BASE64_ENCODE进行编码的时候,输入的字节数多的话会自动添加换行符,所以做了一个去除处理。

create or replace function encrypt_des(input_str varchar2) return varchar2
    is
    enc_str    varchar2(256);
    pad_length NUMBER(1);
    pad_byte   RAW(1);
    raw_input  RAW(512);
    raw_key    RAW(16);
    enc_raw    RAW(1024);
begin
    if input_str is null
    then
        return input_str;
    else
        raw_input := UTL_RAW.CAST_TO_RAW(input_str);
        -- padding: pad n with n lacking n length
        pad_length := (8 - MOD(UTL_RAW.LENGTH(raw_input), 8));
        pad_byte := TO_SINGLE_BYTE(pad_length);
        while pad_length > 0
            loop
                raw_input := UTL_RAW.CONCAT(raw_input, pad_byte);
                pad_length := pad_length - 1;
            end loop;
        -- key setting
        raw_key := UTL_RAW.CAST_TO_RAW('keytests');
        enc_raw := DBMS_OBFUSCATION_TOOLKIT.DESENCRYPT(input => raw_input, key => raw_key);
        -- remove line break
        enc_str := UTL_RAW.CAST_TO_VARCHAR2(REPLACE(UTL_ENCODE.BASE64_ENCODE(enc_raw), '0D0A', ''));
        return enc_str;
    end if;
end;
/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
DBMS_CRYPTO
使用此包需要向调用的用户授予权限。

以SYSDBA用户登录后:

$ sqlplus / as sysdba
1
授予权限:

GRANT EXECUTE ON DBMS_CRYPTO TO USER_NAME;
1
加密函数语法:

DBMS_CRYPTO.ENCRYPT(
   src IN RAW,
   typ IN PLS_INTEGER,
   key IN RAW,
   iv  IN RAW          DEFAULT NULL)
 RETURN RAW;
1
2
3
4
5
6
使用该包的加密函数,就不需要手动补位了,只要选择好想实现加密算法、加密模式和补位的模式即可。因此自定的函数式为输入的字符串做RAW类型的转换并加密和base64编码的实现。

create or replace function crypto_des(input_str varchar2) return varchar2
    is
    enc_str  varchar2(256);
    raw_key  RAW(16);
    enc_raw  RAW(1024);
    enc_type PLS_INTEGER := -- total encryption type
            DBMS_CRYPTO.ENCRYPT_DES
            + DBMS_CRYPTO.CHAIN_CBC
            + DBMS_CRYPTO.PAD_PKCS5;
begin
    if input_str is null
    then
        return input_str;
    else
        -- key setting
        raw_key := UTL_RAW.CAST_TO_RAW('keytests');
        enc_raw := DBMS_CRYPTO.ENCRYPT(UTL_RAW.CAST_TO_RAW(input_str), enc_type, raw_key);
        -- remove line break
        enc_str := UTL_RAW.CAST_TO_VARCHAR2(REPLACE(UTL_ENCODE.BASE64_ENCODE(enc_raw), '0D0A', ''));
        return enc_str;
    end if;
end;
/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
也可以直接一条SQL实现:

select UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(DBMS_CRYPTO.ENCRYPT(UTL_RAW.CAST_TO_RAW('测试一下123abc'), 4353, UTL_RAW.CAST_TO_RAW('keytests')))) as test from dual;
1
4353是DES/CBC/PKCS5加密方式的数字代码。因为PL/SQL中的常量不能直接在SQL中使用,而需要在PL/SQL块中使用,所以把4353换成DBMS_CRYPTO.DES_CBC_PKCS5或是DBMS_CRYPTO.ENCRYPT_DES+ DBMS_CRYPTO.CHAIN_CBC+ DBMS_CRYPTO.PAD_PKCS5的话会报错。

Java中相应的加解密
RFC 822规定中Base64编码后每76个字符需要加一个换行符。一些旧包编码后会带换行符,如sun.mis(效率也不太好)。

JDK1.8可以直接用自带的java.util.Base64包,不会有换行符。

这里示例Base64编码包是commons-codec的,该包编码后不会有换行符。

public static final String KEY = "keytests";

public static String encrypt(String inputStr) {
    if (null != inputStr && !"".equals(inputStr)) {
        try {
            DESKeySpec desKey = new DESKeySpec(KEY.getBytes());
            SecretKey secretKey = SecretKeyFactory.getInstance("DES").generateSecret(desKey);
            Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(new byte[8]));
            byte[] encryptedBytes = cipher.doFinal(inputStr.getBytes());
            return Base64.encodeBase64String(encryptedBytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    return inputStr;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static String decrypt(String encryptedStr){
    if (null != encryptedStr && !"".equals(encryptedStr)) {
        try {
            // encryptStr = encryptStr.replace("\r\n","");
            DESKeySpec desKey = new DESKeySpec(KEY.getBytes());
            SecretKey secretKey = SecretKeyFactory.getInstance("DES").generateSecret(desKey);
            Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(new byte[8]));
            byte[] decryptBytes = cipher.doFinal(Base64.decodeBase64(encryptedStr));
            return new String(decryptBytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    return encryptedStr;
}
————————————————
版权声明:本文为CSDN博主「Tracts」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Tracts/article/details/126775376

其它参考:

oracle数据库字段加密解密 - it610.com

更多推荐