Main Tutorials

Java AES encryption and decryption

AES encryption

The Advanced Encryption Standard (AES, Rijndael) is a block cipher encryption and decryption algorithm, the most used encryption algorithm in the worldwide. The AES processes block of 128 bits using a secret key of 128, 192, or 256 bits.

This article shows you a few of Java AES encryption and decryption examples:

  • AES String encryption – (encrypt and decrypt a string).
  • AES Password-based encryption – (The secret key will derive from a given password).
  • AES File encryption. (password-based).

In this article, we are focus on the 256-bit AES encryption with Galois Counter Mode (GCM).


GCM = CTR + Authentication.

Further Reading
Read this – NIST – Recommendation for Galois/Counter Mode (GCM)

Don’t use AES Electronic codebook (ECB) Mode
The AES ECB mode, or AES/ECB/PKCS5Padding (in Java) is not semantically secure – The ECB-encrypted ciphertext can leak information about the plaintext. Here is a discussion about Why shouldn’t I use ECB encryption?

1. Java and AES encryption inputs.

In AES encryption and decryption, we need the following inputs:

AES encryption best practice
Don’t reuse IV with the same key.

1.1 The IV (initial value or initial vector), it is random bytes, typically 12 bytes or 16 bytes. In Java, we can use SecureRandom to generate the random IV.


  // 16 bytes IV
  public static byte[] getRandomNonce() {
        byte[] nonce = new byte[16];
        new SecureRandom().nextBytes(nonce);
        return nonce;
  }

  // 12 bytes IV
  public static byte[] getRandomNonce() {
        byte[] nonce = new byte[12];
        new SecureRandom().nextBytes(nonce);
        return nonce;
  }

1.2 The AES secret key, either AES-128 or AES-256. In Java, we can use KeyGenerator to generate the AES secret key.


    // 256 bits AES secret key
    public static SecretKey getAESKey() throws NoSuchAlgorithmException {
        KeyGenerator keyGen = KeyGenerator.getInstance("AES");
        keyGen.init(256, SecureRandom.getInstanceStrong());
        return keyGen.generateKey();
    }

1.3 The AES secret key that derived from a given password. In Java, we can use the SecretKeyFactory and PBKDF2WithHmacSHA256 to generate an AES key from a given password.


    // AES key derived from a password
    public static SecretKey getAESKeyFromPassword(char[] password, byte[] salt)
            throws NoSuchAlgorithmException, InvalidKeySpecException {

        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        // iterationCount = 65536
        // keyLength = 256
        KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
        SecretKey secret = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
        return secret;
    }

We use salt to protect rainbow attacks, and it is also a random byte, we can use the same 1.1 getRandomNonce to generate it.

1.4 We group the above methods into a single util class, so that we won’t repeat the same code again and again.

CryptoUtils.java

package com.mkyong.crypto.utils;

import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.ArrayList;
import java.util.List;

public class CryptoUtils {

    public static byte[] getRandomNonce(int numBytes) {
        byte[] nonce = new byte[numBytes];
        new SecureRandom().nextBytes(nonce);
        return nonce;
    }

    // AES secret key
    public static SecretKey getAESKey(int keysize) throws NoSuchAlgorithmException {
        KeyGenerator keyGen = KeyGenerator.getInstance("AES");
        keyGen.init(keysize, SecureRandom.getInstanceStrong());
        return keyGen.generateKey();
    }

    // Password derived AES 256 bits secret key
    public static SecretKey getAESKeyFromPassword(char[] password, byte[] salt)
            throws NoSuchAlgorithmException, InvalidKeySpecException {

        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        // iterationCount = 65536
        // keyLength = 256
        KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
        SecretKey secret = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
        return secret;

    }

