Java 256-Bit Aes Password-Based Encryption

Java 256-bit AES Password-Based Encryption

Share the password (a char[]) and salt (a byte[]—8 bytes selected by a SecureRandom makes a good salt—which doesn't need to be kept secret) with the recipient out-of-band. Then to derive a good key from this information:

/* Derive the key, given password and salt. */
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");

The magic numbers (which could be defined as constants somewhere) 65536 and 256 are the key derivation iteration count and the key size, respectively.

The key derivation function is iterated to require significant computational effort, and that prevents attackers from quickly trying many different passwords. The iteration count can be changed depending on the computing resources available.

The key size can be reduced to 128 bits, which is still considered "strong" encryption, but it doesn't give much of a safety margin if attacks are discovered that weaken AES.

Used with a proper block-chaining mode, the same derived key can be used to encrypt many messages. In Cipher Block Chaining (CBC), a random initialization vector (IV) is generated for each message, yielding different cipher text even if the plain text is identical. CBC may not be the most secure mode available to you (see AEAD below); there are many other modes with different security properties, but they all use a similar random input. In any case, the outputs of each encryption operation are the cipher text and the initialization vector:

/* Encrypt the message. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] ciphertext = cipher.doFinal("Hello, World!".getBytes(StandardCharsets.UTF_8));

Store the ciphertext and the iv. On decryption, the SecretKey is regenerated in exactly the same way, using using the password with the same salt and iteration parameters. Initialize the cipher with this key and the initialization vector stored with the message:

/* Decrypt the message, given derived key and initialization vector. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
String plaintext = new String(cipher.doFinal(ciphertext), StandardCharsets.UTF_8);
System.out.println(plaintext);

Java 7 included API support for AEAD cipher modes, and the "SunJCE" provider included with OpenJDK and Oracle distributions implements these beginning with Java 8. One of these modes is strongly recommended in place of CBC; it will protect the integrity of the data as well as their privacy.


A java.security.InvalidKeyException with the message "Illegal key size or default parameters" means that the cryptography strength is limited; the unlimited strength jurisdiction policy files are not in the correct location. In a JDK, they should be placed under ${jdk}/jre/lib/security

Based on the problem description, it sounds like the policy files are not correctly installed. Systems can easily have multiple Java runtimes; double-check to make sure that the correct location is being used.

AES-256 Password Based Encryption/Decryption in Java

I think that you are making two mistakes :)

I've corrected your sample code to make it work :

import java.security.AlgorithmParameters;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

public class EncryptionDecryption {

private static String salt;
private static int iterations = 65536 ;
private static int keySize = 256;
private static byte[] ivBytes;

private static SecretKey secretKey;

public static void main(String []args) throws Exception {

salt = getSalt();

char[] message = "PasswordToEncrypt".toCharArray();
System.out.println("Message: " + String.valueOf(message));
System.out.println("Encrypted: " + encrypt(message));
System.out.println("Decrypted: " + decrypt(encrypt(message).toCharArray()));
}

public static String encrypt(char[] plaintext) throws Exception {
byte[] saltBytes = salt.getBytes();

SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(plaintext, saltBytes, iterations, keySize);
secretKey = skf.generateSecret(spec);
SecretKeySpec secretSpec = new SecretKeySpec(secretKey.getEncoded(), "AES");

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretSpec);
AlgorithmParameters params = cipher.getParameters();
ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] encryptedTextBytes = cipher.doFinal(String.valueOf(plaintext).getBytes("UTF-8"));

return DatatypeConverter.printBase64Binary(encryptedTextBytes);
}

public static String decrypt(char[] encryptedText) throws Exception {

System.out.println(encryptedText);

byte[] encryptedTextBytes = DatatypeConverter.parseBase64Binary(new String(encryptedText));
SecretKeySpec secretSpec = new SecretKeySpec(secretKey.getEncoded(), "AES");

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretSpec, new IvParameterSpec(ivBytes));

byte[] decryptedTextBytes = null;

try {
decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}

return new String(decryptedTextBytes);

}

public static String getSalt() throws Exception {

SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
byte[] salt = new byte[20];
sr.nextBytes(salt);
return new String(salt);
}
}

The first mistake is that you generate 2 different salts (when using the encrypt method), so encrypted/decrypted logs were differents (logical, but the decryption would still work because you are calling the decryption directly after encryption).

The second mistake was for the secret key. You need to generate a secret key when you are encrypting, but not decrypting. To put it more simply, it is as if i was encrypting with the password "encrypt" and that you are trying to decrypt it with the password "decrypt".

I would advise you to generate every random stuff (such as private key, salt etc on startup). But beware that when you'll stop your app, you won't be able to decrypt old stuff unless getting the exact same random stuff.

Hope I helped :)

Regards,

AES 256 bit encryption

By default Java supports only 128-bit encryption. If you want to exceed more than that you need the unlimited strength file installed. To do that download the jars and extract the jar files from the zip and save them in ${java.home}/jre/lib/security/.

For more see here: https://stackoverflow.com/a/6481658/1008278

Java AES 256 encryption

Yes, it will as 64 characters are 32 bytes and 256 bits and any sequence of 256 bits can be used as an AES-256 key.

I suggest you to use DatatypeConverter.parseHexBinary (or similar utility from library of your choice) to convert hexadecimal strings into byte arrays.

Same password results in different AES keys in Java

Your understanding is correct and the key in your code is the same.

You can't "print" the aesKey since SecretKeySpec has no toString() method. So the built-in Object.toString() will be called, which just prints the address of the object in memory

javax.crypto.spec.SecretKeySpec@14c7f    // <--- useless info //

There are just a few issues with your code:

  • Don't convert encrypted bytes to a UTF-8 string. There can be combinations which are invalid in UTF-8, as well as 00 bytes. Use Base64 or Hex encoding for printing encrypted data.

  • You shouldn't use ASCII bytes as a key, that greatly reduces security of the key. Derive a key from the password, at least with SHA-256, but preferably PBKDF2 or scrypt.

  • Use a high entropy random IV and store it with the ciphertext.

Here's an updated version demonstrating that it's working:

public static String encryptMessageGH(String message, String password) throws Exception {
MessageDigest sha = MessageDigest.getInstance("SHA-256");
byte[] key = sha.digest(password.getBytes("UTF-8"));
SecretKeySpec aesKey = new SecretKeySpec(key, "AES");
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, aesKey, new IvParameterSpec(iv));
byte[] ciphertext = cipher.doFinal(message.getBytes());
byte[] encrypted = new byte[iv.length + ciphertext.length];
System.arraycopy(iv, 0, encrypted, 0, iv.length);
System.arraycopy(ciphertext, 0, encrypted, iv.length, ciphertext.length);
return Base64.getEncoder().encodeToString(encrypted);
}

public static String decryptMessageGH(String encryptedMessage, String password) throws Exception {
MessageDigest sha = MessageDigest.getInstance("SHA-256");
byte[] key = sha.digest(password.getBytes("UTF-8"));
SecretKeySpec aesKey = new SecretKeySpec(key, "AES");
byte[] encrypted = Base64.getDecoder().decode(encryptedMessage);
byte[] iv = new byte[16];
System.arraycopy(encrypted, 0, iv, 0, iv.length);
byte[] ciphertext = new byte[encrypted.length - iv.length];
System.arraycopy(encrypted, iv.length, ciphertext, 0, ciphertext.length);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, aesKey, new IvParameterSpec(iv));
return new String(cipher.doFinal(ciphertext), "UTF-8");
}

public static void main(String[] args) throws Exception {
String orig = "Test message";
String enc = encryptMessageGH(orig, "abcdef123");
System.out.println("Encrypted: " + enc);
String dec = decryptMessageGH(enc, "abcdef123");
System.out.println("Decrypted: " + dec);
}

Output:

Encrypted: lcqcd9UZpjLSY9SsQ/N7kV/cpdzL3c7HQcCSiIs6p/k=
Decrypted: Test message

Does this use 256-bit AES encryption?

Your example appears to use a 32-byte key and a 256 bit version of the AES cryptosystem. So, technically yes it is 256-bit AES encryption. The actual size of the message determines the resulting output but it should be larger then the original message. Also, you should be able to decrypt it and get the original message. Finally, using a constant iv is not recommended and may well render your system insecure in and of itself.



Related Topics



Leave a reply



Submit