个人博客原文地址:http://www.ltang.me/2015/11/06/java-des-secret-key/
背景
在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); } |
问题
在偶然中,我发现使用两个不同的秘钥00401888
和00401889
,对同样的数据,加密出来的结果是一样的。再一看,发现不仅仅这两个秘钥生成的结果是一样的,比如00401880
和00401881
生成的加密结果也是一样的。
刚开始我以为是自己的代码写得有问题,然后再网上搜了下其他人写的java des代码,发现写法都是一样的,在copy到自己的类中调用,生成的结果也是一样的。我再找到一个DES在线加解密的网站,也会有这种秘钥不同但加密结果一样的问题。
原因
仔细地调试了一下代码,又查看了一下关于DES加密算法的说明,发现:
初始Key值为64位,但DES算法规定,其中第8、16、……64位是奇偶校验位,不参与DES运算。故Key 实际可用位数便只有56位
于是明白原因了。
-
00401888
转为Byte数组为{48, 48, 52, 48, 49, 56, 56, 56}
, 比如其中56
的二进制表示为00111000
,但是参与DES运算的只是前七位,即0011100
; -
00401889
转为Byte数组为{48, 48, 52, 48, 49, 56, 56, 57}
, 其中57
的二进制表示为00111001
,但是参与DES运算的只是前七位,即0011100
,跟上面的56
(即数字8的ASC码)是一样的。所以造成了key不一样,但是加密结果一样的现象,本质原因是参与DES运算的那些位是一样的。
明白了这一点,就比较有趣了。比如我可以构造两个完全不同的秘钥0075309J
和1164218K
,它们对应的每一个字符都不一样,但是因为每一个字符的二进制表示,只有最后一位是不一样的,前七位一样,所以它们对同一段数据加密后的结果是完全一样的。理论上是这样,经过试验结果确实如此。