Java 实现 AES 加密

2020-04-12  本文已影响0人  又语

本文介绍 Java 语言实现 AES 加密的方法。


目录


AES 简介

AES,Advanced Encryption Standard,高级加密标准,密码学中又称为 Rijndael 加密法,是一种区块加密标准,用来替代原先的 DES。

AES 是一个迭代的、对称密钥分组的密码,可以使用 128192256 位密钥,并且使用 128 位(16 字节)分组加密和解密数据。美国国家标准与技术局和 ANSI X9 已经设定了最小密钥长度的要求,对称密钥加密的密钥长度是至少 128 位,这样在 2030 年以前是安全的。

AES 加密有多轮重复和变幻,大致步骤如下:

  1. 密钥扩展(Key Expansion)
  2. 初始轮(Initial Round)
  3. 重复轮(Rounds),每一轮又包括:SubBytes、ShiftRows、MixColumns、AddRoundKey
  4. 最终轮(Final Round),最终轮没有 MixColumns

AES transformation

算法/模式/填充 16字节加密后数据长度 不满16字节加密后长度
AES/CBC/NoPadding 16 不支持
AES/CBC/PKCS5Padding 32 16
AES/CBC/ISO10126Padding 32 16
AES/CFB/NoPadding 16 原始数据长度
AES/CFB/PKCS5Padding 32 16
AES/CFB/ISO10126Padding 32 16
AES/ECB/NoPadding 16 不支持
AES/ECB/PKCS5Padding 32 16
AES/ECB/ISO10126Padding 32 16
AES/OFB/NoPadding 16 原始数据长度
AES/OFB/PKCS5Padding 32 16
AES/OFB/ISO10126Padding 32 16
AES/PCBC/NoPadding 16 不支持
AES/PCBC/PKCS5Padding 32 16
AES/PCBC/ISO10126Padding 32 16
  1. 在原始数据长度为 16 的整数倍时,假如原始数据长度等于 16*n,则使用 NoPadding 时加密后数据长度等于 16*n,其它情况下加密数据长度等于 16*(n+1)
  2. 在不足 16 的整数倍的情况下,假如原始数据长度等于 16*n+m [其中 m 小于 16],除了 NoPadding 填充之外的任何方式,加密数据长度都等于 16*(n+1),NoPadding 填充情况下,CBC、ECB 和 PCBC 三种模式是不支持的,CFB 和 OFB两种模式下则加密数据长度等于原始数据长度。

代码实现

ECB

NoPadding
package tutorial.java.util;

import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

public class AesUtils {

    /**
     * 算法名称
     */
    private static final String ALGORITHM_AES = "AES";

