DS-Lab / src / main / java / dslab / crypto / AES.java
AES.java
Raw
package dslab.crypto;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.logging.Logger;

import static java.util.Base64.getDecoder;
import static java.util.Base64.getEncoder;
import static javax.crypto.Cipher.DECRYPT_MODE;
import static javax.crypto.Cipher.ENCRYPT_MODE;

/**
 * Offers encryption and decryption of Strings via AES.
 */
public class AES {

    private static final Logger LOG = Logger.getLogger(AES.class.getSimpleName());
    private final SecretKey secretKey;

    private static final SecureRandom secureRandom = new SecureRandom();
    private final IvParameterSpec iv;
    private final byte[] clientChallenge;
    private Cipher encryptionCipher;
    private Cipher decryptionCipher;

    /**
     * For initialization on challenging side, i.e. client
     */
    public AES() {
        try {
            var aes = KeyGenerator.getInstance("AES");
            aes.init(256);
            secretKey = aes.generateKey();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e); //never happens
        }

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

        clientChallenge = new byte[32];
        secureRandom.nextBytes(clientChallenge);

        initCiphers(new SecretKeySpec(secretKey.getEncoded(), "AES"), iv);
    }

    /**
     * For initialization on challenged side, i.e. server
     */
    public AES(ClientChallengeMessage challengeMessage) {
        clientChallenge = challengeMessage.getChallenge();
        secretKey = challengeMessage.getKey();
        iv = challengeMessage.getInitializationVector();

        initCiphers(challengeMessage.getKey(), challengeMessage.getInitializationVector());
    }

    private void initCiphers(SecretKeySpec key, IvParameterSpec initializationVector) {
        try {
            this.encryptionCipher = Cipher.getInstance("AES/CTR/NoPadding");
            encryptionCipher.init(ENCRYPT_MODE, key, initializationVector);

            this.decryptionCipher = Cipher.getInstance("AES/CTR/NoPadding");
            decryptionCipher.init(DECRYPT_MODE, key, initializationVector);

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public String getBase64EncodedKey() {
        return getEncoder().encodeToString(secretKey.getEncoded());
    }

    public String getBase64ClientChallenge() {
        return getEncoder().encodeToString(clientChallenge);
    }

    public String getBase64InitializationVector() {
        return getEncoder().encodeToString(iv.getIV());
    }

    public String encrypt(String cleartext) {
        try {
            return getEncoder().encodeToString(encryptionCipher.doFinal(cleartext.getBytes()));
        } catch (IllegalBlockSizeException | BadPaddingException e) {
            throw new RuntimeException(e);
        }
    }

    public String decrypt(String ciphertext) {
        try {
            var base64Decoded = getDecoder().decode(ciphertext);
            var decryptedBytes = decryptionCipher.doFinal(base64Decoded);
            return new String(decryptedBytes);
        } catch (IllegalBlockSizeException | BadPaddingException e) {
            throw new RuntimeException(e);
        }
    }
}