    // hex representation
    public static String hex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte b : bytes) {
            result.append(String.format("%02x", b));
        }
        return result.toString();
    }

    // print hex with block size split
    public static String hexWithBlockSize(byte[] bytes, int blockSize) {

        String hex = hex(bytes);

        // one hex = 2 chars
        blockSize = blockSize * 2;

        // better idea how to print this?
        List<String> result = new ArrayList<>();
        int index = 0;
        while (index < hex.length()) {
            result.add(hex.substring(index, Math.min(index + blockSize, hex.length())));
            index += blockSize;
        }

        return result.toString();

    }

}

2. AES encryption and decryption.

The AES-GSM is the most widely used authenticated cipher. This example will encrypt and decrypt a string using 256-bit AES in Galois Counter Mode (GCM).

The AES-GCM inputs:

  • AES Secret key (256 bits)
  • IV – 96 bits (12 bytes)
  • Length (in bits) of authentication tag – 128 bits (16 bytes)

2.1 In Java, we use AES/GCM/NoPadding to represent the AES-GCM algorithm. For the encrypted output, we prefix the 16 bytes IV to the encrypted text (ciphertext), because we need the same IV for decryption.

Is this ok if IV is publicly known?
It is ok for IV to be publicly known, the only secret is the key, keep it private and confidential.

This example will use AES to encrypt a plain text Hello World AES-GCM and later decrypt it back to the original plain text.

EncryptorAesGcm.java

package com.mkyong.crypto.encryptor;

import com.mkyong.crypto.utils.CryptoUtils;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

/**
 * AES-GCM inputs - 12 bytes IV, need the same IV and secret keys for encryption and decryption.
 * <p>
 * The output consist of iv, encrypted content, and auth tag in the following format:
 * output = byte[] {i i i c c c c c c ...}
 * <p>
 * i = IV bytes
 * c = content bytes (encrypted content, auth tag)
 */
public class EncryptorAesGcm {

    private static final String ENCRYPT_ALGO = "AES/GCM/NoPadding";
    private static final int TAG_LENGTH_BIT = 128;
    private static final int IV_LENGTH_BYTE = 12;
    private static final int AES_KEY_BIT = 256;

    private static final Charset UTF_8 = StandardCharsets.UTF_8;

