Java AES加密和解密教程

 更新时间:2020年12月04日 14:49:05   作者:banq  
这篇文章主要介绍了Java AES加密和解密的相关资料,帮助大家更好的理解和学习Java,感兴趣的朋友可以了解下

在本教程中,我们将看到如何使用JDK中的Java密码体系结构(JCA)来实现AES加密和解密。对称密钥块密码在数据加密中起重要作用。这意味着同一密钥可用于加密和解密。高级加密标准(AES)是一种广泛使用的对称密钥加密算法。

AES算法是一种迭代的对称密钥块密码,它支持128、192和256位的加密密钥(秘密密钥),以对128位的块中的数据进行加密和解密。

在AES中生成密钥的方法有两种:从随机数生成或从给定密码生成。

在第一种方法中,应该从像SecureRandom类这样的加密安全(伪)随机数生成器生成秘密密钥。为了生成密钥,我们可以使用KeyGenerator类。让我们定义一种用于生成大小为n(128、192和256)位的AES密钥的方法:

public static SecretKey generateKey(int n) throws NoSuchAlgorithmException {
    KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
    keyGenerator.init(n);
    SecretKey key = keyGenerator.generateKey();
    return key;
}

在第二种方法中,可以使用基于密码的密钥派生功能(例如PBKDF2)从给定的密码派生AES秘密密钥。下面方法可通过65,536次迭代和256位密钥长度从给定密码生成AES密钥:

public static SecretKey getKeyFromPassword(String password, String salt)
    throws NoSuchAlgorithmException, InvalidKeySpecException {
    
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
    KeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(), 65536, 256);
    SecretKey secret = new SecretKeySpec(factory.generateSecret(spec)
        .getEncoded(), "AES");
    return secret;
}

加密字符串

要实现输入字符串加密,我们首先需要根据上一节生成密钥和初始化向量IV:

IV是伪随机值,其大小与加密的块相同。我们可以使用SecureRandom类生成随机IV。

让我们定义一种生成IV的方法:

public static IvParameterSpec generateIv() {
    byte[] iv = new byte[16];
    new SecureRandom().nextBytes(iv);
    return new IvParameterSpec(iv);
}

下一步,我们使用getInstance()方法从Cipher类创建一个实例。

此外,我们使用带有秘密密钥,IV和加密模式的init()方法配置密码实例。最后,我们通过调用doFinal()方法对输入字符串进行加密。此方法获取输入字节并以字节为单位返回密文:

public static String encrypt(String algorithm, String input, SecretKey key,
    IvParameterSpec iv) throws NoSuchPaddingException, NoSuchAlgorithmException,
    InvalidAlgorithmParameterException, InvalidKeyException,
    BadPaddingException, IllegalBlockSizeException {
    
    Cipher cipher = Cipher.getInstance(algorithm);
    cipher.init(Cipher.ENCRYPT_MODE, key, iv);
    byte[] cipherText = cipher.doFinal(input.getBytes());
    return Base64.getEncoder()
        .encodeToString(cipherText);
}

为了解密输入字符串,我们可以使用DECRYPT_MODE初始化密码来解密内容:

public static String decrypt(String algorithm, String cipherText, SecretKey key,
    IvParameterSpec iv) throws NoSuchPaddingException, NoSuchAlgorithmException,
    InvalidAlgorithmParameterException, InvalidKeyException,
    BadPaddingException, IllegalBlockSizeException {
    
    Cipher cipher = Cipher.getInstance(algorithm);
    cipher.init(Cipher.DECRYPT_MODE, key, iv);
    byte[] plainText = cipher.doFinal(Base64.getDecoder()
        .decode(cipherText));
    return new String(plainText);
}

编写一个用于加密和解密字符串输入的测试方法:

@Test
void givenString_whenEncrypt_thenSuccess()
    throws NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException,
    BadPaddingException, InvalidAlgorithmParameterException, NoSuchPaddingException { 
    
    String input = "baeldung";
    SecretKey key = AESUtil.generateKey(128);
    IvParameterSpec ivParameterSpec = AESUtil.generateIv();
    String algorithm = "AES/CBC/PKCS5Padding";
    String cipherText = AESUtil.encrypt(algorithm, input, key, ivParameterSpec);
    String plainText = AESUtil.decrypt(algorithm, cipherText, key, ivParameterSpec);
    Assertions.assertEquals(input, plainText);
}

