Use cases

  • Password based symmetric encryption of a file

Java version

  • openjdk8
  • oraclejdk9
  • openjdk9
  • oraclejdk11
  • openjdk11
  • oraclejdk13
  • openjdk13

Example Code for Java Password based symmetric file encryption using AES-GCM and PBKDF2

package com.cryptoexamples.java;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Base64;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Example for encryption and decryption of a file in one method.
 * - Random password generation using strong secure random number generator
 * - Random salt generation
 * - Key derivation using PBKDF2 HMAC SHA-512,
 * - AES-256 authenticated encryption using GCM
 * - BASE64-encoding as representation for the byte-arrays
 * - Exception handling
 */
public class ExampleFileEncryption {
  private static final Logger LOGGER = Logger.getLogger(ExampleFileEncryption.class.getName());

  /**
   * Demonstrational method that encrypts a file using a password (that is used to derive the required key).
   * @param fileName
   * @param plainText
   * @param password
   * @return true if encryption and decryption were successful, false otherwise
   */
  public static boolean demonstrateFileEncryption(String fileName, String plainText, String password) {
    try {
      // GENERATE password (not needed if you have a password already)
      if(password == null || password.isEmpty()) {
        KeyGenerator keyGen = KeyGenerator.getInstance("AES");
        keyGen.init(256);
        password = Base64.getEncoder().encodeToString(keyGen.generateKey().getEncoded());
      }

      // GENERATE random salt
      final byte[] salt = new byte[64];
      SecureRandom random = SecureRandom.getInstanceStrong();
      random.nextBytes(salt);

      // DERIVE key (from password and salt)
      SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
      KeySpec passwordBasedEncryptionKeySpec = new PBEKeySpec(password.toCharArray(), salt, 10000, 256);
      SecretKey secretKeyFromPBKDF2 = secretKeyFactory.generateSecret(passwordBasedEncryptionKeySpec);
      SecretKey key = new SecretKeySpec(secretKeyFromPBKDF2.getEncoded(), "AES");

      // GENERATE random nonce (number used once)
      final byte[] nonce = new byte[32];
      random.nextBytes(nonce);

      // SET UP CIPHER for encryption
      Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
      GCMParameterSpec spec = new GCMParameterSpec(16 * 8, nonce);
      cipher.init(Cipher.ENCRYPT_MODE, key, spec);

      // TODO store encryption parameters as authenticated data prepended to the file content

      // SET UP OUTPUT STREAM and write content of String
      try (
        FileOutputStream fileOutputStream = new FileOutputStream(fileName);
        CipherOutputStream encryptedOutputStream = new CipherOutputStream(fileOutputStream, cipher);
        InputStream stringInputStream = new ByteArrayInputStream(plainText.getBytes(StandardCharsets.UTF_8))
      ) {
        byte[] buffer = new byte[8192];
        int nread;
        while ((nread = stringInputStream.read(buffer)) > 0) {
          encryptedOutputStream.write(buffer, 0, nread);
        }
        encryptedOutputStream.flush();
      }

      // READ ENCRYPTED FILE
      StringBuilder stringBuilder = new StringBuilder();
      cipher.init(Cipher.DECRYPT_MODE, key, spec);
      String decryptedCipherText;
      try (
        FileInputStream fileInputStream = new FileInputStream(fileName);
        CipherInputStream cipherInputStream = new CipherInputStream(fileInputStream, cipher);
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
      ) {
        byte[] buffer = new byte[8192];
        int nread;
        while ((nread = cipherInputStream.read(buffer)) > 0) {
          byteArrayOutputStream.write(buffer, 0, nread);
        }
        byteArrayOutputStream.flush();
        decryptedCipherText = new String(byteArrayOutputStream.toByteArray(), StandardCharsets.UTF_8);
      }

      LOGGER.log(Level.INFO, decryptedCipherText);
      LOGGER.log(Level.INFO,
              () -> String.format("Decrypted file content and original plain text are the same: %b",
                      decryptedCipherText.compareTo(plainText) == 0)
      );
      return decryptedCipherText.compareTo(plainText) == 0;
    } catch (NoSuchAlgorithmException |
            NoSuchPaddingException |
            InvalidKeyException |
            InvalidParameterException |
            InvalidAlgorithmParameterException |
            InvalidKeySpecException |
            IOException e) {
      LOGGER.log(Level.SEVERE, e.getLocalizedMessage());
      return false;
    }
  }


  public static void main(String[] args) {
    demonstrateFileEncryption("encryptedFile.enc","Multiline text:\nMultiline text:\n",null );
  }

}

References

Authors

Kai Mindermann

Reviews