目录
- 对称加密
- 1 定义
- 2 特点
- 3 使用场景
- 4 常用的对称加密算法
- 5 JDK支持的对称加密算法
- 6 Bouncy Castle 支持的对称加密算法
- 7 算法调用示例
对称加密
1 定义
采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。
2 特点
- 加密和解密使用同样的密钥
- 计算速度快,适用于对大量数据加密处理
- 安全性取决于算法,也取决于密钥的管理,一旦密钥泄漏,数据则暴露无遗
3 使用场景
基于上述的特点,在一些需要高效实时传输的加密通讯场景中,比如使用VPN或者代理进行通讯时,可以使用对称加密。另外在同一个系统内部不同模块,比如前后端,从前端输入的敏感信息,可以使用对称加密算法进行加密后将密文传到后端,避免传输过程中明文被截获,因为同系统内部之间密钥管理相对容易,而对于共享密钥有泄漏风险的其他任何场景,则不适合使用对称加密算法进行加密。
4 常用的对称加密算法
- DES(Data Encryption Standard)
数据加密标准,速度较快,适用于加密大量数据 - 3DES(Triple DES)
基于DES,对一块数据用三个不同的密钥进行三次加密,强度更高 - AES(Advanced Encryption Standard)
高级加密标准,速度快,安全级别高,支持128、192、256、512位密钥的加密 - Blowfish
速度快且安全,而且没有专利和商业限制。
5 JDK支持的对称加密算法
JDK8原生算法列表,可参第一篇博文:
6 Bouncy Castle 支持的对称加密算法
Bouncy Castle原生算法列表,可参第一篇博文:
7 算法调用示例
下面的代码将几种常用的对称加密算法用枚枚举类进行了封装,调用encrypt()和decrypt()方法可以实现加密和解。encrypt()和decrypt()的几个重载方法分别支持了不同秘钥生成方式(秘钥字符串,随机种子生成秘钥),是否设置算法参数(AlgorithmParameterSpec)。
程序采用函数式编程的思想,将整个加密解密过程抽象为一个流程,每一步抽象为一个Lambda表达式,可以根据算法的不同传入不同的Lambda表达式。每添加一种新的算法,只需要传入不同的Lambda表达式即可。
各个类的介绍如下:
SymmetricEncryptionAlgorithmTest :测试加密和解密
SymmetricEncryptionAlgorithm:算法枚举类,用来构建不同算法的加解密流程
EncryptorDefinition:加解密流程的定义,描述了加解密流程需要的所有Lambda表达式
EncryptorBuilder:加解密流程构造器,用于构造一个加解密流程
Encryptor:加解密流程抽象,按顺序调用一个加解密流程的所有步骤,并输出结果
AlgorithmSpec:算法规格,用来封装一个算法所需要的定义信息
AlgorithmParam:算法参数,用来封装一个算法所需要的所有参数
Transformation:算法转换式的封装
AlgorithmParameterSpecInfo:算法规定参数的封装
NumberGenerationAlgorithm:数字生成算法枚举,此算法用于随机数生成
package com.qupeng.crypto.algorithm.fp;
import com.qupeng.crypto.algorithm.oop.NumberGenerationAlgorithm;
import org.junit.Assert;
import org.junit.Test;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
public class SymmetricEncryptionAlgorithmTest {
@Test
public void encryptAES() throws InvalidKeyException, BadPaddingException, IllegalBlockSizeException, IOException, NoSuchMethodException, IllegalAccessException, InvalidAlgorithmParameterException, InstantiationException, InvocationTargetException, NoSuchAlgorithmException, NoSuchPaddingException {
String cipherText = SymmetricEncryptionAlgorithm.AES_CBC_NO_PADDING_128.encrypt("a", "1234567890123456", "1234567890123456");
Assert.assertEquals("a", SymmetricEncryptionAlgorithm.AES_CBC_NO_PADDING_128.decrypt(cipherText, "1234567890123456", "1234567890123456"));
cipherText = SymmetricEncryptionAlgorithm.AES_CBC_NO_PADDING_128.encrypt("a", "12345", NumberGenerationAlgorithm.SHA1_PRNG, "1234567890123456");
Assert.assertEquals("a", SymmetricEncryptionAlgorithm.AES_CBC_NO_PADDING_128.decrypt(cipherText, "12345", NumberGenerationAlgorithm.SHA1_PRNG, "1234567890123456"));
cipherText = SymmetricEncryptionAlgorithm.AES_CBC_PKCS5_PADDING_128.encrypt("a", "1234567890123456", "1234567890123456");
Assert.assertEquals("a", SymmetricEncryptionAlgorithm.AES_CBC_PKCS5_PADDING_128.decrypt(cipherText, "1234567890123456", "1234567890123456"));
cipherText = SymmetricEncryptionAlgorithm.AES_ECB_NO_PADDING_128.encrypt("a", "1234567890123456");
Assert.assertEquals("a", SymmetricEncryptionAlgorithm.AES_ECB_NO_PADDING_128.decrypt(cipherText, "1234567890123456"));
cipherText = SymmetricEncryptionAlgorithm.AES_ECB_PKCS5_PADDING_128.encrypt("a", "1234567890123456");
Assert.assertEquals("a", SymmetricEncryptionAlgorithm.AES_ECB_PKCS5_PADDING_128.decrypt(cipherText, "1234567890123456"));
// AES 192 bits secret
cipherText = SymmetricEncryptionAlgorithm.AES_CBC_PKCS5_PADDING_192.encrypt("a", "123456789012345678901234", "1234567890123456");
Assert.assertEquals("a", SymmetricEncryptionAlgorithm.AES_CBC_PKCS5_PADDING_192.decrypt(cipherText, "123456789012345678901234", "1234567890123456"));
// AES 256 bits secret
cipherText = SymmetricEncryptionAlgorithm.AES_CBC_PKCS5_PADDING_256.encrypt("a", "12345678901234567890123456789012", "1234567890123456");
Assert.assertEquals("a", SymmetricEncryptionAlgorithm.AES_CBC_PKCS5_PADDING_256.decrypt(cipherText, "12345678901234567890123456789012", "1234567890123456"));
}
@Test
public void encryptDES() throws IllegalAccessException, NoSuchAlgorithmException, InstantiationException, NoSuchMethodException, InvalidKeyException, InvalidAlgorithmParameterException, NoSuchPaddingException, BadPaddingException, InvocationTargetException, IllegalBlockSizeException, IOException {
// DES 56 bits secret
String cipherText = SymmetricEncryptionAlgorithm.DES_CBC_NO_PADDING_56.encrypt("a", "12345678", "12345678");
Assert.assertEquals("a", SymmetricEncryptionAlgorithm.DES_CBC_NO_PADDING_56.decrypt(cipherText, "12345678", "12345678"));
cipherText = SymmetricEncryptionAlgorithm.DES_CBC_PKCS5_PADDING_56.encrypt("a", "12345678", "12345678");
Assert.assertEquals("a", SymmetricEncryptionAlgorithm.DES_CBC_PKCS5_PADDING_56.decrypt(cipherText, "12345678", "12345678"));
cipherText = SymmetricEncryptionAlgorithm.DES_ECB_NO_PADDING_56.encrypt("a", "12345678");
Assert.assertEquals("a", SymmetricEncryptionAlgorithm.DES_ECB_NO_PADDING_56.decrypt(cipherText, "12345678"));
cipherText = SymmetricEncryptionAlgorithm.DES_ECB_PKCS5_PADDING_56.encrypt("a", "12345678");
Assert.assertEquals("a", SymmetricEncryptionAlgorithm.DES_ECB_PKCS5_PADDING_56.decrypt(cipherText, "12345678"));
}
@Test
public void encrypt3DES() throws IllegalAccessException, NoSuchAlgorithmException, InstantiationException, NoSuchMethodException, InvalidKeyException, InvalidAlgorithmParameterException, NoSuchPaddingException, BadPaddingException, InvocationTargetException, IllegalBlockSizeException, IOException {
// 3 DES 168 bits secret
String cipherText = SymmetricEncryptionAlgorithm.DESEDE_CBC_NO_PADDING_168.encrypt("a", "123456789012345678901234", "12345678");
Assert.assertEquals("a", SymmetricEncryptionAlgorithm.DESEDE_CBC_NO_PADDING_168.decrypt(cipherText, "123456789012345678901234", "12345678"));
cipherText = SymmetricEncryptionAlgorithm.DESEDE_CBC_PKCS5_PADDING_168.encrypt("a", "123456789012345678901234", "12345678");
Assert.assertEquals("a", SymmetricEncryptionAlgorithm.DESEDE_CBC_NO_PADDING_168.decrypt(cipherText, "123456789012345678901234", "12345678"));
cipherText = SymmetricEncryptionAlgorithm.DESEDE_ECB_NO_PADDING_168.encrypt("a", "123456789012345678901234");
Assert.assertEquals("a", SymmetricEncryptionAlgorithm.DESEDE_ECB_NO_PADDING_168.decrypt(cipherText, "123456789012345678901234"));
cipherText = SymmetricEncryptionAlgorithm.DESEDE_ECB_PKCS5_PADDING_168.encrypt("a", "123456789012345678901234");
Assert.assertEquals("a", SymmetricEncryptionAlgorithm.DESEDE_ECB_PKCS5_PADDING_168.decrypt(cipherText, "123456789012345678901234"));
}
@Test
public void encryptBlowfish() throws BadPaddingException, InvocationTargetException, InvalidAlgorithmParameterException, IllegalBlockSizeException, InstantiationException, NoSuchMethodException, IllegalAccessException, InvalidKeyException, IOException {
// Blowfish
String cipherText = SymmetricEncryptionAlgorithm.BLOWFISH_CBC_NO_PADDING_128.encrypt("a", "1234567890123456", "12345678");
Assert.assertEquals("a", SymmetricEncryptionAlgorithm.BLOWFISH_CBC_NO_PADDING_128.decrypt(cipherText, "1234567890123456", "12345678"));
}
}
package com.qupeng.crypto.algorithm.fp;
import com.qupeng.crypto.algorithm.oop.NumberGenerationAlgorithm;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.security.*;
import java.security.spec.AlgorithmParameterSpec;
public enum SymmetricEncryptionAlgorithm {
AES_CBC_NO_PADDING_128("AES", "CBC", "NoPadding", 128, 16, IvParameterSpec.class, 128),
AES_CBC_NO_PADDING_192("AES", "CBC", "NoPadding", 192, 24, IvParameterSpec.class, 128),
AES_CBC_NO_PADDING_56("AES", "CBC", "NoPadding", 256, 32, IvParameterSpec.class, 128),
AES_CBC_PKCS5_PADDING_128("AES", "CBC", "PKCS5Padding", 128, 16, IvParameterSpec.class, 128),
AES_CBC_PKCS5_PADDING_192("AES", "CBC", "PKCS5Padding", 192, 24, IvParameterSpec.class, 128),
AES_CBC_PKCS5_PADDING_256("AES", "CBC", "PKCS5Padding", 256, 32, IvParameterSpec.class, 128),
AES_ECB_NO_PADDING_128("AES", "ECB", "NoPadding", 128, 16, null, -1),
AES_ECB_NO_PADDING_192("AES", "ECB", "NoPadding", 192, 24, null, -1),
AES_ECB_NO_PADDING_256("AES", "ECB", "NoPadding", 256, 32, null, -1),
AES_ECB_PKCS5_PADDING_128("AES", "ECB", "PKCS5Padding", 128, 16, null, -1),
AES_ECB_PKCS5_PADDING_192("AES", "ECB", "PKCS5Padding", 192, 24, null, -1),
AES_ECB_PKCS5_PADDING_256("AES", "ECB", "PKCS5Padding", 256, 32, null, -1),
DES_CBC_NO_PADDING_56("DES", "CBC", "NoPadding", 56, 8, IvParameterSpec.class, 64),
DES_CBC_PKCS5_PADDING_56("DES", "CBC", "PKCS5Padding", 56, 8, IvParameterSpec.class, 64),
DES_ECB_NO_PADDING_56("DES", "ECB", "NoPadding", 56, 8, null, -1),
DES_ECB_PKCS5_PADDING_56("DES", "ECB", "PKCS5Padding", 56, 8, null, -1),
DESEDE_CBC_NO_PADDING_168("DESede", "CBC", "NoPadding", 168, 24, IvParameterSpec.class, 64),
DESEDE_CBC_PKCS5_PADDING_168("DESede", "CBC", "PKCS5Padding", 168, 24, IvParameterSpec.class, 64),
DESEDE_ECB_NO_PADDING_168("DESede", "ECB", "NoPadding", 168, 24, null, -1),
DESEDE_ECB_PKCS5_PADDING_168("DESede", "ECB", "PKCS5Padding", 168, 24, null, -1),
BLOWFISH_CBC_NO_PADDING_128("Blowfish", "CBC", "NoPadding", 128, 16, IvParameterSpec.class, 64);
private String transformation = "";
private String algorithm = "";
private String mode = "";
private String padding = "";
private int secretKeyStrLength = -1;
private int secretKeyBitLength = -1;
private Class<? extends AlgorithmParameterSpec> algorithmParameterSpecClass;
private int algorithmParameterBitLength = -1;
SymmetricEncryptionAlgorithm(String algorithm, String mode, String padding, int secretKeyBitLength, int secretKeyStrLength, Class<? extends AlgorithmParameterSpec> algorithmParameterSpecClass, int algorithmParameterBitLength) {
this.algorithm = algorithm;
this.mode = mode;
this.padding = padding;
this.transformation = String.format("%s/%s/%s", algorithm, mode, padding);
this.secretKeyStrLength = secretKeyStrLength;
this.secretKeyBitLength = secretKeyBitLength;
this.algorithmParameterSpecClass = algorithmParameterSpecClass;
this.algorithmParameterBitLength = algorithmParameterBitLength;
}
// 1 使用秘钥字符串加密,附加向量参数
public String encrypt(String plainText, String secretKeyStr, String algorithmParameterStr) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvocationTargetException, NoSuchMethodException, InvalidAlgorithmParameterException, InstantiationException, IllegalAccessException {
Encryptor encryptor = EncryptorBuilder.from(AlgorithmSpec.of().setTransformation(this.algorithm, this.mode, this.padding).setSecretKeyBitLength(this.secretKeyBitLength)
.setAlgorithmParameterSpecClass(this.algorithmParameterSpecClass).setAlgorithmParameterBitLength(this.algorithmParameterBitLength),
AlgorithmParam.of().setPlainText(plainText).setSecretKeyStr(secretKeyStr).setAlgorithmParameterStr(algorithmParameterStr),
(algorithmSpec, algorithmParam) -> {
validateSecretKeyStr(algorithmSpec, algorithmParam);
validateParamSpec(algorithmSpec, algorithmParam);
})
.getCipher(this::getCipher)
.getSecretKeyByStr((secretKeyStrTmp, algorithmTmp) -> new SecretKeySpec(secretKeyStrTmp.getBytes(), algorithmTmp))
.padding(this::setPadding)
.initCipher((cipher, key) -> initCipher(cipher, Cipher.ENCRYPT_MODE, key, algorithmParameterStr))
.get();
return encryptor.encrypt().get();
}
// 1 使用秘钥字符串解密,附加向量参数
public String decrypt(String base64Content, String secretKeyStr, String algorithmParameterStr) throws InvalidKeyException, BadPaddingException, IllegalBlockSizeException, IOException, InvocationTargetException, NoSuchMethodException, InvalidAlgorithmParameterException, InstantiationException, IllegalAccessException {
Encryptor encryptor = EncryptorBuilder.from(AlgorithmSpec.of().setTransformation(this.algorithm, this.mode, this.padding).setSecretKeyBitLength(this.secretKeyBitLength)
.setAlgorithmParameterSpecClass(this.algorithmParameterSpecClass).setAlgorithmParameterBitLength(this.algorithmParameterBitLength),
AlgorithmParam.of().setBase64CipherText(base64Content).setSecretKeyStr(secretKeyStr).setAlgorithmParameterStr(algorithmParameterStr),
(algorithmSpec, algorithmParam) -> {
validateSecretKeyStr(algorithmSpec, algorithmParam);
validateParamSpec(algorithmSpec, algorithmParam);
})
.getCipher(this::getCipher)
.getSecretKeyByStr((secretKeyStrTmp, algorithmTmp) -> new SecretKeySpec(secretKeyStrTmp.getBytes(), algorithmTmp))
.initCipher((cipher, key) -> initCipher(cipher, Cipher.DECRYPT_MODE, key, algorithmParameterStr))
.get();
return encryptor.decrypt().get();
}
// 2 使用秘钥字符串加密
public String encrypt(String plainText, String secretKeyStr) throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
Encryptor encryptor = EncryptorBuilder.from(AlgorithmSpec.of().setTransformation(this.algorithm, this.mode, this.padding).setSecretKeyBitLength(this.secretKeyBitLength),
AlgorithmParam.of().setPlainText(plainText).setSecretKeyStr(secretKeyStr),
(algorithmSpec, algorithmParam) -> {
validateSecretKeyStr(algorithmSpec, algorithmParam);
})
.getCipher(this::getCipher)
.getSecretKeyByStr((secretKeyStrTmp, algorithmTmp) -> new SecretKeySpec(secretKeyStrTmp.getBytes(), algorithmTmp))
.initCipher((cipher, key) -> initCipher(cipher, Cipher.ENCRYPT_MODE, key))
.padding(this::setPadding).get();
return encryptor.encrypt().get();
}
// 2 使用秘钥字符串解密
public String decrypt(String base64Content, String secretKeyStr) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, IOException {
Encryptor encryptor = EncryptorBuilder.from(AlgorithmSpec.of().setTransformation(this.algorithm, this.mode, this.padding).setSecretKeyBitLength(this.secretKeyBitLength),
AlgorithmParam.of().setBase64CipherText(base64Content).setSecretKeyStr(secretKeyStr),
(algorithmSpec, algorithmParam) -> {
validateSecretKeyStr(algorithmSpec, algorithmParam);
})
.getCipher(this::getCipher)
.getSecretKeyByStr((secretKeyStrTmp, algorithmTmp) -> new SecretKeySpec(secretKeyStrTmp.getBytes(), algorithmTmp))
.initCipher((cipher, key) -> initCipher(cipher, Cipher.DECRYPT_MODE, key))
.get();
return encryptor.decrypt().get();
}
// 3 使用随机数种子加密,指定随机数算法,附加向量参数
public String encrypt(String plainText, String randomSeedStr, NumberGenerationAlgorithm ngAlgorithm, String algorithmParameterStr) throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
Encryptor encryptor = EncryptorBuilder.from(AlgorithmSpec.of().setTransformation(this.algorithm, this.mode, this.padding).setSecretKeyBitLength(this.secretKeyBitLength)
.setAlgorithmParameterSpecClass(this.algorithmParameterSpecClass).setAlgorithmParameterBitLength(this.algorithmParameterBitLength),
AlgorithmParam.of().setPlainText(plainText).setRandomSeedStr(randomSeedStr).setNgAlgorithm(ngAlgorithm).setAlgorithmParameterStr(algorithmParameterStr),
(algorithmSpec, algorithmParam) -> {
validateRandomSeed(algorithmSpec, algorithmParam);
validateParamSpec(algorithmSpec, algorithmParam);
})
.getCipher(this::getCipher)
.getSecretKeyByRandomSeed(this::getSecretKeyByRandomSeed)
.initCipher((cipher, key) -> initCipher(cipher, Cipher.ENCRYPT_MODE, key, algorithmParameterStr))
.padding(this::setPadding).get();
return encryptor.encrypt().get();
}
// 3 使用随机数种子解密,指定随机数算法,附加向量参数
public String decrypt(String base64Content, String randomSeedStr, NumberGenerationAlgorithm ngAlgorithm, String algorithmParameterStr) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, IOException {
Encryptor encryptor = EncryptorBuilder.from(AlgorithmSpec.of().setTransformation(this.algorithm, this.mode, this.padding).setSecretKeyBitLength(this.secretKeyBitLength)
.setAlgorithmParameterSpecClass(this.algorithmParameterSpecClass).setAlgorithmParameterBitLength(this.algorithmParameterBitLength),
AlgorithmParam.of().setBase64CipherText(base64Content).setRandomSeedStr(randomSeedStr).setNgAlgorithm(ngAlgorithm).setAlgorithmParameterStr(algorithmParameterStr),
(algorithmSpec, algorithmParam) -> {
validateRandomSeed(algorithmSpec, algorithmParam);
validateParamSpec(algorithmSpec, algorithmParam);
})
.getCipher(this::getCipher)
.getSecretKeyByRandomSeed(this::getSecretKeyByRandomSeed)
.initCipher((cipher, key) -> initCipher(cipher, Cipher.DECRYPT_MODE, key, algorithmParameterStr))
.get();
return encryptor.decrypt().get();
}
// 4 使用随机数种子加密,指定随机数算法
public String encrypt(String plainText, String randomSeedStr, NumberGenerationAlgorithm ngAlgorithm) throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
Encryptor encryptor = EncryptorBuilder.from(AlgorithmSpec.of().setTransformation(this.algorithm, this.mode, this.padding).setSecretKeyBitLength(this.secretKeyBitLength)
.setAlgorithmParameterSpecClass(this.algorithmParameterSpecClass).setAlgorithmParameterBitLength(this.algorithmParameterBitLength),
AlgorithmParam.of().setPlainText(plainText).setRandomSeedStr(randomSeedStr).setNgAlgorithm(ngAlgorithm), this::validateSecretKeyStr)
.getCipher(this::getCipher)
.getSecretKeyByRandomSeed(this::getSecretKeyByRandomSeed)
.padding(this::setPadding).get();
return encryptor.encrypt().get();
}
// 4 使用随机数种子解密,指定随机数算法
public String decrypt(String base64Content, String randomSeedStr, NumberGenerationAlgorithm ngAlgorithm) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, IOException {
Encryptor encryptor = EncryptorBuilder.from(AlgorithmSpec.of().setTransformation(this.algorithm, this.mode, this.padding).setSecretKeyBitLength(this.secretKeyBitLength)
.setAlgorithmParameterSpecClass(this.algorithmParameterSpecClass).setAlgorithmParameterBitLength(this.algorithmParameterBitLength),
AlgorithmParam.of().setBase64CipherText(base64Content).setRandomSeedStr(randomSeedStr).setNgAlgorithm(ngAlgorithm), this::validateSecretKeyStr)
.getCipher(this::getCipher)
.getSecretKeyByStr((secretKeyStrTmp, algorithmTmp) -> new SecretKeySpec(secretKeyStrTmp.getBytes(), algorithmTmp))
.get();
return encryptor.decrypt().get();
}
private boolean validateSecretKeyStr(AlgorithmSpec algorithmSpec, AlgorithmParam algorithmParam) {
if (this.secretKeyStrLength != algorithmParam.getSecretKeyStr().get().length()) {
throw new RuntimeException("Secret key must be " + algorithmSpec.getSecretKeyBitLength().getAsInt() + " bits.");
}
return true;
}
private boolean validateParamSpec(AlgorithmSpec algorithmSpec, AlgorithmParam algorithmParam) {
if (algorithmSpec.getAlgorithmParameterBitLength().getAsInt() != algorithmParam.getAlgorithmParameterStr().get().length() * 8) {
throw new RuntimeException("Vector key must be " + algorithmSpec.getAlgorithmParameterBitLength().getAsInt() + " bits.");
}
return true;
}
private boolean validateRandomSeed(AlgorithmSpec algorithmSpec, AlgorithmParam algorithmParam) {
if (!algorithmParam.getRandomSeedStr().isPresent()) {
throw new RuntimeException("Secret key must be " + algorithmSpec.getSecretKeyBitLength().getAsInt() + " bits.");
}
return true;
}
private Cipher getCipher(String transformation) {
try {
return Cipher.getInstance(transformation);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
e.printStackTrace();
return null;
}
}
private Key getSecretKeyByRandomSeed(String seedStr, NumberGenerationAlgorithm ngAlgorithm) {
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance(this.algorithm);
SecureRandom random = SecureRandom.getInstance(ngAlgorithm.getAlgorithmName());
random.setSeed(seedStr.getBytes());
keyGenerator.init(this.secretKeyBitLength, random);
return keyGenerator.generateKey();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
private byte[] setPadding(String plainText, int blockSize) {
byte[] plainTextBytes = plainText.getBytes();
byte[] plainTextBytesNoPadding = plainTextBytes;
int length = plainTextBytes.length;
//计算需填充长度
length = length + (blockSize - (length % blockSize));
plainTextBytes = new byte[length];
//填充
System.arraycopy(plainTextBytesNoPadding, 0, plainTextBytes, 0, plainTextBytesNoPadding.length);
return plainTextBytes;
}
private void initCipher(Cipher cipher, int mode, Key key, String algorithmParameterStr){
try {
AlgorithmParameterSpec algorithmParameterSpec = this.algorithmParameterSpecClass.getConstructor(new Class[]{byte[].class}).newInstance(algorithmParameterStr.getBytes());
cipher.init(mode, key, algorithmParameterSpec);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException | InvalidKeyException e) {
e.printStackTrace();
}
}
private void initCipher(Cipher cipher, int mode, Key key){
try {
cipher.init(mode, key);
} catch (InvalidKeyException e) {
e.printStackTrace();
}
}
}
package com.qupeng.crypto.algorithm.fp;
import com.qupeng.crypto.algorithm.oop.NumberGenerationAlgorithm;
import javax.crypto.Cipher;
import java.security.Key;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
public abstract class EncryptorDefinition<E extends EncryptorDefinition<E>> {
protected Encryptor encryptor = new Encryptor();
public E getCipher(Function<String, Cipher> cipherSupplier) {
encryptor.setCipherSupplier(cipherSupplier);
return (E)this;
}
public E getSecretKeyByStr(BiFunction<String, String, Key> keyGenerator) {
encryptor.setKeyGeneratorByStr(keyGenerator);
return (E)this;
}
public E getSecretKeyByRandomSeed(BiFunction<String, NumberGenerationAlgorithm, Key> keyGenerator) {
encryptor.setKeyGenerator(keyGenerator);
return (E)this;
}
public E padding(BiFunction<String, Integer, byte[]> padding) {
encryptor.setPadding(padding);
return (E)this;
}
public E initCipher(BiConsumer<Cipher, Key> initCipher) {
encryptor.setInitCipher(initCipher);
return (E)this;
}
public Encryptor get() {
return this.encryptor;
}
}
package com.qupeng.crypto.algorithm.fp;
import java.util.function.BiConsumer;
public class EncryptorBuilder extends EncryptorDefinition<EncryptorBuilder> {
public static EncryptorBuilder from(AlgorithmSpec algorithmSpec, AlgorithmParam algorithmParam, BiConsumer<AlgorithmSpec, AlgorithmParam> paramValidator) {
EncryptorBuilder encryptorBuilder = new EncryptorBuilder();
paramValidator.accept(algorithmSpec, algorithmParam);
Encryptor encryptor = encryptorBuilder.get();
encryptor.setTransformation(algorithmSpec.getTransformation())
.setSecretKeyBitLength(algorithmSpec.getSecretKeyBitLength().getAsInt());
algorithmSpec.getAlgorithmParameterSpecClass().ifPresent(clazz -> encryptor.setAlgorithmParameterSpecInfo(algorithmSpec.getAlgorithmParameterSpecInfo()));
algorithmParam.getPlainText().ifPresent(plainText -> encryptor.setPlainText(plainText));
algorithmParam.getBase64CipherText().ifPresent(cipherText -> encryptor.setBase64CipherText(cipherText));
algorithmParam.getSecretKeyStr().ifPresent(secretKeyStr -> encryptor.setSecretKeyStr(secretKeyStr));
algorithmParam.getRandomSeedStr().ifPresent(randomSeedStr -> encryptor.setRandomSeedStr(randomSeedStr));
algorithmParam.getNgAlgorithm().ifPresent(ngAlgorithm -> encryptor.setNgAlgorithm(ngAlgorithm));
algorithmParam.getAlgorithmParameterStr().ifPresent(algorithmParameterStr -> encryptor.setAlgorithmParameterStr(algorithmParameterStr));
return encryptorBuilder;
}
}
package com.qupeng.crypto.algorithm.fp;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Optional;
import java.util.OptionalInt;
public class AlgorithmSpec {
private Optional<String> algorithm = Optional.empty();
private Optional<String> mode = Optional.empty();
private Optional<String> padding = Optional.empty();
private OptionalInt secretKeyBitLength = OptionalInt.empty();
private Optional<Class<? extends AlgorithmParameterSpec>> algorithmParameterSpecClass = Optional.empty();
private OptionalInt algorithmParameterBitLength = OptionalInt.empty();
public static AlgorithmSpec of() {
return new AlgorithmSpec();
}
public AlgorithmSpec setTransformation(String algotirhm, String mode, String padding) {
this.algorithm = Optional.of(algotirhm);
this.mode = Optional.of(mode);
this.padding = Optional.of(padding);
return this;
}
public Transformation getTransformation() {
return new Transformation(this.algorithm.get(), this.mode.get(), this.padding.get());
}
public OptionalInt getSecretKeyBitLength() {
return secretKeyBitLength;
}
public AlgorithmSpec setSecretKeyBitLength(int secretKeyBitLength) {
this.secretKeyBitLength = OptionalInt.of(secretKeyBitLength);
return this;
}
public AlgorithmSpec setAlgorithmParameterSpecClass(Class<? extends AlgorithmParameterSpec> algorithmParameterSpecClass) {
this.algorithmParameterSpecClass = Optional.of(algorithmParameterSpecClass);
return this;
}
public AlgorithmSpec setAlgorithmParameterBitLength(int algorithmParameterBitLength) {
this.algorithmParameterBitLength = OptionalInt.of(algorithmParameterBitLength);
return this;
}
public Optional<Class<? extends AlgorithmParameterSpec>> getAlgorithmParameterSpecClass() {
return algorithmParameterSpecClass;
}
public OptionalInt getAlgorithmParameterBitLength() {
return algorithmParameterBitLength;
}
public AlgorithmParameterSpecInfo getAlgorithmParameterSpecInfo() {
return new AlgorithmParameterSpecInfo(this.algorithmParameterSpecClass.get() , this.algorithmParameterBitLength.getAsInt());
}
}
package com.qupeng.crypto.algorithm.fp;
import com.qupeng.crypto.algorithm.oop.NumberGenerationAlgorithm;
import java.util.Optional;
public class AlgorithmParam {
private Optional<String> plainText = Optional.empty();
private Optional<String> base64CipherText = Optional.empty();
private Optional<String> secretKeyStr = Optional.empty();
private Optional<String> randomSeedStr = Optional.empty();
private Optional<NumberGenerationAlgorithm> ngAlgorithm = Optional.empty();
private Optional<String> algorithmParameterStr = Optional.empty();
public static AlgorithmParam of() {
return new AlgorithmParam();
}
public AlgorithmParam setPlainText(String plainText) {
this.plainText = Optional.of(plainText);
return this;
}
public AlgorithmParam setBase64CipherText(String base64CipherText) {
this.base64CipherText = Optional.of(base64CipherText);
return this;
}
public AlgorithmParam setSecretKeyStr(String secretKeyStr) {
this.secretKeyStr = Optional.of(secretKeyStr);
return this;
}
public AlgorithmParam setRandomSeedStr(String randomSeedStr) {
this.randomSeedStr = Optional.of(randomSeedStr);
return this;
}
public AlgorithmParam setNgAlgorithm(NumberGenerationAlgorithm ngAlgorithm) {
this.ngAlgorithm = Optional.of(ngAlgorithm);
return this;
}
public AlgorithmParam setAlgorithmParameterStr(String algorithmParameterStr) {
this.algorithmParameterStr = Optional.of(algorithmParameterStr);
return this;
}
public Optional<String> getPlainText() {
return plainText;
}
public Optional<String> getBase64CipherText() {
return base64CipherText;
}
public Optional<String> getSecretKeyStr() {
return secretKeyStr;
}
public Optional<String> getRandomSeedStr() {
return randomSeedStr;
}
public Optional<NumberGenerationAlgorithm> getNgAlgorithm() {
return ngAlgorithm;
}
public Optional<String> getAlgorithmParameterStr() {
return algorithmParameterStr;
}
}
package com.qupeng.crypto.algorithm.fp;
import com.qupeng.crypto.algorithm.oop.NumberGenerationAlgorithm;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
public class Encryptor {
private Optional<Transformation> transformation = Optional.empty();
private OptionalInt secretKeyBitLength = OptionalInt.empty();
private Optional<AlgorithmParameterSpecInfo> algorithmParameterSpecInfo = Optional.empty();
private Optional<String> plainText = Optional.empty();
private Optional<String> base64CipherText = Optional.empty();
private Optional<String> secretKeyStr = Optional.empty();
private Optional<String> randomSeedStr = Optional.empty();
private Optional<NumberGenerationAlgorithm> ngAlgorithm = Optional.empty();
private Optional<String> algorithmParameterStr = Optional.empty();
private Optional<BiFunction<String, String, Key>> keyGeneratorByStr = Optional.empty();
private Optional<BiFunction<String, NumberGenerationAlgorithm, Key>> keyGeneratorByRandomSeed = Optional.empty();
private Optional<Function<String, Cipher>> cipherSupplier = Optional.empty();
private Optional<BiFunction<String, Integer, byte[]>> padding = Optional.empty();
private Optional<BiConsumer<Cipher, Key>> initCipher = Optional.empty();
public Optional<String> encrypt() throws BadPaddingException, IllegalBlockSizeException, InvalidKeyException, NoSuchMethodException, InvalidAlgorithmParameterException, IllegalAccessException, InstantiationException, InvocationTargetException {
// Step1: create a Cipher instance
Cipher cipher = cipherSupplier.get().apply(this.transformation.get().toString());
// Step2: padding plain text if no-padding
byte[][] plainTextBytes = new byte[1][];
if ("NoPadding".equals(transformation.get().getPadding().get())) {
padding.ifPresent(paddingOperator -> plainTextBytes[0] = paddingOperator.apply(plainText.get(), cipher.getBlockSize()));
} else {
plainTextBytes[0] = plainText.get().getBytes();
}
// Step3: generate secret key
Key[] secretKey = generateSecretKey();
// Step4: init cipher
initCipher.ifPresent(init -> init.accept(cipher, secretKey[0]));
// Step5: encrypt plain text
byte[] encrypted = cipher.doFinal(plainTextBytes[0]);
String cipherText = new BASE64Encoder().encode(encrypted);
System.out.println(String.format("%s(%d) plain text: %s -> cipher text: %s", this.transformation.get().toString(), this.secretKeyBitLength.getAsInt(), plainText.get(), cipherText));
return Optional.ofNullable(cipherText);
}
public Optional<String> decrypt() throws IOException, BadPaddingException, IllegalBlockSizeException, InvalidKeyException, NoSuchMethodException, InvalidAlgorithmParameterException, IllegalAccessException, InstantiationException, InvocationTargetException {
// Step1: create a Cipher instance
Cipher cipher = cipherSupplier.get().apply(this.transformation.get().toString());
// Step2: generate secret key
Key[] secretKey = generateSecretKey();
// Step3: init cipher
initCipher.ifPresent(init -> init.accept(cipher, secretKey[0]));
// Step4: decrypt cipher text
byte[] plainBytes = cipher.doFinal(new BASE64Decoder().decodeBuffer(base64CipherText.get()));
String plainText = new String(plainBytes).trim();
System.out.println(String.format("%s(%d) cipher text: %s -> plain text: %s", this.transformation.get().toString(), this.secretKeyBitLength.getAsInt(), base64CipherText.get(), plainText));
return Optional.of(plainText);
}
private Key[] generateSecretKey() {
Key secretKey[] = new Key[1];
keyGeneratorByStr.ifPresent(keyGenerator -> secretKey[0] = keyGenerator.apply(secretKeyStr.get(), transformation.get().getAlgorithm().get()));
keyGeneratorByRandomSeed.ifPresent(keyGenerator -> secretKey[0] = keyGenerator.apply(randomSeedStr.get(), ngAlgorithm.get()));
return secretKey;
}
public Encryptor setTransformation(Transformation transformation) {
this.transformation = Optional.of(transformation);
return this;
}
public Encryptor setSecretKeyBitLength(int secretKeyBitLength) {
this.secretKeyBitLength = OptionalInt.of(secretKeyBitLength);
return this;
}
public Encryptor setAlgorithmParameterSpecInfo(AlgorithmParameterSpecInfo algorithmParameterSpecInfo) {
this.algorithmParameterSpecInfo = Optional.of(algorithmParameterSpecInfo);
return this;
}
public Encryptor setPlainText(String plainText) {
this.plainText = Optional.of(plainText);
return this;
}
public Encryptor setBase64CipherText(String base64CipherText) {
this.base64CipherText = Optional.of(base64CipherText);
return this;
}
public Encryptor setSecretKeyStr(String secretKeyStr) {
this.secretKeyStr = Optional.of(secretKeyStr);
return this;
}
public Encryptor setRandomSeedStr(String randomSeedStr) {
this.randomSeedStr = Optional.of(randomSeedStr);
return this;
}
public Encryptor setNgAlgorithm(NumberGenerationAlgorithm ngAlgorithm) {
this.ngAlgorithm = Optional.of(ngAlgorithm);
return this;
}
public Encryptor setAlgorithmParameterStr(String algorithmParameterStr) {
this.algorithmParameterStr = Optional.of(algorithmParameterStr);
return this;
}
public void setKeyGeneratorByStr(BiFunction<String, String, Key> keyGeneratorByStr) {
this.keyGeneratorByStr = Optional.of(keyGeneratorByStr);
}
public void setKeyGenerator(BiFunction<String, NumberGenerationAlgorithm, Key> keyGeneratorByRandomSeed) {
this.keyGeneratorByRandomSeed = Optional.of(keyGeneratorByRandomSeed);
}
public void setCipherSupplier(Function<String, Cipher> cipherSupplier) {
this.cipherSupplier = Optional.of(cipherSupplier);
}
public void setPadding(BiFunction<String, Integer, byte[]> padding) {
this.padding = Optional.of(padding);
}
public void setInitCipher(BiConsumer<Cipher, Key> initCipher) {
this.initCipher = Optional.of(initCipher);
}
}
package com.qupeng.crypto.algorithm.fp;
import java.util.Optional;
public class Transformation {
private Optional<String> algorithm = Optional.empty();
private Optional<String> mode = Optional.empty();
private Optional<String> padding = Optional.empty();
public Transformation(String algorithm, String mode, String padding) {
this.algorithm = Optional.of(algorithm);
this.mode = Optional.of(mode);
this.padding = Optional.of(padding);
}
public Optional<String> getAlgorithm() {
return algorithm;
}
public Optional<String> getMode() {
return mode;
}
public Optional<String> getPadding() {
return padding;
}
@Override
public String toString() {
return String.format("%s/%s/%s", this.algorithm.get(), this.mode.get(), this.padding.get());
}
}
package com.qupeng.crypto.algorithm.fp;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Optional;
import java.util.OptionalInt;
public class AlgorithmParameterSpecInfo {
private Optional<Class<? extends AlgorithmParameterSpec>> algorithmParameterSpecClass;
private OptionalInt algorithmParameterBitLength = OptionalInt.empty();
public AlgorithmParameterSpecInfo(Class<? extends AlgorithmParameterSpec> algorithmParameterSpecClass, int algorithmParameterBitLength) {
this.algorithmParameterSpecClass = Optional.of(algorithmParameterSpecClass);
this.algorithmParameterBitLength = OptionalInt.of(algorithmParameterBitLength);
}
public Optional<Class<? extends AlgorithmParameterSpec>> getAlgorithmParameterSpecClass() {
return algorithmParameterSpecClass;
}
public OptionalInt getAlgorithmParameterBitLength() {
return algorithmParameterBitLength;
}
}
package com.qupeng.crypto.algorithm.oop;
public enum NumberGenerationAlgorithm {
NATIVE_PRNG("NativePRNG"),
NATIVE_PRNG_BLOCKING("NativePRNGBlocking"),
NATIVE_PRNG_NON_BLOCKING("NativePRNGNonBlocking"),
PKCS11("PKCS11"),
SHA1_PRNG("SHA1PRNG"),
WINDOWS_PRNG("Windows-PRNG");
private String algorithmName = "";
NumberGenerationAlgorithm(String algorithmName) {
this.algorithmName = algorithmName;
}
public String getAlgorithmName() {
return this.algorithmName;
}
}