加密文件

现在,让我们使用AES算法加密文件。步骤是相同的​​,但是我们需要一些IO类来处理文件。让我们加密一个文本文件:

public static void encryptFile(String algorithm, SecretKey key, IvParameterSpec iv,
    File inputFile, File outputFile) throws IOException, NoSuchPaddingException,
    NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException,
    BadPaddingException, IllegalBlockSizeException {
    
    Cipher cipher = Cipher.getInstance(algorithm);
    cipher.init(Cipher.ENCRYPT_MODE, key, iv);
    FileInputStream inputStream = new FileInputStream(inputFile);
    FileOutputStream outputStream = new FileOutputStream(outputFile);
    byte[] buffer = new byte[64];
    int bytesRead;
    while ((bytesRead = inputStream.read(buffer)) != -1) {
        byte[] output = cipher.update(buffer, 0, bytesRead);
        if (output != null) {
            outputStream.write(output);
        }
    }
    byte[] outputBytes = cipher.doFinal();
    if (outputBytes != null) {
        outputStream.write(outputBytes);
    }
    inputStream.close();
    outputStream.close();
}

请注意,不建议尝试将整个文件(尤其是大文件)读入内存。相反,我们一次加密一个缓冲区。

为了解密文件,我们使用类似的步骤,并使用DECRYPT_MODE初始化密码,如前所述。

再次,让我们定义一个用于加密和解密文本文件的测试方法。在这种方法中,我们从测试资源目录中读取baeldung.txt文件,将其加密为一个名为baeldung.encrypted的文件,然后将该文件解密为一个新文件:

@Test
void givenFile_whenEncrypt_thenSuccess() 
    throws NoSuchAlgorithmException, IOException, IllegalBlockSizeException, 
    InvalidKeyException, BadPaddingException, InvalidAlgorithmParameterException, 
    NoSuchPaddingException {
    
    SecretKey key = AESUtil.generateKey(128);
    String algorithm = "AES/CBC/PKCS5Padding";
    IvParameterSpec ivParameterSpec = AESUtil.generateIv();
    Resource resource = new ClassPathResource("inputFile/baeldung.txt");
    File inputFile = resource.getFile();
    File encryptedFile = new File("classpath:baeldung.encrypted");
    File decryptedFile = new File("document.decrypted");
    AESUtil.encryptFile(algorithm, key, ivParameterSpec, inputFile, encryptedFile);
    AESUtil.decryptFile(
      algorithm, key, ivParameterSpec, encryptedFile, decryptedFile);
    assertThat(inputFile).hasSameTextualContentAs(decryptedFile);
}

基于密码加密解密

我们可以使用从给定密码派生的密钥进行AES加密和解密。

为了生成密钥,我们使用getKeyFromPassword()方法。加密和解密步骤与字符串输入部分中显示的步骤相同。然后,我们可以使用实例化的密码和提供的密钥来执行加密。

让我们写一个测试方法:

@Test
void givenPassword_whenEncrypt_thenSuccess() 
    throws InvalidKeySpecException, NoSuchAlgorithmException, 
    IllegalBlockSizeException, InvalidKeyException, BadPaddingException, 
    InvalidAlgorithmParameterException, NoSuchPaddingException {
    
    String plainText = "www.baeldung.com";
    String password = "baeldung";
    String salt = "12345678";
    IvParameterSpec ivParameterSpec = AESUtil.generateIv();
    SecretKey key = AESUtil.getKeyFromPassword(password,salt);
    String cipherText = AESUtil.encryptPasswordBased(plainText, key, ivParameterSpec);
    String decryptedCipherText = AESUtil.decryptPasswordBased(
      cipherText, key, ivParameterSpec);
    Assertions.assertEquals(plainText, decryptedCipherText);
}

加密对象

为了加密Java对象,我们需要使用SealedObject类。该对象应可序列化。让我们从定义学生类开始:

public class Student implements Serializable {
    private String name;
    private int age;

    // standard setters and getters
}

接下来,让我们加密Student对象:

