Note that there are some explanatory texts on larger screens.

plurals
  1. POAES with password based SecretKeySpec vs PBE
    primarykey
    data
    text
    <p>After reading through several stackoverflow questions about implementing AES I think I'm starting to understand the basics:</p> <ul> <li>Every single time I should generate a new IV</li> <li>When using PBE the iteration count should be around 1000-4000+</li> <li>Since I can't predict the amount of data to be encrypted I shouldn't use ECB cipher mode</li> </ul> <p>My environment is quite simple:</p> <ul> <li>the passphrase should be secure, it's a securerandom generated random 32 character at the moment (i.e. not set by a user).</li> <li>the generated encrypted content may end up being stored as cookies, so they are somewhat public</li> </ul> <p>Based on these I came up with the following Java code:</p> <pre><code>public class SecureEncryption { private static final String CONTENT = "thisneedstobestoredverysecurely"; private static final String PASSPHRASE = "mysuperstrongpassword"; private static final int IV_LENGTH = 16; public static void main(String[] args) throws Exception { MessageDigest digest = MessageDigest.getInstance("SHA-1"); byte[] passphrase = digest.digest(PASSPHRASE.getBytes("UTF-8")); Cipher instance = Cipher.getInstance("AES/CFB/NoPadding"); passphrase = Arrays.copyOf(passphrase, 16); SecretKeySpec secretKey = new SecretKeySpec(passphrase, "AES"); byte[] iv = new byte[16]; SecureRandom sr = new SecureRandom(); sr.nextBytes(iv); instance.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv)); byte[] encrypted = instance.doFinal(CONTENT.getBytes("UTF-8")); byte[] result = addIVtoEncrypted(iv, encrypted); System.arraycopy(result, 0, iv, 0, IV_LENGTH); System.arraycopy(result, IV_LENGTH, encrypted, 0, result.length - IV_LENGTH); instance.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv)); byte[] decrypted = instance.doFinal(encrypted); System.out.println(new String(decrypted, "UTF-8")); } private static byte[] addIVtoEncrypted(byte[] iv, byte[] encrypted) { byte[] ret = new byte[IV_LENGTH + encrypted.length]; System.arraycopy(iv, 0, ret, 0, IV_LENGTH); System.arraycopy(encrypted, 0, ret, IV_LENGTH, encrypted.length); return ret; } } </code></pre> <p>While this works fine, I'm not sure if it's as secure as it can get.. I'm kind of lost at the moment regarding the following things:</p> <ul> <li>is PBE using AES+SHA more secure? Does the salt+iteration count adds considerably to security? Which exact combination of PBE should I use if so?</li> <li>Should I instead consider using salt for the encryptable content (rather than for the PBE key)?</li> <li>If using salt for the content, what is preferred: one static value, or different values, but appended/prepended to the encrypted result (just as it is done with IV)?</li> </ul> <p><strong>UPDATE:</strong> based on the recommendations received here I rewrote my implementation:</p> <pre><code>public class SecureEncryption { private static final String CONTENT = "thisneedstobestoredverysecurely"; private static final String PASSPHRASE = "mysuperstrongpassword"; private static final int IV_LENGTH = 16; private static final int AES_KEY_LENGTH = 16; private static final int MAC_KEY_LENGTH = 16; private static final int MAC_LENGTH = 20; private static final int ITERATION_COUNT = 4096; private static final String AES = "AES"; private static final String CIPHER_ALGORITHM = "AES/CFB/NoPadding"; private static final String SECRET_KEY_ALGORITHM = "PBKDF2WithHmacSHA1"; private static final String MAC_ALGORITHM = "HmacSHA1"; public static void main(String[] args) throws Exception { Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); SecureRandom sr = new SecureRandom(); byte[] salt = new byte[16]; sr.nextBytes(salt); SecretKeyFactory factory = SecretKeyFactory.getInstance(SECRET_KEY_ALGORITHM); SecretKey secretKey = factory.generateSecret(new PBEKeySpec(PASSPHRASE.toCharArray(), salt, ITERATION_COUNT, 256)); byte[] secretBytes = secretKey.getEncoded(); byte[] iv = new byte[16]; sr.nextBytes(iv); cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(secretBytes, 0, AES_KEY_LENGTH, AES), new IvParameterSpec(iv)); byte[] encrypted = cipher.doFinal(CONTENT.getBytes("UTF-8")); byte[] result = concatArrays(iv, encrypted); byte[] macResult = getMAC(secretBytes, result); result = concatArrays(macResult, result); System.arraycopy(result, 0, macResult, 0, MAC_LENGTH); System.arraycopy(result, MAC_LENGTH, iv, 0, IV_LENGTH); System.arraycopy(result, MAC_LENGTH + IV_LENGTH, encrypted, 0, result.length - IV_LENGTH - MAC_LENGTH); if (!Arrays.equals(getDigest(getMAC(secretBytes, concatArrays(iv, encrypted))), getDigest(macResult))) { System.out.println("Invalid MAC"); } cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(secretBytes, 0, AES_KEY_LENGTH, AES), new IvParameterSpec(iv)); byte[] decrypted = cipher.doFinal(encrypted); System.out.println(new String(decrypted, "UTF-8")); } private static byte[] getDigest(byte[] mac) throws Exception { MessageDigest digest = MessageDigest.getInstance("SHA1"); return digest.digest(mac); } private static byte[] getMAC(byte[] secretBytes, byte[] data) throws Exception { Mac mac = Mac.getInstance(MAC_ALGORITHM); mac.init(new SecretKeySpec(secretBytes, AES_KEY_LENGTH, MAC_KEY_LENGTH, MAC_ALGORITHM)); return mac.doFinal(data); } private static byte[] concatArrays(byte[] first, byte[] second) { byte[] ret = new byte[first.length + second.length]; System.arraycopy(first, 0, ret, 0, first.length); System.arraycopy(second, 0, ret, first.length, second.length); return ret; } } </code></pre> <p>The plan will be to generate the salt installation time, and then it will remain the same for all encryption/decryption operations. I'm assuming that this should provide good enough protection against rainbow table attacks.</p> <p><strong>UPDATE 2:</strong> I had to realize that my MAC verification code wasn't quite optimal: the MAC already was SHA-1 hashed, so there was no point in creating a yet another SHA1 digest. I've also adjusted the MAC verification so it no longer uses Arrays.equals as that is vulnerable against timing attacks.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload