java 实现 oracle 的 des3 加密、解密
java 实现 oracle 的 des3 加密 解密 。 dbms_obfuscation_toolkit.des3encrypt、dbms_obfuscation_toolkit.des3decrypt
由于业务需要对接接口,接口提供方需要的校验token是一个利用oracle数据库的 dbms_obfuscation_toolkit.des3encrypt
、 dbms_obfuscation_toolkit.des3decrypt
实现加解密的字符串,开始以为就是通用的 des3
加密,直接实现了一个,可是怎么也跟 oracle
的对不上。
那就先在网上找一下实现吧,倒是找到了一个 des
的对应实现,对应的改为了 des3
,可惜还是对不上,但是关注到了一些细节, des3
的算法是一回事,具体实现却是有好几种方式,比如 ECB
CBC
还有填充方式等,所以需要知道 oracle
到底如何实现的。
查找 oracle
官方文档,只能确认了是 CBC
以及 dbms_obfuscation_toolkit.des3encrypt
的 which
参数未指定时,默认是 two-key
加密, iv
未指定默认为 Null
,好家伙查找相关资料未果。那就继续代码试验一下, java
中的 des3
要求 key
是 24
位,不是就报错,但是接口方提供的只有 17
位, what?!
oracle
中说要求 16
及以上,怎么还不一样,难道...,在 oracle
中试验,结果发现 16
位以上的字符串没有任何效果与 16
位加密的结果一致。然后又搜索了一下相关资料,确认 oracle
把前 8
位拷贝到了后面 8
位,组成了 24
位。
好吧, key
的问题解决了,这次总行了吧,结果尝试还是不一致,但是有点奇怪的是解密的字符串前面的 8
位是乱码,但是后面的 8
位是正确的,这是什么道理?百度不着,手头没办法翻墙,于是就 bing
国际搜索一下,这次找到了原因,有道友发现是 iv
的值引起的, oracle
使用了 iv
对前 8
位做了一些运算,肯定有一个默认的 iv
且不是 new byte[8]
初始值。这道友倒是说自己通过运算得到了 iv
是什么,并且处理成功,可是没贴出来结果...... 咱也试试,想着8位,就试试有规律的,可是白折腾。
没办法,再 bing
一下,多翻了几页,还真找到了(所以早翻墙,多搜索,不知道 chatgpt
是不是能直接有答案)。 iv
是 0123456789abcdef
字符数组的 16
进制。 参考链接: Oracle Triple DES Encryption -> Java Decryption
下面是整理后,具体的实现代码(用到了 commons-codec
包):
import org.apache.commons.codec.binary.Hex;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import static org.apache.commons.codec.CharEncoding.UTF_8;
/**
* 对应 oracle 数据库的 dbms_obfuscation_toolkit.des3encrypt(加密)、dbms_obfuscation_toolkit.des3decrypt(解密)
* dbms_obfuscation_toolkit.des3encrypt 的which假如未指定,则默认为0 也就是 two-keyMode,
* 且采用的是 CBC 方式,需要有 iv 参数,这里oracle在方法内做了处理,虽然方法说明得iv是Null,
* 但实际使用了 0123456789abcdef 的16进制作为了向量,对字符串的前8位做了一定的运算处理(官方文档并未说明,由网友推算出来的)
*/
public class Des3Helper {
private final Cipher cipher = Cipher.getInstance("DESede/CBC/NoPadding");
;
private final IvParameterSpec iv = new IvParameterSpec(Hex.decodeHex("0123456789abcdef".toCharArray()));
private final SecretKeySpec secretKeySpec;
/**
* 构造函数-要求key为16位及以上,实际16位之后的没有作用
* @param key 必须16位及以上,实际16位之后的没有作用
* @throws Exception 处理异常
*/
public Des3Helper(String key) throws Exception {
// 处理为24位,前8位填充到最后8位
byte[] keyBytes = new byte[24];
System.arraycopy(key.getBytes(UTF_8), 0, keyBytes, 0, 16);
System.arraycopy(key.getBytes(UTF_8), 0, keyBytes, 16, 8);
this.secretKeySpec = new SecretKeySpec(keyBytes, "DESede");
}
/**
* 解密-对应 oracle des3解密 dbms_obfuscation_toolkit.des3decrypt
* @param str 待解密的16进制字符串
* @return 加密前的原始字符串
* @throws Exception 处理异常
*/
public String decrypt(final String str) throws Exception {
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, iv);
return new String(cipher.doFinal(Hex.decodeHex(str))).trim();
}
/**
* 加密-对应 oracle des3加密 dbms_obfuscation_toolkit.des3encrypt
* @param str 待加密的字符串
* @return 加密后的16进制字符串
* @throws Exception 处理异常
*/
public String encrypt(String str) throws Exception {
// 需要填充原始字符串为8的倍数
byte[] inBytes = new byte[((str.length() / 8) + 1) * 8];
System.arraycopy(str.getBytes(UTF_8), 0, inBytes, 0, str.length());
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, iv);
return Hex.encodeHexString(cipher.doFinal(inBytes), false);
}
}