    // AES-GCM needs GCMParameterSpec
    public static byte[] encrypt(byte[] pText, SecretKey secret, byte[] iv) throws Exception {

        Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO);
        cipher.init(Cipher.ENCRYPT_MODE, secret, new GCMParameterSpec(TAG_LENGTH_BIT, iv));
        byte[] encryptedText = cipher.doFinal(pText);
        return encryptedText;

    }

    // prefix IV length + IV bytes to cipher text
    public static byte[] encryptWithPrefixIV(byte[] pText, SecretKey secret, byte[] iv) throws Exception {

        byte[] cipherText = encrypt(pText, secret, iv);

        byte[] cipherTextWithIv = ByteBuffer.allocate(iv.length + cipherText.length)
                .put(iv)
                .put(cipherText)
                .array();
        return cipherTextWithIv;

    }

    public static String decrypt(byte[] cText, SecretKey secret, byte[] iv) throws Exception {

        Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO);
        cipher.init(Cipher.DECRYPT_MODE, secret, new GCMParameterSpec(TAG_LENGTH_BIT, iv));
        byte[] plainText = cipher.doFinal(cText);
        return new String(plainText, UTF_8);

    }

    public static String decryptWithPrefixIV(byte[] cText, SecretKey secret) throws Exception {

        ByteBuffer bb = ByteBuffer.wrap(cText);

        byte[] iv = new byte[IV_LENGTH_BYTE];
        bb.get(iv);
        //bb.get(iv, 0, iv.length);

        byte[] cipherText = new byte[bb.remaining()];
        bb.get(cipherText);

        String plainText = decrypt(cipherText, secret, iv);
        return plainText;

    }

    public static void main(String[] args) throws Exception {

        String OUTPUT_FORMAT = "%-30s:%s";

        String pText = "Hello World AES-GCM, Welcome to Cryptography!";

        // encrypt and decrypt need the same key.
        // get AES 256 bits (32 bytes) key
        SecretKey secretKey = CryptoUtils.getAESKey(AES_KEY_BIT);

        // encrypt and decrypt need the same IV.
        // AES-GCM needs IV 96-bit (12 bytes)
        byte[] iv = CryptoUtils.getRandomNonce(IV_LENGTH_BYTE);

        byte[] encryptedText = EncryptorAesGcm.encryptWithPrefixIV(pText.getBytes(UTF_8), secretKey, iv);

        System.out.println("\n------ AES GCM Encryption ------");
        System.out.println(String.format(OUTPUT_FORMAT, "Input (plain text)", pText));
        System.out.println(String.format(OUTPUT_FORMAT, "Key (hex)", CryptoUtils.hex(secretKey.getEncoded())));
        System.out.println(String.format(OUTPUT_FORMAT, "IV  (hex)", CryptoUtils.hex(iv)));
        System.out.println(String.format(OUTPUT_FORMAT, "Encrypted (hex) ", CryptoUtils.hex(encryptedText)));
        System.out.println(String.format(OUTPUT_FORMAT, "Encrypted (hex) (block = 16)", CryptoUtils.hexWithBlockSize(encryptedText, 16)));

        System.out.println("\n------ AES GCM Decryption ------");
        System.out.println(String.format(OUTPUT_FORMAT, "Input (hex)", CryptoUtils.hex(encryptedText)));
        System.out.println(String.format(OUTPUT_FORMAT, "Input (hex) (block = 16)", CryptoUtils.hexWithBlockSize(encryptedText, 16)));
        System.out.println(String.format(OUTPUT_FORMAT, "Key (hex)", CryptoUtils.hex(secretKey.getEncoded())));

        String decryptedText = EncryptorAesGcm.decryptWithPrefixIV(encryptedText, secretKey);

        System.out.println(String.format(OUTPUT_FORMAT, "Decrypted (plain text)", decryptedText));

    }

}

Output

Plain Text : Hello World AES-GCM

Terminal

------ AES GCM Encryption ------
Input (plain text)            :Hello World AES-GCM
Key (hex)                     :603d87185bf855532f14a77a91ec7b025c004bf664e9f5c6e95613ee9577f436
IV  (hex)                     :bdb271ce5235996a0709e09c
Encrypted (hex)               :bdb271ce5235996a0709e09c2d03eefe319e9329768724755c56291aecaef88cd1e6bdf72b8c7b54d75a94e66b0cd3
Encrypted (hex) (block = 16)  :[bdb271ce5235996a0709e09c2d03eefe, 319e9329768724755c56291aecaef88c, d1e6bdf72b8c7b54d75a94e66b0cd3]

------ AES GCM Decryption ------
Input (hex)                   :bdb271ce5235996a0709e09c2d03eefe319e9329768724755c56291aecaef88cd1e6bdf72b8c7b54d75a94e66b0cd3
Input (hex) (block = 16)      :[bdb271ce5235996a0709e09c2d03eefe, 319e9329768724755c56291aecaef88c, d1e6bdf72b8c7b54d75a94e66b0cd3]
Key (hex)                     :603d87185bf855532f14a77a91ec7b025c004bf664e9f5c6e95613ee9577f436
Decrypted (plain text)        :Hello World AES-GCM

Plain Text : Hello World AES-GCM, Welcome to Cryptography!

Terminal

------ AES GCM Encryption ------
Input (plain text)            :Hello World AES-GCM, Welcome to Cryptography!
Key (hex)                     :ddc24663d104e1c2f81f11aef98156503dafdc435f81e3ac3d705015ebab095c
IV  (hex)                     :b05d6aedf023f73b9e1e2d11
Encrypted (hex)               :b05d6aedf023f73b9e1e2d11f6f5137d971aea8c5cdd5b045e0960eb4408e0ee4635cccc2dfeec2c13a89bd400f659be82dc2329e9c36e3b032f38bd42296a8495ac840b0625c097d9
Encrypted (hex) (block = 16)  :[b05d6aedf023f73b9e1e2d11f6f5137d, 971aea8c5cdd5b045e0960eb4408e0ee, 4635cccc2dfeec2c13a89bd400f659be, 82dc2329e9c36e3b032f38bd42296a84, 95ac840b0625c097d9]