public static SealedObject encryptObject(String algorithm, Serializable object,
    SecretKey key, IvParameterSpec iv) throws NoSuchPaddingException,
    NoSuchAlgorithmException, InvalidAlgorithmParameterException, 
    InvalidKeyException, IOException, IllegalBlockSizeException {
    
    Cipher cipher = Cipher.getInstance(algorithm);
    cipher.init(Cipher.ENCRYPT_MODE, key, iv);
    SealedObject sealedObject = new SealedObject(object, cipher);
    return sealedObject;
}

稍后可以使用正确的密码解密加密的对象:

public static Serializable decryptObject(String algorithm, SealedObject sealedObject,
    SecretKey key, IvParameterSpec iv) throws NoSuchPaddingException,
    NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException,
    ClassNotFoundException, BadPaddingException, IllegalBlockSizeException,
    IOException {
    
    Cipher cipher = Cipher.getInstance(algorithm);
    cipher.init(Cipher.DECRYPT_MODE, key, iv);
    Serializable unsealObject = (Serializable) sealedObject.getObject(cipher);
    return unsealObject;
}

让我们写一个测试用例:

@Test
void givenObject_whenEncrypt_thenSuccess() 
    throws NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException,
    InvalidAlgorithmParameterException, NoSuchPaddingException, IOException, 
    BadPaddingException, ClassNotFoundException {
    
    Student student = new Student("Baeldung", 20);
    SecretKey key = AESUtil.generateKey(128);
    IvParameterSpec ivParameterSpec = AESUtil.generateIv();
    String algorithm = "AES/CBC/PKCS5Padding";
    SealedObject sealedObject = AESUtil.encryptObject(
      algorithm, student, key, ivParameterSpec);
    Student object = (Student) AESUtil.decryptObject(
      algorithm, sealedObject, key, ivParameterSpec);
    assertThat(student).isEqualToComparingFieldByField(object);
}

可以在GitHub上获得本文的完整源代码 。

以上就是Java AES加密和解密教程的详细内容,更多关于Java AES加密和解密的资料请关注脚本之家其它相关文章!

相关文章

  • Java实现的快速查找算法示例

    Java实现的快速查找算法示例

    这篇文章主要介绍了Java实现的快速查找算法,结合具体实例形式分析了快速查找算法的原理与相关实现技巧,需要的朋友可以参考下
    2017-09-09
  • Java将Exception信息转为String字符串的方法

    Java将Exception信息转为String字符串的方法

    今天小编就为大家分享一篇Java将Exception信息转为String字符串的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-10-10
  • 自定义指定路由上的Gateway过滤器工厂详解

    自定义指定路由上的Gateway过滤器工厂详解

    这篇文章主要介绍了自定义指定路由上的Gateway过滤器工厂详解,gateway是Spring Cloud中的一个网关服务,gateway可以使用服务注册中心进行服务发现和负载均衡,同时还可以配置断言来判断请求是否符合路由规则,需要的朋友可以参考下
    2023-09-09
  • Jmeter连接Mysql数据库实现过程详解

    Jmeter连接Mysql数据库实现过程详解

    这篇文章主要介绍了Jmeter连接Mysql数据库实现过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-08-08
  • SpringBoot服务访问路径动态处理方式

    SpringBoot服务访问路径动态处理方式

    这篇文章主要介绍了SpringBoot服务访问路径动态处理方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • IDEA使用MyBatisCodeHelperPro来generator代码的详细教程

    IDEA使用MyBatisCodeHelperPro来generator代码的详细教程

    这篇文章主要介绍了IDEA使用MyBatisCodeHelperPro来generator代码的详细教程,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-09-09
  • Mybatis复杂查询的实现

    Mybatis复杂查询的实现

    本文主要介绍了Mybatis复杂查询的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-09-09
  • 浅谈Arrays.asList() 和ArrayList类型区别

    浅谈Arrays.asList() 和ArrayList类型区别

    下面小编就为大家带来一篇Arrays.asList() 和ArrayList类型区别。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-10-10
  • Java视频格式转化的实现方法

    Java视频格式转化的实现方法

    这篇文章主要为大家详细介绍了Java视频格式转化的实现方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-02-02
  • JAVA导出EXCEL表格的实例教学

    JAVA导出EXCEL表格的实例教学

    在本文中我们给大家整理了关于JAVA导出EXCEL表格的实例教学以及相关知识点,需要的朋友们学习下。
    2019-02-02

最新评论