Mike Barkmin

Passwortbasierte AES-Verschlüsselung in Java

Hier ein Weg wie man die AES-Verschlüsselung mit Javas hauseigenen Klassen umsetzten kann. Zur Verschlüsselung wird ein IvParameterSpec und ein Salt benötigt. Da diese beim ver- und entschlüsseln identisch sein müssen, müssen diese in Programm gespeichert oder direkt im verschlüsselten Text gespeichert werden.

Der nachfolgende Quelltext nutzt die ersten 16-bits für die IvParameterSpec, die zweiten 16-bits für den Salt und die restlichen Bits für den verschlüsselten Text. So wird nur noch das Password zum Entschlüsseln benötigt.

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Base64;
import java.util.Scanner;

public class Cipher {

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

    public static String encrypt(String plainText, String password)
            throws NoSuchPaddingException, NoSuchAlgorithmException,
                   InvalidAlgorithmParameterException, InvalidKeyException,
                   BadPaddingException, IllegalBlockSizeException,
                   InvalidKeySpecException, UnsupportedEncodingException {
        SecureRandom secureRandom = new SecureRandom();

        byte[] iv = new byte[16];
        secureRandom.nextBytes(iv);
        IvParameterSpec ivSpec = new IvParameterSpec(iv);

        byte[] salt = new byte[16];
        secureRandom.nextBytes(salt);

        SecretKey key = getKeyFromPassword(password, salt);
        javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, key, ivSpec);
        byte[] cipherText = cipher.doFinal(plainText.getBytes("UTF-8"));

        byte[] encryptedData = new byte[iv.length + salt.length + cipherText.length];
        System.arraycopy(iv, 0, encryptedData, 0, iv.length);
        System.arraycopy(salt, 0, encryptedData, iv.length, salt.length);
        System.arraycopy(cipherText, 0, encryptedData, iv.length + salt.length, cipherText.length);
        return Base64.getEncoder()
                     .encodeToString(encryptedData);
    }

    public static String decrypt(String encryptedText, String password)
            throws NoSuchPaddingException, NoSuchAlgorithmException,
                   InvalidAlgorithmParameterException,
                   InvalidKeyException, BadPaddingException,
                   IllegalBlockSizeException, InvalidKeySpecException,
                   UnsupportedEncodingException {
        byte[] encryptedData = Base64.getDecoder().decode(encryptedText);

        byte[] iv = new byte[16];
        System.arraycopy(encryptedData, 0, iv, 0, iv.length);
        IvParameterSpec ivSpec = new IvParameterSpec(iv);

        byte[] salt = new byte[16];
        System.arraycopy(encryptedData, iv.length, salt, 0, salt.length);

        SecretKey key = getKeyFromPassword(password, salt);
        javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5PADDING");
        cipher.init(javax.crypto.Cipher.DECRYPT_MODE, key, ivSpec);

        byte[] cipherText = new byte[encryptedData.length - 16 - 16];
        System.arraycopy(encryptedData, 32, cipherText, 0, cipherText.length);

        byte[] decryptedText = cipher.doFinal(cipherText);
        return new String(decryptedText, "UTF-8");
    }

    public static void main(String[] args)
            throws InvalidAlgorithmParameterException, NoSuchPaddingException,
                   IllegalBlockSizeException, UnsupportedEncodingException,
                   NoSuchAlgorithmException, BadPaddingException,
                   InvalidKeySpecException, InvalidKeyException {
        Scanner scanner = new Scanner(System.in);
        System.out.println("Schlüssel:");
        String password = scanner.nextLine().trim();
        System.out.println();

        while(true) {
            System.out.println("Klartext:");
            String text = scanner.nextLine()
                                 .trim();

            if (text.isEmpty()) {
                break;
            }

            System.out.println("Verschlüsselter Text:");
            System.out.println(encrypt(text, password));

            System.out.println();
        }
    }

}