------ AES GCM Decryption ------
Input (hex)                   :b05d6aedf023f73b9e1e2d11f6f5137d971aea8c5cdd5b045e0960eb4408e0ee4635cccc2dfeec2c13a89bd400f659be82dc2329e9c36e3b032f38bd42296a8495ac840b0625c097d9
Input (hex) (block = 16)      :[b05d6aedf023f73b9e1e2d11f6f5137d, 971aea8c5cdd5b045e0960eb4408e0ee, 4635cccc2dfeec2c13a89bd400f659be, 82dc2329e9c36e3b032f38bd42296a84, 95ac840b0625c097d9]
Key (hex)                     :ddc24663d104e1c2f81f11aef98156503dafdc435f81e3ac3d705015ebab095c
Decrypted (plain text)        :Hello World AES-GCM, Welcome to Cryptography!

3. AES Password-Based encryption and decryption.

For password-based encryption, we can use the Password-Based Cryptography Specification (PKCS), defined RFC 8018, to generate a key from a given password.

For PKCS inputs:

  • Password, you provide this.
  • Salt – At least 64 bits (8 bytes) random bytes.
  • Iteration Count – Recommend a minimum iteration count of 1,000.

What is salt and iteration count?

  • The salt produces a broad set of keys for a given password. For example, if the salt is 128 bits, there will be as many as 2^128 keys for each password. Therefore, it increases the difficulty of rainbow attacks. Furthermore, the rainbow table that attackers build for one user’s password became useless for another user.
  • The iteration count increasing the cost of producing keys from a password, therefore increasing difficulty and slow down the speed of attacks.

3.1 For the encrypted output, we prefix the 12 bytes IV and password salt to the ciphertext, because we need the same IV and password salt (for secret key) for decryption. Furthermore, we use Base64 encoder to encode the encrypted text into a string representation, so that we can send the encrypted text or ciphertext in string format (was byte array).

Is this ok if password salt is publicly known?
It is the same with IV, and it is ok for password salt to be publicly known, the only secret is the key, keeping it private and confidential.

EncryptorAesGcmPassword.java

package com.mkyong.crypto.encryptor;

import com.mkyong.crypto.utils.CryptoUtils;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

/**
 * AES-GCM inputs - 12 bytes IV, need the same IV and secret keys for encryption and decryption.
 * <p>
 * The output consist of iv, password's salt, encrypted content and auth tag in the following format:
 * output = byte[] {i i i s s s c c c c c c ...}
 * <p>
 * i = IV bytes
 * s = Salt bytes
 * c = content bytes (encrypted content)
 */
public class EncryptorAesGcmPassword {

    private static final String ENCRYPT_ALGO = "AES/GCM/NoPadding";

    private static final int TAG_LENGTH_BIT = 128; // must be one of {128, 120, 112, 104, 96}
    private static final int IV_LENGTH_BYTE = 12;
    private static final int SALT_LENGTH_BYTE = 16;
    private static final Charset UTF_8 = StandardCharsets.UTF_8;

    // return a base64 encoded AES encrypted text
    public static String encrypt(byte[] pText, String password) throws Exception {

        // 16 bytes salt
        byte[] salt = CryptoUtils.getRandomNonce(SALT_LENGTH_BYTE);

        // GCM recommended 12 bytes iv?
        byte[] iv = CryptoUtils.getRandomNonce(IV_LENGTH_BYTE);

        // secret key from password
        SecretKey aesKeyFromPassword = CryptoUtils.getAESKeyFromPassword(password.toCharArray(), salt);

        Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO);

