目录

  • 对称加密
  • 1 定义
  • 2 特点
  • 3 使用场景
  • 4 常用的对称加密算法
  • 5 JDK支持的对称加密算法
  • 6 Bouncy Castle 支持的对称加密算法
  • 7 算法调用示例


对称加密

1 定义

采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。

2 特点

  1. 加密和解密使用同样的密钥
  2. 计算速度快,适用于对大量数据加密处理
  3. 安全性取决于算法,也取决于密钥的管理,一旦密钥泄漏,数据则暴露无遗

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;
    }
}