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加密对接来说,通常会有一些问题。基本的程序如下:
publicstaticbyte[]desEncrypt(Stringmessage,Stringkey)throwsException{
Ciphercipher=Cipher.getInstance("DESede");
DESKeySpecdesKeySpec=newDESKeySpec(key.getBytes("UTF-8"));
SecretKeyFactorykeyFactory=SecretKeyFactory.getInstance("DESede");
SecretKeysecretKey=keyFactory.generateSecret(desKeySpec);
cipher.init(Cipher.ENCRYPT_MODE,secretKey);
returncipher.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才能保证通信。