发布于 

JAVA DES 秘钥问题

在偶然中,我发现使用两个不同的秘钥0040188800401889,对同样的数据,加密出来的结果是一样的。再一看,发现不仅仅这两个秘钥生成的结果是一样的,比如0040188000401881生成的加密结果也是一样的。

刚开始我以为是自己的代码写得有问题,然后再网上搜了下其他人写的java des代码,发现写法都是一样的,在copy到自己的类中调用,生成的结果也是一样的。我再找到一个DES在线加解密的网站,也会有这种秘钥不同但加密结果一样的问题。

背景

url动态加解密中,我使用的是DES加解密,秘钥使用当前系统时间转换为的小时数:

1
2
3
4
5
6
7
8
9
10
 public static String encrypt(String data) throws Exception {

Date date = new Date();
long hour = date.getTime() / 1000 / 60 / 60;
String key = String.valueOf(hour);
while (key.length() < 8) {
key = "0" + key;
}
return encrypt(data, key);
}

加密代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public final static String encrypt(String data, String key) throws Exception  {
return byte2hex(encrypt(data.getBytes(), key.getBytes()));
}

private static byte[] encrypt(byte[] data, byte[] key) throws Exception {

SecureRandom sr = new SecureRandom();
// 从原始密匙数据创建DESKeySpec对象
DESKeySpec dks = new DESKeySpec(key);
// 创建一个密匙工厂,然后用它把DESKeySpec转换成一个SecretKey对象
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
SecretKey securekey = keyFactory.generateSecret(dks);
// Cipher对象实际完成加密操作
Cipher cipher = Cipher.getInstance(ALGORITHM);
// 用密匙初始化Cipher对象
cipher.init(Cipher.ENCRYPT_MODE, securekey, sr);
// 正式执行加密操作
return cipher.doFinal(data);
}

问题

在偶然中,我发现使用两个不同的秘钥0040188800401889,对同样的数据,加密出来的结果是一样的。再一看,发现不仅仅这两个秘钥生成的结果是一样的,比如0040188000401881生成的加密结果也是一样的。

刚开始我以为是自己的代码写得有问题,然后再网上搜了下其他人写的java des代码,发现写法都是一样的,在copy到自己的类中调用,生成的结果也是一样的。我再找到一个DES在线加解密的网站,也会有这种秘钥不同但加密结果一样的问题。

原因

仔细地调试了一下代码,又查看了一下关于DES加密算法的说明,发现:

初始Key值为64位,但DES算法规定,其中第8、16、……64位是奇偶校验位,不参与DES运算。故Key 实际可用位数便只有56位

于是明白原因了。

  1. 00401888转为Byte数组为{48, 48, 52, 48, 49, 56, 56, 56}, 比如其中56的二进制表示为00111000,但是参与DES运算的只是前七位,即0011100;

  2. 00401889转为Byte数组为{48, 48, 52, 48, 49, 56, 56, 57}, 其中57的二进制表示为00111001,但是参与DES运算的只是前七位,即0011100,跟上面的56(即数字8的ASC码)是一样的。所以造成了key不一样,但是加密结果一样的现象,本质原因是参与DES运算的那些位是一样的。

明白了这一点,就比较有趣了。比如我可以构造两个完全不同的秘钥0075309J1164218K,它们对应的每一个字符都不一样,但是因为每一个字符的二进制表示,只有最后一位是不一样的,前七位一样,所以它们对同一段数据加密后的结果是完全一样的。理论上是这样,经过试验结果确实如此。