代码
/**
* 3DES加解密
*/
public class DESedeUtils {
private static final String ALGORITHM_MD5 = "md5";
private static final String ALGORITHM_DESEDE = "DESede";//加密算法,可用 DES,DESede,Blowfish
private static final String CHARSET = "UTF-8";
public static void main(String[] args) throws Exception {
byte[] key = get3DESKeyBytes("123456");
byte[] source = "12345678".getBytes(CHARSET);
byte[] source1 = "0123456789123456".getBytes(CHARSET);
byte[] source2 = "1".getBytes(CHARSET);
test(source, key);
test(source1, key);
test(source2, key);
}
private static void test(byte[] source, byte[] key) throws Exception {
//生成的密文
byte[] cryptograph = encryptOrDecrypt(source, key, Cipher.ENCRYPT_MODE);
//通过Base64编码为ASCII字符后传输
String cryptographStr = Base64.getEncoder().encodeToString(cryptograph);
//收到后先用Base64解码
byte[] targetBase64 = Base64.getDecoder().decode(cryptographStr.getBytes(CHARSET));
// 解密密文
byte[] target = encryptOrDecrypt(targetBase64, key, Cipher.DECRYPT_MODE);
System.out.println("加密前【" + new String(source, CHARSET) + "】\n加密后【" + cryptographStr + "】\n解密后【" + new String(target, CHARSET) + "】");
System.out.println("加密前字节数【" + source.length + "】加密后字节数【" + cryptograph.length + "】解密后字节数【" + target.length + "】\n");
}
/**
* 加密或解密。加密和解密用的同一个算法和密钥
* @param src 要加密或解密的数据
* @param key 密钥
* @param mode 加密或解密模式。值请选择Cipher.DECRYPT_MODE或Cipher.ENCRYPT_MODE
* @return 加密或解密后的数据
*/
public static byte[] encryptOrDecrypt(byte[] src, byte[] key, int mode) throws Exception {
Cipher cipher = Cipher.getInstance(ALGORITHM_DESEDE); //负责加密/解密的Cipher工具类
Key key3DES = new SecretKeySpec(key, ALGORITHM_DESEDE); //3DES密钥
cipher.init(mode, key3DES); //加密模式
return cipher.doFinal(src);//按单部分操作加密或解密数据,或者结束一个多部分操作。返回:包含结果的新缓冲区
}
/**
* 根据字符串生成3DES的密钥字节数组<br>
* 注意java的3des为24位密钥(24*8=192bit),c代码的话只要16位(16*8=128bit)
*/
public static byte[] get3DESKeyBytes(String sKey) throws Exception {
//获得指定摘要算法的 MessageDigest 对象
MessageDigest md = MessageDigest.getInstance(ALGORITHM_MD5);
//使用指定的字节更新摘要(继续多部分加密或解密操作,以处理其他数据部分)
md.update(sKey.getBytes(CHARSET));
//获得密文。注意:长度为16而不是32。一个字节(byte)占8位(bit)
byte[] digestOfPassword = md.digest();
//将16位消息摘要数组中的内容,拷贝到一个长度为【24】的数组中
byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
//再用前8位数据对应补全后8位
for (int j = 0, k = 16; j < 8;) {
keyBytes[k++] = keyBytes[j++];
}
return keyBytes;
}
}
结果
加密前【12345678】
加密后【CS4ct3yhhJUnQXsmJGodZQ==】
解密后【12345678】
加密前字节数【8】加密后字节数【16】解密后字节数【8】
加密前【0123456789123456】
加密后【nZswcCGGA2sDye/3Ad6/BCdBeyYkah1l】
解密后【0123456789123456】
加密前字节数【16】加密后字节数【24】解密后字节数【16】
加密前【1】
加密后【p9itKXB92d0=】
解密后【1】
加密前字节数【1】加密后字节数【8】解密后字节数【1】
解疑:两张生成Key的方式
public class Test {
private static final String ALGORITHM_DESEDE = "DESede";//加密算法,可用 DES,DESede(即3DES),Blowfish
private static final String CHARSET = "UTF-8";
public static void main(String[] args) throws Exception {
//用来加密的字符串
byte[] keyBytes = "012345678901234567890123".getBytes(CHARSET);//java的3DES为24位密钥
//第一种产生Key的方式:根据提供的密钥规范生成一个密钥工厂,通过此密钥工厂根据指定的字节数组构造一个 SecretKey(只能对对称密钥进行操作)
Key key1 = SecretKeyFactory.getInstance(ALGORITHM_DESEDE).generateSecret(new DESedeKeySpec(keyBytes));//相关类都在【javax.crypto.spec】包下
//第二种产生Key的方式:直接根据指定的字节数组构造一个符合指定密钥规范的 SecretKey,而无须通过一个(基于 provider 的)SecretKeyFactory
Key key2 = new SecretKeySpec(keyBytes, ALGORITHM_DESEDE);
System.out.println(Arrays.toString(key1.getEncoded()) + "\n" + Arrays.toString(key2.getEncoded()) + "\n");
byte[] source1 = "12345678".getBytes(CHARSET);
byte[] source2 = "虽然两个Key并不相同,但是却可以用一个加密用另一个解密,谁能告诉我为什么?".getBytes(CHARSET);
test(source1, key1, key2);
test(source2, key2, key1);
}
private static void test(byte[] source, Key key1, Key key2) throws Exception {
//用第一个Key生成密文
byte[] cryptograph = encryptOrDecrypt(source, key1, Cipher.ENCRYPT_MODE);
//通过Base64编码为ASCII字符后传输
String cryptographStr = Base64.getEncoder().encodeToString(cryptograph);
//收到后先用Base64解码
byte[] targetBase64 = Base64.getDecoder().decode(cryptographStr.getBytes(CHARSET));
//用第二个Key 解密密文
byte[] target = encryptOrDecrypt(targetBase64, key2, Cipher.DECRYPT_MODE);
System.out.println("加密前【" + new String(source, CHARSET) + "】\n加密后【" + cryptographStr + "】\n解密后【" + new String(target, CHARSET) + "】\n");
}
/**
* 加密或解密。加密和解密用的同一个算法和密钥
* @param src 要加密或解密的数据
* @param key3DES 密钥
* @param mode 加密或解密模式。值请选择Cipher.DECRYPT_MODE或Cipher.ENCRYPT_MODE
* @return 加密或解密后的数据
*/
public static byte[] encryptOrDecrypt(byte[] src, Key key3DES, int mode) throws Exception {
Cipher cipher = Cipher.getInstance(ALGORITHM_DESEDE); //负责加密/解密的Cipher工具类
cipher.init(mode, key3DES); //加密模式
return cipher.doFinal(src);//按单部分操作加密或解密数据,或者结束一个多部分操作。返回:包含结果的新缓冲区
}
}
结果
[49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 49, 49, 50, 50]
[48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51]
加密前【12345678】
加密后【JHkgWn5or6kghB7oq65OZQ==】
解密后【12345678】
加密前【虽然两个Key并不相同,但是却可以用一个加密用另一个解密,谁能告诉我为什么?】
加密后【GfBN4sqbEb0WQ3tIUE+7L47WNDTUc+ZHphOC8qWbb5EozifxYNHENpEP09a1t9cq+x/s+NCcBqED4WuTgY2JeGGW/DuMJayKZ31SbmvoVHrFRsQpA7dQ51eZ1XOefcX8ElepTc52UFQ2FR/+3D4Gug==】
解密后【虽然两个Key并不相同,但是却可以用一个加密用另一个解密,谁能告诉我为什么?】