package com.example.passrepo.crypto; import java.security.GeneralSecurityException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import javax.crypto.Cipher; import javax.crypto.Mac; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import com.google.common.base.Preconditions; public class Encryption { private static final int REQUIRED_KEY_LENGTH_IN_BITS = 256; public static class CipherText { public final byte[] bytes; public final byte[] iv; public final byte[] mac; public CipherText(byte[] bytes, byte[] iv, byte[] mac) { this.bytes = bytes; this.iv = iv; this.mac = mac; } public boolean hmacVerified(byte[] hmacKey) { byte[] calculatedMac = calculateMac(hmacKey, iv, bytes); return Arrays.equals(mac, calculatedMac); } } public static CipherText encrypt(byte[] plainText, PasswordHasher.Keys keys) { try { Preconditions.checkArgument(keys.encryptionKey.length == REQUIRED_KEY_LENGTH_IN_BITS / 8); Preconditions.checkArgument(keys.hmacKey.length == REQUIRED_KEY_LENGTH_IN_BITS / 8); Cipher cipher = Cipher.getInstance("AES/CTR/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keys.encryptionKey, "AES")); final byte[] cipherText = cipher.doFinal(plainText); final byte[] iv = cipher.getIV(); final byte[] mac = calculateMac(keys.hmacKey, iv, cipherText); return new CipherText(cipherText, iv, mac); } catch (GeneralSecurityException e) { throw new RuntimeException(e); } } public static byte[] calculateMac(byte[] hmacKey, byte[] iv, byte[] cipherText) { try { Mac hmacSHA256 = Mac.getInstance("HmacSHA256"); hmacSHA256.init(new SecretKeySpec(hmacKey, "HmacSHA256")); hmacSHA256.update(iv); hmacSHA256.update(cipherText); return hmacSHA256.doFinal(); } catch (GeneralSecurityException e) { throw new RuntimeException(e); } } public static byte[] decrypt(CipherText cipherText, byte[] key) { try { Preconditions.checkArgument(key.length == REQUIRED_KEY_LENGTH_IN_BITS / 8); Cipher cipher = Cipher.getInstance("AES/CTR/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(cipherText.iv)); return cipher.doFinal(cipherText.bytes); } catch (GeneralSecurityException e) { throw new RuntimeException(e); } } }