    /**
     * AES 128 位密钥加密
     *
     * @param content  待加密内容
     * @param password 加密密码
     * @return 加密后密文
     */
    public static byte[] encrypt128(byte[] content, byte[] password) {
        try {
            Key key = keyGenerator(password, 128);
            // 构造密码生成器
            Cipher cipher = Cipher.getInstance(ALGORITHM_AES + "/" + Mode.ECB.value + "/" + Padding.NO_PADDING.value);
            // 初始化密码生成器
            cipher.init(Cipher.ENCRYPT_MODE, key);
            return cipher.doFinal(content);
        } catch (InvalidKeyException e) {
            throw new UnsupportedOperationException("Invalid Key");
        } catch (NoSuchAlgorithmException e) {
            throw new UnsupportedOperationException("No such algorithm");
        } 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");
        }
    }

    /**
     * AES 128 位密钥解密
     *
     * @param content  待解密内容
     * @param password 解密密码
     * @return 解密后原文
     */
    public static byte[] decrypt128(byte[] content, byte[] password) {
        try {
            Key key = keyGenerator(password, 128);
            // 构造密码生成器
            Cipher cipher = Cipher.getInstance(ALGORITHM_AES + "/" + Mode.ECB.value + "/" + Padding.NO_PADDING.value);
            // 初始化密码生成器
            cipher.init(Cipher.DECRYPT_MODE, key);
            return cipher.doFinal(content);
        } catch (InvalidKeyException e) {
            throw new UnsupportedOperationException("Invalid Key");
        } catch (NoSuchAlgorithmException e) {
            throw new UnsupportedOperationException("No such algorithm");
        } 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");
        }
    }

    /**
     * 生成 AES 密钥对象
     *
     * @param password  加密密码
     * @param keyLength 密钥长度
     * @return 密钥对象
     * @throws NoSuchAlgorithmException 无相关算法
     */
    private static Key keyGenerator(byte[] password, int keyLength) throws NoSuchAlgorithmException {
        KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM_AES);
        keyGenerator.init(keyLength, new SecureRandom(password));
        SecretKey secretKey = keyGenerator.generateKey();
        return new SecretKeySpec(secretKey.getEncoded(), ALGORITHM_AES);
    }

    private enum Mode {
        ECB("ECB");

        private String value;

        Mode(String value) {
            this.value = value;
        }
    }

    private enum Padding {
        NO_PADDING("NoPadding");

        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 AesUtilsTest {

    @Test
    public void test() {
        // NoPadding 填充模式下待加密原文长度必须是 16 的整数倍
        String content = "AES Test Content";
        String password = "AES Password";
        byte[] encryptResult = AesUtils.encrypt128(content.getBytes(StandardCharsets.UTF_8),
                password.getBytes(StandardCharsets.UTF_8));
        byte[] decryptResult = AesUtils.decrypt128(encryptResult,
                password.getBytes(StandardCharsets.UTF_8));
        Assert.assertEquals(content, new String(decryptResult, StandardCharsets.UTF_8));
    }
}
PKCS5Padding
package tutorial.java.util;

import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

public class AesUtils {

    /**
     * 算法名称
     */
    private static final String ALGORITHM_AES = "AES";

    /**
     * AES 192 位密钥加密
     *
     * @param content  待加密内容
     * @param password 加密密码
     * @return 加密后密文
     */
    public static byte[] encrypt192(byte[] content, byte[] password) {
        try {
            Key key = keyGenerator(password, 192);
            // 构造密码生成器
            Cipher cipher = Cipher.getInstance(ALGORITHM_AES + "/" + Mode.ECB.value + "/" + Padding.PKCS5_PADDING.value);
            // 初始化密码生成器
            cipher.init(Cipher.ENCRYPT_MODE, key);
            return cipher.doFinal(content);
        } catch (InvalidKeyException e) {
            throw new UnsupportedOperationException("Invalid Key");
        } catch (NoSuchAlgorithmException e) {
            throw new UnsupportedOperationException("No such algorithm");
        } 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");
        }
    }

    /**
     * AES 192 位密钥解密
     *
     * @param content  待解密内容
     * @param password 解密密码
     * @return 解密后原文
     */
    public static byte[] decrypt192(byte[] content, byte[] password) {
        try {
            Key key = keyGenerator(password, 192);
            // 构造密码生成器
            Cipher cipher = Cipher.getInstance(ALGORITHM_AES + "/" + Mode.ECB.value + "/" + Padding.PKCS5_PADDING.value);
            // 初始化密码生成器
            cipher.init(Cipher.DECRYPT_MODE, key);
            return cipher.doFinal(content);
        } catch (InvalidKeyException e) {
            throw new UnsupportedOperationException("Invalid Key");
        } catch (NoSuchAlgorithmException e) {
            throw new UnsupportedOperationException("No such algorithm");
        } 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");
        }
    }

    /**
     * 生成 AES 密钥对象
     *
     * @param password  加密密码
     * @param keyLength 密钥长度
     * @return 密钥对象
     * @throws NoSuchAlgorithmException 无相关算法
     */
    private static Key keyGenerator(byte[] password, int keyLength) throws NoSuchAlgorithmException {
        KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM_AES);
        keyGenerator.init(keyLength, new SecureRandom(password));
        SecretKey secretKey = keyGenerator.generateKey();
        return new SecretKeySpec(secretKey.getEncoded(), ALGORITHM_AES);
    }

    private enum Mode {
        ECB("ECB");

        private String value;

        Mode(String value) {
            this.value = value;
        }
    }

    private 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 AesUtilsTest {

    @Test
    public void test() {
        // PKCS5Padding 填充模式下待加密原文长度不要求必须是 16 的整数倍
        String content = "AES Test X";
        String password = "AES Password";
        byte[] encryptResult = AesUtils.encrypt192(content.getBytes(StandardCharsets.UTF_8),
                password.getBytes(StandardCharsets.UTF_8));
        byte[] decryptResult = AesUtils.decrypt192(encryptResult,
                password.getBytes(StandardCharsets.UTF_8));
        Assert.assertEquals(content, new String(decryptResult, StandardCharsets.UTF_8));
    }
}
ISO10126Padding
package tutorial.java.util;

import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

public class AesUtils {

    /**
     * 算法名称
     */
    private static final String ALGORITHM_AES = "AES";

    /**
     * AES 256 位密钥加密
     *
     * @param content  待加密内容
     * @param password 加密密码
     * @return 加密后密文
     */
    public static byte[] encrypt256(byte[] content, byte[] password) {
        try {
            Key key = keyGenerator(password, 256);
            // 构造密码生成器
            Cipher cipher = Cipher.getInstance(ALGORITHM_AES + "/" + Mode.ECB.value + "/" + Padding.ISO10126_PADDING.value);
            // 初始化密码生成器
            cipher.init(Cipher.ENCRYPT_MODE, key);
            return cipher.doFinal(content);
        } catch (InvalidKeyException e) {
            throw new UnsupportedOperationException("Invalid Key");
        } catch (NoSuchAlgorithmException e) {
            throw new UnsupportedOperationException("No such algorithm");
        } 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");
        }
    }

    /**
     * AES 256 位密钥解密
     *
     * @param content  待解密内容
     * @param password 解密密码
     * @return 解密后原文
     */
    public static byte[] decrypt256(byte[] content, byte[] password) {
        try {
            Key key = keyGenerator(password, 256);
            // 构造密码生成器
            Cipher cipher = Cipher.getInstance(ALGORITHM_AES + "/" + Mode.ECB.value + "/" + Padding.ISO10126_PADDING.value);
            // 初始化密码生成器
            cipher.init(Cipher.DECRYPT_MODE, key);
            return cipher.doFinal(content);
        } catch (InvalidKeyException e) {
            throw new UnsupportedOperationException("Invalid Key");
        } catch (NoSuchAlgorithmException e) {
            throw new UnsupportedOperationException("No such algorithm");
        } 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");
        }
    }

    /**
     * 生成 AES 密钥对象
     *
     * @param password  加密密码
     * @param keyLength 密钥长度
     * @return 密钥对象
     * @throws NoSuchAlgorithmException 无相关算法
     */
    private static Key keyGenerator(byte[] password, int keyLength) throws NoSuchAlgorithmException {
        KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM_AES);
        keyGenerator.init(keyLength, new SecureRandom(password));
        SecretKey secretKey = keyGenerator.generateKey();
        return new SecretKeySpec(secretKey.getEncoded(), ALGORITHM_AES);
    }

    private enum Mode {
        ECB("ECB");

        private String value;

        Mode(String value) {
            this.value = value;
        }
    }

    private enum Padding {
        NO_PADDING("NoPadding"),
        PKCS5_PADDING("PKCS5Padding"),
        ISO10126_PADDING("ISO10126Padding");

        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 AesUtilsTest {

    @Test
    public void test() {
        // ISO10126Padding 填充模式下待加密原文长度不要求必须是 16 的整数倍
        String content = "AES Test X";
        String password = "AES Password";
        byte[] encryptResult = AesUtils.encrypt256(content.getBytes(StandardCharsets.UTF_8),
                password.getBytes(StandardCharsets.UTF_8));
        byte[] decryptResult = AesUtils.decrypt256(encryptResult,
                password.getBytes(StandardCharsets.UTF_8));
        Assert.assertEquals(content, new String(decryptResult, StandardCharsets.UTF_8));
    }
}

CBC

package tutorial.java.util;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;

public class AesUtils {

    /**
     * 算法名称
     */
    private static final String ALGORITHM_AES = "AES";

    /**
     * 偏移量
     * 注意:
     * 1.除 ECB 模式外,其它模式下加解密 Cipher 初始化必须传入同一个 IvParameterSpec 实例参数
     * 2.IvParameterSpec 构造函数中参数字节数组长度必须是 16 位
     */
    private static final IvParameterSpec IV_PARAMETER_SPEC =
            new IvParameterSpec(new SecureRandom().generateSeed(16));

    /**
     * AES 256 位密钥加密
     *
     * @param content  待加密内容
     * @param password 加密密码
     * @return 加密后密文
     */
    public static byte[] encrypt256(byte[] content, byte[] password) {
        try {
            Key key = keyGenerator(password, 256);
            // 构造密码生成器
            Cipher cipher = Cipher.getInstance(ALGORITHM_AES + "/" + Mode.CBC.value + "/" + Padding.NO_PADDING.value);
            // 初始化密码生成器,注意除 ECB 模式外,其它模式初始化时必须指定偏移量
            cipher.init(Cipher.ENCRYPT_MODE, key, 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 (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("Invalid Algorithm Parameter");
        }
    }

    /**
     * AES 256 位密钥解密
     *
     * @param content  待解密内容
     * @param password 解密密码
     * @return 解密后原文
     */
    public static byte[] decrypt256(byte[] content, byte[] password) {
        try {
            Key key = keyGenerator(password, 256);
            // 构造密码生成器
            Cipher cipher = Cipher.getInstance(ALGORITHM_AES + "/" + Mode.CBC.value + "/" + Padding.NO_PADDING.value);
            // 初始化密码生成器,注意除 ECB 模式外,其它模式初始化时必须指定偏移量
            cipher.init(Cipher.DECRYPT_MODE, key, 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 (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("Invalid Algorithm Parameter");
        }
    }

    /**
     * 生成 AES 密钥对象
     *
     * @param password  加密密码
     * @param keyLength 密钥长度
     * @return 密钥对象
     * @throws NoSuchAlgorithmException 无相关算法
     */
    private static Key keyGenerator(byte[] password, int keyLength) throws NoSuchAlgorithmException {
        KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM_AES);
        keyGenerator.init(keyLength, new SecureRandom(password));
        SecretKey secretKey = keyGenerator.generateKey();
        return new SecretKeySpec(secretKey.getEncoded(), ALGORITHM_AES);
    }

    private enum Mode {
        ECB("ECB"),
        CBC("CBC");

        private String value;

        Mode(String value) {
            this.value = value;
        }
    }

    private enum Padding {
        NO_PADDING("NoPadding"),
        PKCS5_PADDING("PKCS5Padding"),
        ISO10126_PADDING("ISO10126Padding");

        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 AesUtilsTest {

    @Test
    public void test() {
        // NoPadding 填充模式下待加密原文长度必须是 16 的整数倍
        String content = "AES Test Content";
        String password = "AES Password";
        byte[] encryptResult = AesUtils.encrypt256(content.getBytes(StandardCharsets.UTF_8),
                password.getBytes(StandardCharsets.UTF_8));
        byte[] decryptResult = AesUtils.decrypt256(encryptResult,
                password.getBytes(StandardCharsets.UTF_8));
        Assert.assertEquals(content, new String(decryptResult, StandardCharsets.UTF_8));
    }
}

CFB

package tutorial.java.util;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;

public class AesUtils {

    /**
     * 算法名称
     */
    private static final String ALGORITHM_AES = "AES";

    /**
     * 偏移量
     * 注意:
     * 1.除 ECB 模式外,其它模式下加解密 Cipher 初始化必须传入同一个 IvParameterSpec 实例参数
     * 2.IvParameterSpec 构造函数中参数字节数组长度必须是 16 位
     */
    private static final IvParameterSpec IV_PARAMETER_SPEC =
            new IvParameterSpec(new SecureRandom().generateSeed(16));

    /**
     * AES 256 位密钥加密
     *
     * @param content  待加密内容
     * @param password 加密密码
     * @return 加密后密文
     */
    public static byte[] encrypt256(byte[] content, byte[] password) {
        try {
            Key key = keyGenerator(password, 256);
            // 构造密码生成器
            Cipher cipher = Cipher.getInstance(ALGORITHM_AES + "/" + Mode.CFB.value + "/" + Padding.NO_PADDING.value);
            // 初始化密码生成器,注意除 ECB 模式外,其它模式初始化时必须指定偏移量
            cipher.init(Cipher.ENCRYPT_MODE, key, 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 (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("Invalid Algorithm Parameter");
        }
    }

    /**
     * AES 256 位密钥解密
     *
     * @param content  待解密内容
     * @param password 解密密码
     * @return 解密后原文
     */
    public static byte[] decrypt256(byte[] content, byte[] password) {
        try {
            Key key = keyGenerator(password, 256);
            // 构造密码生成器
            Cipher cipher = Cipher.getInstance(ALGORITHM_AES + "/" + Mode.CFB.value + "/" + Padding.NO_PADDING.value);
            // 初始化密码生成器,注意除 ECB 模式外,其它模式初始化时必须指定偏移量
            cipher.init(Cipher.DECRYPT_MODE, key, 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 (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("Invalid Algorithm Parameter");
        }
    }

    /**
     * 生成 AES 密钥对象
     *
     * @param password  加密密码
     * @param keyLength 密钥长度
     * @return 密钥对象
     * @throws NoSuchAlgorithmException 无相关算法
     */
    private static Key keyGenerator(byte[] password, int keyLength) throws NoSuchAlgorithmException {
        KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM_AES);
        keyGenerator.init(keyLength, new SecureRandom(password));
        SecretKey secretKey = keyGenerator.generateKey();
        return new SecretKeySpec(secretKey.getEncoded(), ALGORITHM_AES);
    }

    private enum Mode {
        ECB("ECB"),
        CBC("CBC"),
        CFB("CFB");

        private String value;

        Mode(String value) {
            this.value = value;
        }
    }

    private enum Padding {
        NO_PADDING("NoPadding"),
        PKCS5_PADDING("PKCS5Padding"),
        ISO10126_PADDING("ISO10126Padding");

        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 AesUtilsTest {

    @Test
    public void test() {
        // NoPadding 填充模式下待加密原文长度必须是 16 的整数倍
        String content = "AES Test Content";
        String password = "AES Password";
        byte[] encryptResult = AesUtils.encrypt256(content.getBytes(StandardCharsets.UTF_8),
                password.getBytes(StandardCharsets.UTF_8));
        byte[] decryptResult = AesUtils.decrypt256(encryptResult,
                password.getBytes(StandardCharsets.UTF_8));
        Assert.assertEquals(content, new String(decryptResult, StandardCharsets.UTF_8));
    }
}

OFB

package tutorial.java.util;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;

public class AesUtils {

    /**
     * 算法名称
     */
    private static final String ALGORITHM_AES = "AES";

    /**
     * 偏移量
     * 注意:
     * 1.除 ECB 模式外,其它模式下加解密 Cipher 初始化必须传入同一个 IvParameterSpec 实例参数
     * 2.IvParameterSpec 构造函数中参数字节数组长度必须是 16 位
     */
    private static final IvParameterSpec IV_PARAMETER_SPEC =
            new IvParameterSpec(new SecureRandom().generateSeed(16));

    /**
     * AES 256 位密钥加密
     *
     * @param content  待加密内容
     * @param password 加密密码
     * @return 加密后密文
     */
    public static byte[] encrypt256(byte[] content, byte[] password) {
        try {
            Key key = keyGenerator(password, 256);
            // 构造密码生成器
            Cipher cipher = Cipher.getInstance(ALGORITHM_AES + "/" + Mode.OFB.value + "/" + Padding.NO_PADDING.value);
            // 初始化密码生成器,注意除 ECB 模式外,其它模式初始化时必须指定偏移量
            cipher.init(Cipher.ENCRYPT_MODE, key, 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 (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("Invalid Algorithm Parameter");
        }
    }

    /**
     * AES 256 位密钥解密
     *
     * @param content  待解密内容
     * @param password 解密密码
     * @return 解密后原文
     */
    public static byte[] decrypt256(byte[] content, byte[] password) {
        try {
            Key key = keyGenerator(password, 256);
            // 构造密码生成器
            Cipher cipher = Cipher.getInstance(ALGORITHM_AES + "/" + Mode.OFB.value + "/" + Padding.NO_PADDING.value);
            // 初始化密码生成器,注意除 ECB 模式外,其它模式初始化时必须指定偏移量
            cipher.init(Cipher.DECRYPT_MODE, key, 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 (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("Invalid Algorithm Parameter");
        }
    }

    /**
     * 生成 AES 密钥对象
     *
     * @param password  加密密码
     * @param keyLength 密钥长度
     * @return 密钥对象
     * @throws NoSuchAlgorithmException 无相关算法
     */
    private static Key keyGenerator(byte[] password, int keyLength) throws NoSuchAlgorithmException {
        KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM_AES);
        keyGenerator.init(keyLength, new SecureRandom(password));
        SecretKey secretKey = keyGenerator.generateKey();
        return new SecretKeySpec(secretKey.getEncoded(), ALGORITHM_AES);
    }

    private enum Mode {
        ECB("ECB"),
        CBC("CBC"),
        CFB("CFB"),
        OFB("OFB");

        private String value;

        Mode(String value) {
            this.value = value;
        }
    }

    private enum Padding {
        NO_PADDING("NoPadding"),
        PKCS5_PADDING("PKCS5Padding"),
        ISO10126_PADDING("ISO10126Padding");

        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 AesUtilsTest {

    @Test
    public void test() {
        // NoPadding 填充模式下待加密原文长度必须是 16 的整数倍
        String content = "AES Test Content";
        String password = "AES Password";
        byte[] encryptResult = AesUtils.encrypt256(content.getBytes(StandardCharsets.UTF_8),
                password.getBytes(StandardCharsets.UTF_8));
        byte[] decryptResult = AesUtils.decrypt256(encryptResult,
                password.getBytes(StandardCharsets.UTF_8));
        Assert.assertEquals(content, new String(decryptResult, StandardCharsets.UTF_8));
    }
}

PCBC

package tutorial.java.util;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;

public class AesUtils {

    /**
     * 算法名称
     */
    private static final String ALGORITHM_AES = "AES";

    /**
     * 偏移量
     * 注意:
     * 1.除 ECB 模式外,其它模式下加解密 Cipher 初始化必须传入同一个 IvParameterSpec 实例参数
     * 2.IvParameterSpec 构造函数中参数字节数组长度必须是 16 位
     */
    private static final IvParameterSpec IV_PARAMETER_SPEC =
            new IvParameterSpec(new SecureRandom().generateSeed(16));

    /**
     * AES 256 位密钥加密
     *
     * @param content  待加密内容
     * @param password 加密密码
     * @return 加密后密文
     */
    public static byte[] encrypt256(byte[] content, byte[] password) {
        try {
            Key key = keyGenerator(password, 256);
            // 构造密码生成器
            Cipher cipher = Cipher.getInstance(ALGORITHM_AES + "/" + Mode.PCBC.value + "/" + Padding.NO_PADDING.value);
            // 初始化密码生成器,注意除 ECB 模式外,其它模式初始化时必须指定偏移量
            cipher.init(Cipher.ENCRYPT_MODE, key, 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 (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("Invalid Algorithm Parameter");
        }
    }

    /**
     * AES 256 位密钥解密
     *
     * @param content  待解密内容
     * @param password 解密密码
     * @return 解密后原文
     */
    public static byte[] decrypt256(byte[] content, byte[] password) {
        try {
            Key key = keyGenerator(password, 256);
            // 构造密码生成器
            Cipher cipher = Cipher.getInstance(ALGORITHM_AES + "/" + Mode.PCBC.value + "/" + Padding.NO_PADDING.value);
            // 初始化密码生成器,注意除 ECB 模式外,其它模式初始化时必须指定偏移量
            cipher.init(Cipher.DECRYPT_MODE, key, 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 (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("Invalid Algorithm Parameter");
        }
    }

    /**
     * 生成 AES 密钥对象
     *
     * @param password  加密密码
     * @param keyLength 密钥长度
     * @return 密钥对象
     * @throws NoSuchAlgorithmException 无相关算法
     */
    private static Key keyGenerator(byte[] password, int keyLength) throws NoSuchAlgorithmException {
        KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM_AES);
        keyGenerator.init(keyLength, new SecureRandom(password));
        SecretKey secretKey = keyGenerator.generateKey();
        return new SecretKeySpec(secretKey.getEncoded(), ALGORITHM_AES);
    }

    private enum Mode {
        ECB("ECB"),
        CBC("CBC"),
        CFB("CFB"),
        OFB("OFB"),
        PCBC("PCBC");

        private String value;

        Mode(String value) {
            this.value = value;
        }
    }

    private enum Padding {
        NO_PADDING("NoPadding"),
        PKCS5_PADDING("PKCS5Padding"),
        ISO10126_PADDING("ISO10126Padding");

        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 AesUtilsTest {

    @Test
    public void test() {
        // NoPadding 填充模式下待加密原文长度必须是 16 的整数倍
        String content = "AES Test Content";
        String password = "AES Password";
        byte[] encryptResult = AesUtils.encrypt256(content.getBytes(StandardCharsets.UTF_8),
                password.getBytes(StandardCharsets.UTF_8));
        byte[] decryptResult = AesUtils.decrypt256(encryptResult,
                password.getBytes(StandardCharsets.UTF_8));
        Assert.assertEquals(content, new String(decryptResult, StandardCharsets.UTF_8));
    }
}
上一篇 下一篇

猜你喜欢

热点阅读