        // ASE-GCM needs GCMParameterSpec
        cipher.init(Cipher.ENCRYPT_MODE, aesKeyFromPassword, new GCMParameterSpec(TAG_LENGTH_BIT, iv));

        byte[] cipherText = cipher.doFinal(pText);

        // prefix IV and Salt to cipher text
        byte[] cipherTextWithIvSalt = ByteBuffer.allocate(iv.length + salt.length + cipherText.length)
                .put(iv)
                .put(salt)
                .put(cipherText)
                .array();

        // string representation, base64, send this string to other for decryption.
        return Base64.getEncoder().encodeToString(cipherTextWithIvSalt);

    }

    // we need the same password, salt and iv to decrypt it
    private static String decrypt(String cText, String password) throws Exception {

        byte[] decode = Base64.getDecoder().decode(cText.getBytes(UTF_8));

        // get back the iv and salt from the cipher text
        ByteBuffer bb = ByteBuffer.wrap(decode);

        byte[] iv = new byte[IV_LENGTH_BYTE];
        bb.get(iv);

        byte[] salt = new byte[SALT_LENGTH_BYTE];
        bb.get(salt);

        byte[] cipherText = new byte[bb.remaining()];
        bb.get(cipherText);

        // get back the aes key from the same password and salt
        SecretKey aesKeyFromPassword = CryptoUtils.getAESKeyFromPassword(password.toCharArray(), salt);

        Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO);

        cipher.init(Cipher.DECRYPT_MODE, aesKeyFromPassword, new GCMParameterSpec(TAG_LENGTH_BIT, iv));

        byte[] plainText = cipher.doFinal(cipherText);

        return new String(plainText, UTF_8);

    }

    public static void main(String[] args) throws Exception {

        String OUTPUT_FORMAT = "%-30s:%s";
        String PASSWORD = "this is a password";
        String pText = "AES-GSM Password-Bases encryption!";

        String encryptedTextBase64 = EncryptorAesGcmPassword.encrypt(pText.getBytes(UTF_8), PASSWORD);

        System.out.println("\n------ AES GCM Password-based Encryption ------");
        System.out.println(String.format(OUTPUT_FORMAT, "Input (plain text)", pText));
        System.out.println(String.format(OUTPUT_FORMAT, "Encrypted (base64) ", encryptedTextBase64));

        System.out.println("\n------ AES GCM Password-based Decryption ------");
        System.out.println(String.format(OUTPUT_FORMAT, "Input (base64)", encryptedTextBase64));

        String decryptedText = EncryptorAesGcmPassword.decrypt(encryptedTextBase64, PASSWORD);
        System.out.println(String.format(OUTPUT_FORMAT, "Decrypted (plain text)", decryptedText));

    }

}

Output

Terminal

------ AES GCM Password-based Encryption ------
Input (plain text)            :AES-GSM Password-Bases encryption!
Encrypted (base64)            :KmrvjnMusJTQo/hB7T5BvlQpvi3bVbdjpZP51NT7I/enrIfSQuDfSK6iXgdPzvUP2IE54mwrKiyHqMkG8224lRZ9tXHcclmdh98I8b3B

------ AES GCM Password-based Decryption ------
Input (base64)                :KmrvjnMusJTQo/hB7T5BvlQpvi3bVbdjpZP51NT7I/enrIfSQuDfSK6iXgdPzvUP2IE54mwrKiyHqMkG8224lRZ9tXHcclmdh98I8b3B
Decrypted (plain text)        :AES-GSM Password-Bases encryption!

3.2 If password is not match, Java throws AEADBadTagException: Tag mismatch!


  // change the password to something else
  String decryptedText = EncryptorAesGcmPassword.decrypt(encryptedTextBase64, "other password");
  System.out.println(String.format(OUTPUT_FORMAT, "Decrypted (plain text)", decryptedText));

Output

Terminal

