因为工作原因,需要将Oracle中的加密数据读取到Java中进行解密,因为程序并不是通过JDBC直接访问数据库,而是Oracle导出文件的方式(为了避免关键信息在文件传输中泄漏因而加密),所以不能使用Oracle自身的解密方法解密,因而才有了下文。
Oracle加密
加密函数
加密函数代码如下:
create or replace function encrypt_des(p_text varchar2, p_key varchar2) return varchar2
is
v_text varchar2(4000);
v_enc varchar2(4000);
raw_input RAW(128) ;
key_input RAW(128) ;
decrypted_raw RAW(2048);
begin
v_text := rpad( p_text, (trunc(length(p_text)/8)+1)*8, chr(0));
raw_input := UTL_RAW.CAST_TO_RAW(v_text);
key_input := UTL_RAW.CAST_TO_RAW(p_key);
dbms_obfuscation_toolkit.DESEncrypt(input => raw_input,key => key_input,encrypted_data =>decrypted_raw);
select utl_raw.cast_to_varchar2(utl_encode.base64_encode(decrypted_raw)) into v_enc from dual;
return v_enc;
end;
/
简单说明一下加密函数:要加密的原文必须是8的整数倍字节,如果不足,会在右侧填充ASCII为0的字符,而不是字符0;如果原文字节整好是8的整数倍会额外添加8个字节的0;代码是v_text := rpad( p_text, (trunc(length(p_text)/8)+1)*8, chr(0));这一句。然后调用
调用函数进行加密DESEncrypt加密,加密完成后,使用base64_encode对密文进行base64加密,并输出结果。
函数调用
select encrypt_des('123456', 'encryptk') from dual;
select encrypt_des('12345678', 'encryptk') from dual;
select encrypt_des('1234567812345678', 'encryptk') from dual;
'123456'是要加密的密文,'encryptk'是用来加密的密钥,必须8字节,如果超过8字节不会报错,但是实际计算中会被截断。
结果如图:
encrypt_des('123456', 'encryptk') => DCxkHWiXj68=
encrypt_des('12345678', 'encryptk') => RVo37OSWJUcfYVa4klQ0zg==
encrypt_des('1234567812345678', 'encryptk') => RVo37OSWJUfF3nxSbhl12nnrknts79U6
加密分析
密文对比如下:
DCxkHWiXj68=
RVo37OSWJUcfYVa4klQ0zg==
RVo37OSWJUfF3nxSbhl12nnrknts79U6
比较明显的是,对于12345678的加密密文并base64编码后是RVo37OSWJU,原文第二字节同样也是12345678,但是base64编码后在JUfF3nxSbhl12nnrknts79U6中,很明显与第一字节的密文相差甚远,所以可以肯定的是Oracle中DES加密,并不是采用了ECB模式,极有可能是CBC模式。
DES的加密的几种模式可以参考这里:https://www.cnblogs.com/yanzi-meng/p/9640578.html
比较ECB和CBC两种模式,可以发现,ECB对相同原文的加密密文是相同的,和字节位置无关,CBC对相同原文的加密密文是不同的,和字节位置有关。那么对于第一组字节(前8字节,即64bit)来说,CBC是有可能和ECB相同的,那么什么时候相同呢?
由图可以看出,DES加密算法的CBC模式和ECB模式主要的区别在于,CBC算法使用了加密向量IV对原文进行异或,而且IV是变化的,第组字节的加密结果是第二组字节的IV。这就是CBC加密相同字节但是密文不同的原因。
对于第一组字节来说,如果IV向量是ASCII码为0是8字节数据的话,那么ECB的加密结果和CBC是一样的。
综上分析:Oracle中使用的DES加密算法是CBC模式,使用NoPadding方式,原文的填充是其他代码完成的,且初时向量位8字节ASCII码为0的数据。
Java实现相同DES加密和解密
JAVA实现相同DES加密算法
直接附上代码:
try {
DESKeySpec desKey = new DESKeySpec("encryptk".getBytes());
SecretKey securekey = SecretKeyFactory.getInstance("DES").generateSecret(desKey);
Cipher cipher = Cipher.getInstance("DES/CBC/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, securekey, new IvParameterSpec(new byte[8]));
Base64.Encoder encoder = Base64.getEncoder();
String inStr = "1234567812345678";
byte[] inBytes = new byte[((int) (inStr.length() / 8) + 1) * 8];
for (int i = 0; i < inStr.length(); i++) {
inBytes[i] = inStr.getBytes()[i];
}
byte[] enBytes = cipher.doFinal(inBytes);
byte[] base64Bytes = encoder.encode(enBytes);
String hexStr = DatatypeConverter.printHexBinary(enBytes);
System.out.println(inStr + " encrypted (hex) :" + hexStr);
System.out.println(inStr + " encrypted (base64):" + new String(base64Bytes));
} catch (Exception e) {
e.printStackTrace();
}
执行结果:
比较和Oracle加密结果相同。
Java DES 解密
代码如下:
public static void main(String[] args) {
System.out.println(DesDecrypt("RVo37OSWJUfF3nxSbhl12nnrknts79U6"));
}
public static String DesDecrypt(String encryptStr)
{
try {
DESKeySpec desKey = new DESKeySpec("encryptk".getBytes());
SecretKey securekey = SecretKeyFactory.getInstance("DES").generateSecret(desKey);
Cipher cipher = Cipher.getInstance("DES/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, securekey, new IvParameterSpec(new byte[8]));
byte[]decryptBytes = cipher.doFinal(Base64.getDecoder().decode(encryptStr));
return new String(decryptBytes);
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
执行结果: