How to generate AES keys for encryption and decryption using AES/CBC/PKCS5Padding
I am trying to create AES key using this code
public static SecretKey generateSecretKey() {
KeyGenerator generator;
try {
generator = KeyGenerator.getInstance(StaticHandler.AES_KEY_MODE); // Is "AES"
generator.init(StaticHandler.AES_KEY_SIZE); // The AES key size in number of bits // Is "128"
return generator.generateKey();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
But use this code to encrypt/decrypt
public static String encrypt(String data, SecretKey secret, Charset charset) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
return new String(cipher.doFinal(data.getBytes()), charset);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static String decrypt(String data, @NonNull SecretKey secret, Charset charset) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret);
return new String(cipher.doFinal(data.getBytes()), charset);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
get errorjava.security.InvalidKeyException: Parameters missing
I'm guessing I need to add some salt, although I don't know how to do it with the generated key. I'd like to avoid generating passwords, but I wouldn't mind if it was securely generated.
EDIT: In hindsight, if I'm sending packets over the network, should I use GCM or CBC encryption? Keep in mind that I'm using randomly generated keys and I don't keep them for sessions (randomly generated per client and server session).
No, you don't need salt, your keys are actually fine. CBC mode requires an IV (Initialization Vector), see Wikipedia , and the IV should be different for each encrypted data, but each decryption must use the same value as the corresponding encryption. (Added) For CBC, though not otherwise, it is also critical for the IV that the adversary cannot predict the IV; the easiest and most common way to achieve uniqueness and unpredictability is to use a secure random number (aka bit) generator, such as Java's SecureRandom
. If you want to know the other way, it's not really a programming question, it's more applicable to crypto.SX or security.SX where multiple Q's already exist.
You can explicitly generate the IV and assign it to encrypt and decrypt, or allow the encryption operation to generate the IV itself, get the IV from the encrypted cipher, and assign it to the decrypted cipher. In either case, the encryptor must provide the value that the decryptor will use; the common approach is to simply concatenate the IV with the ciphertext (making them easy to match correctly), but there are also other discussion methods. See https://docs.oracle.com/en-us/java/javase/11/security/java-cryptography-architecture-jca-reference-guide.html in the "Initializing the Cryptographic Object" section (following the two paragraphs) box within the method declaration box) and "Managing Algorithm Parameters".
Also don't store in ciphertextString
. Java String
is designed to handle valid characters, not arbitrary bytes. "Decoding" the ciphertext into a string and "encoding" it into binary will pretty much lose or change some data, especially if you allow for different character sets on both ends, and with modern cryptography, the ciphertext will not be changed at all Destroy all or most of your data. Since the ciphertext is bytes, it is better to treat it as byte[]
; . If something like a URL is n't possible because you want to put it , use one of several schemes designed to encode arbitrary bytes as text so they can be recovered correctly: base64 (3 or 4 major variants, plus many minor variants), base32, hex/base16, URL "percent" encoding, MIME reference printable, yencode, Kermit, PPP, etc. j8+ provides a newer base64 variant (ie not uuencode).java.util.Base64
Conversely, while "plain text" in modern encryption can actually be any form of data, if you do have text and you belong, String
you should encode it with the appropriate charset before encryption, and decrypt it with the same character set to decode, i.e.
byte[] ctext = encCipher.doFinal (input.getBytes(charset));
...
String output = new String (decCipher.doFinal (ctext), charset);
While the "best" character set may vary by data, if you don't know what the data is or don't want to bother analyzing it, it 's pretty good UTF-8
for most textual data , and is very popular and standard.