Exception in thread "main" javax.crypto.AEADBadTagException: Tag mismatch!
  at java.base/com.sun.crypto.provider.GaloisCounterMode.decryptFinal(GaloisCounterMode.java:623)
  at java.base/com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:1118)
  at java.base/com.sun.crypto.provider.CipherCore.fillOutputBuffer(CipherCore.java:1055)
  at java.base/com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:855)
  at java.base/com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
  at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2207)
  at com.mkyong.crypto.encryptor.EncryptorAesGcmPassword.decrypt(EncryptorAesGcmPassword.java:88)
  at com.mkyong.crypto.encryptor.EncryptorAesGcmPassword.main(EncryptorAesGcmPassword.java:109)

4. AES File encryption and decryption.

This example is an AES password-based file encryption. The ideas are the same, but we need some IO classes to work with the resources or files.

Here’s a text file, at the resources folder.

readme.txt

This is line 1.
This is line 2.
This is line 3.
This is line 4.
This is line 5.
This is line 9.
This is line 10.

4.1 This example is similar to 3.1 EncryptorAesGcmPassword.java, with some minor changes like return a byte[] instead of base64 encoded string.


  public static byte[] encrypt(byte[] pText, String password) throws Exception {

        //...

        // prefix IV and Salt to cipher text
        byte[] cipherTextWithIvSalt = ByteBuffer.allocate(iv.length + salt.length + cipherText.length)
                .put(iv)
                .put(salt)
                .put(cipherText)
                .array();

        // it works, even if we save the based64 encoded string into a file.
        // return Base64.getEncoder().encodeToString(cipherTextWithIvSalt);

        // we save the byte[] into a file.
        return cipherTextWithIvSalt;

    }

Add encryptFile and decryptFile to work with the file.


    public static void encryptFile(String fromFile, String toFile, String password) throws Exception {

        // read a normal txt file
        byte[] fileContent = Files.readAllBytes(Paths.get(ClassLoader.getSystemResource(fromFile).toURI()));

        // encrypt with a password
        byte[] encryptedText = EncryptorAesGcmPasswordFile.encrypt(fileContent, password);

        // save a file
        Path path = Paths.get(toFile);

        Files.write(path, encryptedText);

    }

    public static byte[] decryptFile(String fromEncryptedFile, String password) throws Exception {

        // read a file
        byte[] fileContent = Files.readAllBytes(Paths.get(fromEncryptedFile));

        return EncryptorAesGcmPasswordFile.decrypt(fileContent, password);

    }

4.2 Read the above readme.txt file from the classpath, encrypt it, and the encrypted data to a new file c:\test\readme.encrypted.txt.


  String password = "password123";
  String fromFile = "readme.txt"; // from resources folder
  String toFile = "c:\\test\\readme.encrypted.txt";

  // encrypt file
  EncryptorAesGcmPasswordFile.encryptFile(fromFile, toFile, password);

Output

AES file encryption

4.3 Read the encrypted file, decrypt it, and print the output.


  String password = "password123";
  String toFile = "c:\\test\\readme.encrypted.txt";

  // decrypt file
  byte[] decryptedText = EncryptorAesGcmPasswordFile.decryptFile(toFile, password);
  String pText = new String(decryptedText, UTF_8);
  System.out.println(pText);

Output

Terminal

This is line 1.
This is line 2.
This is line 3.
This is line 4.
This is line 5.
This is line 9.
This is line 10.

P.S The AES image encryption is the same concept.

Download Source Code

$ git clone https://github.com/mkyong/core-java

$ cd java-crypto

Let me know if the article needs improvement. Thanks.

References

About Author

author image
Founder of Mkyong.com, love Java and open source stuff. Follow him on Twitter. If you like my tutorials, consider make a donation to these charities.

Comments

Subscribe
Notify of
35 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments
Lara
1 year ago

Hi i have these errors, could you help

