Java AES加密和解密教程

网友投稿 285 2023-02-15

Java AES加密和解密教程

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

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

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

在第一种方法中,应该从像SecureRandom类这样的加密安全(伪)随机数生成器生成秘密密钥。hdhhYJT为了生成密钥,我们可以使用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();

    ResohdhhYJTurce 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 = "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 sthttp://atic 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加密和解密的资料请关注我们其它相关文章!

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:解决jmap命令打印JVM堆信息异常的问题
下一篇:Zookeeper全局唯一ID生成方案解析
相关文章

 发表评论

暂时没有评论,来抢沙发吧~