import java.security.Key;
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
public class BcTest {
// 3DES key = 00 01 02 03 04 05 06 07 01 02 03 04 05 06 07 08 02 03 04 05 06 07 08 09
// data ("01234567") = 30 31 32 33 34 35 36 37
// 3des cipher = 5a b3 fd 7b 2a ca b2 95
// cipher mode = DESede/ECB/NoPadding
public static void main(String[] args) throws Exception {
byte[] key1 = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 };
byte[] key2 = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
byte[] key3 = new byte[] { 2, 3, 4, 5, 6, 7, 8, 9 };
byte[] data = "01234567".getBytes(); // 由于未采用填充模式,因此原文长度必须为 8 的倍数
// 3DES ciphertext = EK3(DK2(EK1(plaintext)))
byte[] crypt = encrypt(decrypt(encrypt(data, key1), key2), key3);
// 3DES plaintext = DK1(EK2(DK3(ciphertext)))
byte[] plain = decrypt(encrypt(decrypt(crypt, key3), key2), key1);
System.out.println(" key: " + ByteUtil.bytes2HexSpace(key1) + " "
+ ByteUtil.bytes2HexSpace(key2) + " "
+ ByteUtil.bytes2HexSpace(key3));
System.out.println(" data: " + ByteUtil.bytes2HexSpace(data));
System.out.println("crypt: " + ByteUtil.bytes2HexSpace(crypt));
System.out.println("plain: " + ByteUtil.bytes2HexSpace(plain));
}
public static byte[] decrypt(byte[] crypt, byte[] key) throws Exception {
Key k = toKey(key);
Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, k);
return cipher.doFinal(crypt);
}
public static byte[] encrypt(byte[] data, byte[] key) throws Exception {
Key k = toKey(key);
Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, k);
return cipher.doFinal(data);
}
public static SecretKey toKey(byte[] key) throws Exception {
KeySpec dks = new DESKeySpec(key);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
return keyFactory.generateSecret(dks);
}
}
class ByteUtil {
private static final char HEX[] = "0123456789abcdef".toCharArray();
public static String bytes2HexSpace(byte bys[]) {
char chs[] = new char[(bys.length * 2 + bys.length) - 1];
int i = 0;
int offset = 0;
for (; i < bys.length; i++) {
if (i > 0)
chs[offset++] = ' ';
chs[offset++] = HEX[bys[i] >> 4 & 15];
chs[offset++] = HEX[bys[i] & 15];
}
return new String(chs);
}
}
上面 DES 的加密模式采用 ECB,填充方式为不进行填充。
实际上 JCE 提供了 3DES 的加密算法,算法名为 DESede,并不需要自己通过 DES 去实现 3DES。只要把上面加密算法中有关 DES 改为 DESede,toKey 方法那个 KeySpec 的引用类由 DESKeySpec 改为 DESedeKeySpec 就可以了。
使用cipher可以很容易的实现3des加密,但是跟其他平台开发的3des加密对接来说,通常会有一些问题。基本的程序如下:
public static byte[] desEncrypt(String message, String key) throws Exception {
Cipher cipher = Cipher.getInstance("DESede");
DESKeySpec desKeySpec = new DESKeySpec(key.getBytes("UTF-8"));
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return cipher.doFinal(message.getBytes("UTF-8"));
』
我们跟其他平台对接发现对同样输入加密以后结果不同,看看jdk的文档,有如下描述:
A transformation is a string that describes the operation (or set of operations) to be performed on the given input, to produce some output.
A transformation is of the form:
- "algorithm/mode/padding" or
- "algorithm"
(in the latter case, provider-specific default values for the mode and padding scheme are used).
根据前面的代码,我们已经选择了正确的算法,那么加密不同的原因应该就是mode和padding了。
he SunJCE provider uses ECB as the default mode, and PKCS5Padding as the default padding scheme for DES, DES-EDE and Blowfish ciphers. This means that in the case of the SunJCE provider,
Cipher c1 = Cipher.getInstance("DES/ECB/PKCS5Padding");
and
Cipher c1 = Cipher.getInstance("DES");
are equivalent statements.
对于其他语言开发的3des,一定要采用相同的mode和padding才能保证通信。