本文介绍 Java 语言实现 DES 加密的方法。
目录
- DES 简介
- 代码实现
- ECB
- CBC
- CTR
DES 简介
DES(Data Encryption Standard,数据加密标准)是一种对称加密算法,目前已经不是一种安全的加密方法,因为使用的秘钥过短。
DES 算法的入口参数有三个:
- Key:8 字节共 64 位,是 DES 算法的工作密钥。
- Data:8 字节共 64 位,需要被加解密的数据。
- Mode:DES 工作方式,加密或解密。
五种分组模式:EBC(电子密码本)、CBC(加密分组链接)、CFB(加密反馈模式)、OFB(输出反馈)、CTR。
其中 CTR 模式被广泛用于 ATM 网络安全和 IPSec 应用中,相对于其它模式,CTR 模式具有以下特点:
- 硬件效率:允许同时处理多块明文和密文。
- 软件效率:允许并行计算,可以很好地利用 CPE 流水等并行技术。
- 预处理:算法和加密盒的输出不依赖于明文和密文的输入,因此如果有足够的保证安全的存储器,加密算法将仅仅是一系列异或运算,这将极大地提高吞吐量。
- 随机访问:第
i
块密文的解密不依赖于第i-1
块密文,提供很高的随机访问能力。 - 可证明的安全性:能够证明 CTR 至少和其他模式一样安全。
- 简单性:与其它模式不同,CTR 模式仅要求实现加密算法,但不要求实现解密算法。对于 AES 等加解密本质上不同的算法来说,这种简化是巨大的。
- 无填充,可以高效地作为流式加密使用。
Java 实现 DES 对称加密算法时常采用的是 NoPadding
(不填充)、Zeros
填充(0填充) 和 PKCS5Padding
填充。
代码实现
ECB
package tutorial.java.util;
import javax.crypto.*;
import javax.crypto.spec.DESKeySpec;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
public class DesUtils {
/**
* 算法名称
*/
private static final String ALGORITHM_DES = "DES";
/**
* ECB 模式加密
*
* @param key 密钥
* @param content 加密原文
* @param padding 填充模式
* @return 加密后字符数组
*/
public static byte[] encryptEcb(byte[] key, byte[] content, Padding padding) {
try {
// 生成密钥
Key desKey = keyGenerator(key);
// 实例化一个 Cipher 对象用于完成加密操作
Cipher cipher = Cipher.getInstance(ALGORITHM_DES + "/ECB/" + padding.value);
// 初始化 Cipher 对象,设置为加密模式
cipher.init(Cipher.ENCRYPT_MODE, desKey, new SecureRandom());
return cipher.doFinal(content);
} catch (InvalidKeyException e) {
throw new UnsupportedOperationException("Invalid Key");
} catch (NoSuchAlgorithmException e) {
throw new UnsupportedOperationException("No such algorithm");
} catch (InvalidKeySpecException e) {
throw new UnsupportedOperationException("Invalid key spec");
} catch (NoSuchPaddingException e) {
throw new UnsupportedOperationException("No such padding");
} catch (BadPaddingException e) {
throw new UnsupportedOperationException("Bad padding");
} catch (IllegalBlockSizeException e) {
throw new UnsupportedOperationException("Illegal block size");
}
}
/**
* ECB 模式解密
*
* @param key 密钥
* @param content 密文
* @param padding 填充模式
* @return 原文字符数组
*/
public static byte[] decryptEcb(byte[] key, byte[] content, Padding padding) {
try {
// 生成密钥
Key desKey = keyGenerator(key);
// 实例化一个 Cipher 对象用于完成解密操作
Cipher cipher = Cipher.getInstance(ALGORITHM_DES + "/ECB/" + padding.value);
// 初始化 Cipher 对象,设置为解密模式
cipher.init(Cipher.DECRYPT_MODE, desKey);
return cipher.doFinal(content);
} catch (NoSuchPaddingException e) {
throw new UnsupportedOperationException("No such padding");
} catch (NoSuchAlgorithmException e) {
throw new UnsupportedOperationException("No such algorithm");
} catch (InvalidKeyException e) {
throw new UnsupportedOperationException("Invalid Key");
} catch (InvalidKeySpecException e) {
throw new UnsupportedOperationException("Invalid key spec");
} catch (BadPaddingException e) {
throw new UnsupportedOperationException("Bad padding");
} catch (IllegalBlockSizeException e) {
throw new UnsupportedOperationException("Illegal block size");
}
}
/**
* 生成 DES 密钥对象
*
* @param key 密钥字节数组
* @return 密钥对象
* @throws InvalidKeyException Key 无效
* @throws NoSuchAlgorithmException 无相关算法
* @throws InvalidKeySpecException KeySepc 无效
*/
private static Key keyGenerator(byte[] key)
throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException {
DESKeySpec desKeySpec = new DESKeySpec(key);
// 创建一个密钥工厂,用于转换 DESKeySpec
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(ALGORITHM_DES);
// 生成一个密钥并返回
return secretKeyFactory.generateSecret(desKeySpec);
}
public static enum Padding {
NO_PADDING("NoPadding"),
PKCS5_PADDING("PKCS5Padding");
private String value;
Padding(String value) {
this.value = value;
}
}
}
单元测试
package tutorial.java.util;
import org.junit.Assert;
import org.junit.Test;
import java.nio.charset.StandardCharsets;
public class DesUtilsTest {
private void test(DesUtils.Padding padding, String key, String content) {
byte[] encryptResult = DesUtils.encryptEcb(key.getBytes(StandardCharsets.UTF_8),
content.getBytes(StandardCharsets.UTF_8),
padding);
byte[] decryptResult = DesUtils.decryptEcb(key.getBytes(StandardCharsets.UTF_8),
encryptResult,
padding);
Assert.assertEquals(content, new String(decryptResult, StandardCharsets.UTF_8));
}
@Test
public void testEcbNoPadding() {
// 密钥长度必须是 8 的倍数
String key = "12345678";
// 待加密原文长度必须是 8 的倍数
String content = "12345678";
test(DesUtils.Padding.NO_PADDING, key, content);
}
@Test
public void testEcbPkcs5Padding() {
// 密钥长度必须是 8 的倍数
String key = "12345678";
String content = "123456789";
test(DesUtils.Padding.PKCS5_PADDING, key, content);
}
}
CBC
package tutorial.java.util;
import javax.crypto.*;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
public class DesUtils {
/**
* 算法名称
*/
private static final String ALGORITHM_DES = "DES";
private static final SecureRandom RANDOM = new SecureRandom();
/**
* 注意:
* 1.CBC 模式下加解密 Cipher 初始化必须传入同一个 IvParameterSpec 实例参数
* 2.IvParameterSpec 构造函数中参数字节数组长度必须是 8 位
*/
private static final IvParameterSpec IV_PARAMETER_SPEC = new IvParameterSpec(RANDOM.generateSeed(8));
/**
* CBC 模式加密
*
* @param key 密钥
* @param content 加密原文
* @param padding 填充模式
* @return 加密后字符数组
*/
public static byte[] encryptCbc(byte[] key, byte[] content, Padding padding) {
try {
// 生成密钥
Key desKey = keyGenerator(key);
// 实例化一个 Cipher 对象用于完成加密操作
Cipher cipher = Cipher.getInstance(ALGORITHM_DES + "/CBC/" + padding.value);
// 初始化 Cipher 对象,设置为加密模式
cipher.init(Cipher.ENCRYPT_MODE, desKey, IV_PARAMETER_SPEC);
return cipher.doFinal(content);
} catch (InvalidKeyException e) {
throw new UnsupportedOperationException("Invalid Key");
} catch (NoSuchAlgorithmException e) {
throw new UnsupportedOperationException("No such algorithm");
} catch (InvalidKeySpecException e) {
throw new UnsupportedOperationException("Invalid key spec");
} catch (NoSuchPaddingException e) {
throw new UnsupportedOperationException("No such padding");
} catch (BadPaddingException e) {
throw new UnsupportedOperationException("Bad padding");
} catch (IllegalBlockSizeException e) {
throw new UnsupportedOperationException("Illegal block size");
} catch (InvalidAlgorithmParameterException e) {
throw new UnsupportedOperationException("Illegal algorithm parameter");
}
}
/**
* CBC 模式解密
*
* @param key 密钥
* @param content 密文
* @param padding 填充模式
* @return 原文字符数组
*/
public static byte[] decryptCbc(byte[] key, byte[] content, Padding padding) {
try {
// 生成密钥
Key desKey = keyGenerator(key);
// 实例化一个 Cipher 对象用于完成解密操作
Cipher cipher = Cipher.getInstance(ALGORITHM_DES + "/CBC/" + padding.value);
// 初始化 Cipher 对象,设置为解密模式
cipher.init(Cipher.DECRYPT_MODE, desKey, IV_PARAMETER_SPEC);
return cipher.doFinal(content);
} catch (NoSuchPaddingException e) {
throw new UnsupportedOperationException("No such padding");
} catch (NoSuchAlgorithmException e) {
throw new UnsupportedOperationException("No such algorithm");
} catch (InvalidKeyException e) {
throw new UnsupportedOperationException("Invalid Key");
} catch (InvalidKeySpecException e) {
throw new UnsupportedOperationException("Invalid key spec");
} catch (BadPaddingException e) {
throw new UnsupportedOperationException("Bad padding");
} catch (IllegalBlockSizeException e) {
throw new UnsupportedOperationException("Illegal block size");
} catch (InvalidAlgorithmParameterException e) {
throw new UnsupportedOperationException("Illegal algorithm parameter");
}
}
/**
* 生成 DES 密钥对象
*
* @param key 密钥字节数组
* @return 密钥对象
* @throws InvalidKeyException Key 无效
* @throws NoSuchAlgorithmException 无相关算法
* @throws InvalidKeySpecException KeySepc 无效
*/
private static Key keyGenerator(byte[] key)
throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException {
DESKeySpec desKeySpec = new DESKeySpec(key);
// 创建一个密钥工厂,用于转换 DESKeySpec
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(ALGORITHM_DES);
// 生成一个密钥并返回
return secretKeyFactory.generateSecret(desKeySpec);
}
public static enum Padding {
NO_PADDING("NoPadding"),
PKCS5_PADDING("PKCS5Padding");
private String value;
Padding(String value) {
this.value = value;
}
}
}
单元测试
package tutorial.java.util;
import org.junit.Assert;
import org.junit.Test;
import java.nio.charset.StandardCharsets;
public class DesUtilsTest {
private void test(DesUtils.Padding padding, String key, String content) {
byte[] encryptResult = DesUtils.encryptCbc(key.getBytes(StandardCharsets.UTF_8),
content.getBytes(StandardCharsets.UTF_8),
padding);
byte[] decryptResult = DesUtils.decryptCbc(key.getBytes(StandardCharsets.UTF_8),
encryptResult,
padding);
Assert.assertEquals(content, new String(decryptResult, StandardCharsets.UTF_8));
}
@Test
public void testCbcNoPadding() {
// 密钥长度必须是 8 的倍数
String key = "12345678";
// 待加密原文长度必须是 8 的倍数
String content = "12345678";
test(DesUtils.Padding.NO_PADDING, key, content);
}
@Test
public void testCbcPkcs5Padding() {
// 密钥长度必须是 8 的倍数
String key = "12345678";
String content = "123456789";
test(DesUtils.Padding.PKCS5_PADDING, key, content);
}
}
CTR
package tutorial.java.util;
import javax.crypto.*;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
public class DesUtils {
/**
* 算法名称
*/
private static final String ALGORITHM_DES = "DES";
private static final SecureRandom RANDOM = new SecureRandom();
/**
* 注意:
* 1.CTR 模式下加解密 Cipher 初始化必须传入同一个 IvParameterSpec 实例参数
* 2.IvParameterSpec 构造函数中参数字节数组长度必须是 8 位
*/
private static final IvParameterSpec IV_PARAMETER_SPEC = new IvParameterSpec(RANDOM.generateSeed(8));
/**
* CTR 模式加密
*
* @param key 密钥
* @param content 加密原文
* @param padding 填充模式
* @return 加密后字符数组
*/
public static byte[] encryptCtr(byte[] key, byte[] content, Padding padding) {
try {
// 生成密钥
Key desKey = keyGenerator(key);
// 实例化一个 Cipher 对象用于完成加密操作
Cipher cipher = Cipher.getInstance(ALGORITHM_DES + "/CTR/" + padding.value);
// 初始化 Cipher 对象,设置为加密模式
cipher.init(Cipher.ENCRYPT_MODE, desKey, IV_PARAMETER_SPEC);
return cipher.doFinal(content);
} catch (InvalidKeyException e) {
throw new UnsupportedOperationException("Invalid Key");
} catch (NoSuchAlgorithmException e) {
throw new UnsupportedOperationException("No such algorithm");
} catch (InvalidKeySpecException e) {
throw new UnsupportedOperationException("Invalid key spec");
} catch (NoSuchPaddingException e) {
throw new UnsupportedOperationException("No such padding");
} catch (BadPaddingException e) {
throw new UnsupportedOperationException("Bad padding");
} catch (IllegalBlockSizeException e) {
throw new UnsupportedOperationException("Illegal block size");
} catch (InvalidAlgorithmParameterException e) {
throw new UnsupportedOperationException("Illegal algorithm parameter");
}
}
/**
* CTR 模式解密
*
* @param key 密钥
* @param content 密文
* @param padding 填充模式
* @return 原文字符数组
*/
public static byte[] decryptCtr(byte[] key, byte[] content, Padding padding) {
try {
// 生成密钥
Key desKey = keyGenerator(key);
// 实例化一个 Cipher 对象用于完成解密操作
Cipher cipher = Cipher.getInstance(ALGORITHM_DES + "/CTR/" + padding.value);
// 初始化 Cipher 对象,设置为解密模式
cipher.init(Cipher.DECRYPT_MODE, desKey, IV_PARAMETER_SPEC);
return cipher.doFinal(content);
} catch (NoSuchPaddingException e) {
throw new UnsupportedOperationException("No such padding");
} catch (NoSuchAlgorithmException e) {
throw new UnsupportedOperationException("No such algorithm");
} catch (InvalidKeyException e) {
throw new UnsupportedOperationException("Invalid Key");
} catch (InvalidKeySpecException e) {
throw new UnsupportedOperationException("Invalid key spec");
} catch (BadPaddingException e) {
throw new UnsupportedOperationException("Bad padding");
} catch (IllegalBlockSizeException e) {
throw new UnsupportedOperationException("Illegal block size");
} catch (InvalidAlgorithmParameterException e) {
throw new UnsupportedOperationException("Illegal algorithm parameter");
}
}
/**
* 生成 DES 密钥对象
*
* @param key 密钥字节数组
* @return 密钥对象
* @throws InvalidKeyException Key 无效
* @throws NoSuchAlgorithmException 无相关算法
* @throws InvalidKeySpecException KeySepc 无效
*/
private static Key keyGenerator(byte[] key)
throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException {
DESKeySpec desKeySpec = new DESKeySpec(key);
// 创建一个密钥工厂,用于转换 DESKeySpec
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(ALGORITHM_DES);
// 生成一个密钥并返回
return secretKeyFactory.generateSecret(desKeySpec);
}
public static enum Padding {
NO_PADDING("NoPadding"),
PKCS5_PADDING("PKCS5Padding");
private String value;
Padding(String value) {
this.value = value;
}
}
}
单元测试
package tutorial.java.util;
import org.junit.Assert;
import org.junit.Test;
import java.nio.charset.StandardCharsets;
public class DesUtilsTest {
private void test(DesUtils.Padding padding, String key, String content) {
byte[] encryptResult = DesUtils.encryptCtr(key.getBytes(StandardCharsets.UTF_8),
content.getBytes(StandardCharsets.UTF_8),
padding);
byte[] decryptResult = DesUtils.decryptCtr(key.getBytes(StandardCharsets.UTF_8),
encryptResult,
padding);
Assert.assertEquals(content, new String(decryptResult, StandardCharsets.UTF_8));
}
@Test
public void testCtrNoPadding() {
// 密钥长度必须是 8 的倍数
String key = "12345678";
// 待加密原文长度必须是 8 的倍数
String content = "12345678";
test(DesUtils.Padding.NO_PADDING, key, content);
}
@Test
public void testCtrPkcs5Padding() {
// 密钥长度必须是 8 的倍数
String key = "12345678";
String content = "123456789";
test(DesUtils.Padding.PKCS5_PADDING, key, content);
}
}
网友评论