美文网首页
Java 实现 AES 加密

Java 实现 AES 加密

作者: 又语 | 来源:发表于2020-04-12 09:26 被阅读0次

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


目录

  • AES 简介
  • AES transformation
  • 代码实现
    • ECB
      • NoPadding
      • PKCS5Padding
      • ISO10126Padding
    • CBC
    • CFB
    • OFB
    • PCBC

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

相关文章

网友评论

      本文标题:Java 实现 AES 加密

      本文链接:https://www.haomeiwen.com/subject/lrvdmhtx.html