Exception in thread “main” java.lang.NullPointerException: Cannot invoke “java.net.URL.toURI()” because the return value of “java.lang.ClassLoader.getSystemResource(String)” is null
    at AES.EncryptorAesGcmPasswordFile.encryptFile(EncryptorAesGcmPasswordFile.java:93)
    at AES.EncryptorAesGcmPasswordFile.main(EncryptorAesGcmPasswordFile.java:121)

Duraiarasan Jayaraman
24 days ago

Can we able to get AES key by having only iv key and cipher key

Jean-Charles
10 months ago

Very nice, clear and useful article.
In my code, using buffer I got an exception: javax.crypto.AEADBadTagException: Tag mismatch! when I use update during the decryption. My code works fine with AES/CTR and works well with AES/GCM when there is only one doFinal. Any idea?

Hareesh
1 year ago

Hello mkyonng
Is there any way we can do encryption of a file in javascript with crypto js AES and decrypt the same in Java with AES?

I am able to encrypt the file but not able to decrypt the file in java side

Rachna
1 year ago

Hi, Thanks for saving my day. After spending the whole day. I got this solution. which makes my day

Michael Fehr
3 years ago

Very good article, thanks! Just one typo: “2.1 In Java, we use AES/CBC/PKCS5Padding to represent the AES-GCM algorithm.” I think it should be “2.1 In Java, we use AES/GCM/NoPadding to represent the AES-GCM algorithm.”

Dan
2 years ago

This is great, I have made a modified version of this using CipherInputStream/CipherOutputStream. My question is, would it not be more flexible when writing the iv/salt during encryption process to also write out their bit depth, i.e. [IV-LENGTH-BYTES][IV-VALUE][SALT_LENGTH_BYTES][SALT_VALUE], this way, when it comes time to decode, it can be decoded with a ‘different’ version of the code, that changed the encryption details. I suppose by that logic this could be extended to include ‘password-iterations/key_length), but that may reveal too much, and be come security issue?

Menuka
2 years ago

Hello Mkyong,

Thank you very much for the article. I would like to know that a file encrypted in this method in java can be decrypted in Python. If it’s possible how can we find the key which is used to encrypt the file?

Frank
2 years ago

Veracode is flagging a couple of things here. one thing it flags is the the 12 byte IV — it wants to see 96. I think it sees it as each bit being stored in each byte. It’s also flagging the iterationCount… they recommend 200000 iterations or more.

Frank
2 years ago
Reply to  Frank

See article at https://www.veracode.com/blog/research/encryption-and-decryption-java-cryptography
And corresponding example: https://github.com/1MansiS/java_crypto/blob/master/cipher/SecuredGCMUsage.java

It could that Veracode has it wrong… all other examples I see on the Web use 12 bytes like you. I’d be curious to get your thoughts mkyong. Thank you.

Frank
2 years ago

Thanks for the practical example. FYI, I scanned this code with veracode and it recommends using IV tLen of size 96 and iteration count of 200,000 or more.

Tushar
2 years ago

Hi mkyong,

I am using this method to Encrypt and Decrypt my data into DB, but i am facing one issue. While searching for data i am not able to retrieve it. because every time encryption is generating different string and what i have already saved in DB is different.
Let me give one example :- while adding an document into repository, I am encrypting document’s title, but while i am searching, in a search criteria i am entering plain text(Document’s title) which i have entered at the time of adding document into repository. But i am getting different encrypt string for that. so i am unable to fetch my document. Can you please give some solution on that?

Last edited 2 years ago by Tushar
Luke
3 years ago

First of all, thank you for all this explanation.
i Have a problem, i want to use AES file encryption and decryption but i want to generate et secret key automatically…i don’t want to add it by myself.
Please How can i do it ?

Luke-Skywalker
2 years ago
Reply to  Luke

He already did it on another project…You just have to use key.generator with random number.

Siva
3 years ago

Should this be in a singleton implementation – SecretKeyFactory factory = SecretKeyFactory.getInstance(“PBKDF2WithHmacSHA256”);
Looks like this takes a bit of time to get instantiated.

Mato
3 years ago

Very useful. Thank you very much. I’m new to Java, so I’m sorry if my question is wrong. How to verify when decoding a file if an incorrect password is entered. I know that it will make an exception, but how to compare in this example if password is correct?

Mato
3 years ago
Reply to  Mato

I’ve already found a solution. Thank you.

Giannis
3 years ago

Hi, my question is a bit stupid. The import com.mkyong.crypto.utils.CryptoUtils; says that doesn’t exist, and I’m trying for couple of hours to find how to download it or something, and I can’t find anything. Please someone do me a favor and help me out. Have a great day.

President James K. Polk
3 years ago

A very good article. I have one minor suggestion. I would not use SecureRandom.getInstanceStrong() to generate symmetric keys in most scenarios. The “strong” SecureRandom instances are designed to provide long-lived secrets, and therefore are allowed to be much slower. For a single use AES key, it is probably best to just use new SecureRandom().

Rishabh Singhal
3 years ago

Very Nice and Clear Article. How can we use AES for XML specifically. If possible can you please publish a tutorial about it.

Xyz
3 years ago

How to run I have 8 error

viveknath
3 years ago

hi mykong,

Great Article!
Does AES – CCM algorithm works in the same way?
Do you have any inputs for interaction with BLE security layers for example?

Erik Sanchez
3 years ago

Hi mykong,
If i need to encrypt, i followed the implementation as step 2 showed, but later I need to decrypt from outside this method.
The idea is to generate this encrypted value and later from an outside method, decrypt the values.
In step 2 you use

public static SecretKey getAESKey(int keysize) 

And I am able to decrypt but only having the SecretKey, that was inside this method generated.
If an outside method (another API) would like to decrypt this values, how can i get the SecretKey value in order to get the info.

Thank you for your help. I would assume that using the method instead of the previous one

    public static SecretKey getAESKeyFromPassword(char[] password, byte[] salt)

But not sure if my thinking is right.

Erik Sanchez
3 years ago
Reply to  Erik Sanchez

i know we have Base64 example, but we are looking to do this but with AES-256, because its more secure.

michael
3 years ago

I do not understand what pText and cText are for the AES Password-Based encryption and decryption

michael
3 years ago
Reply to  mkyong

thank you, and the password is the secret key 😀

kingk
3 years ago

SHA1PRNG, there is a SeedGenerator which does various things depending on the configuration.

If java.security.egd or securerandom.source point to “file:/dev/random” or “file:/dev/urandom”, we will use NativeSeedGenerator, which calls super() which calls SeedGenerator.URLSeedGenerator(/dev/random). (A nested class within SeedGenerator.) The only things that changed in this bug was that urandom will also trigger use of this code path.

If those properties point to another URL that exists, we’ll initialize SeedGenerator.URLSeedGenerator(url). This is why “file:///dev/urandom”, “file:/./dev/random”, etc. will work.

SecureRandom.getInstanceStrong()

kingk
3 years ago

SecureRandom. GetInstanceStrong () will cause congestion
n SHA1PRNG, there is a SeedGenerator which does various things depending on the configuration.

If java.security.egd or securerandom.source point to “file:/dev/random” or “file:/dev/urandom”, we will use NativeSeedGenerator, which calls super() which calls SeedGenerator.URLSeedGenerator(/dev/random). (A nested class within SeedGenerator.) The only things that changed in this bug was that urandom will also trigger use of this code path.

If those properties point to another URL that exists, we’ll initialize SeedGenerator.URLSeedGenerator(url). This is why “file:///dev/urandom”, “file:/./dev/random”, etc. will work.

Vlad VALICA
3 years ago

Thanks for this article!
One typo in
2. AES encryption and decryption.
The AES-GSM is the most widely used authenticated cipher.
That should be AES-GCM
The initialisation vector for GCM shall have 12bit length, you can remove the question mark in the code sample.
Thanks again for your explicit and clear example!