Java AES CBC encryption/decryption
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
6
down vote
favorite
I've looked at multiple examples of Java AES CBC mode encryption but I couldn't find a good answer that was safe to use. I ended up writing my own util code. My questions is: Is this code safe to use? I've read some stuff about padding oracle attacks but I don't really understand how they work.
I've tested the code and it seems to work properly:
CryptoUtils.removeCryptoRestriction();
String string = "Hello world! This is a test string. Have a nice day!";
byte iv = CryptoUtils.generateIv();
byte key = CryptoUtils.generateKey();
byte encrypted = CryptoUtils.doCrypto(Cipher.ENCRYPT_MODE, key, iv, string.getBytes("UTF-8"));
System.out.println(new String(encrypted));
byte decrypted = CryptoUtils.doCrypto(Cipher.DECRYPT_MODE, key, iv, encrypted);
System.out.println(new String(decrypted));
Output:
��zI FÃÂ��O>��_�@3�÷+|ZI�m���M�4�;���ygr8G�ê8�6u���M
Hello world! This is a test string. Have a nice day!
Here's the util code:
public static byte doCrypto(int mode, byte keyBytes, byte ivBytes, byte bytes) throws GeneralSecurityException
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, 0, keyBytes.length, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
cipher.init(mode, secretKeySpec, ivParameterSpec);
return cipher.doFinal(bytes);
public static void doCrypto(int mode, byte keyBytes, byte ivBytes, File in, File out) throws GeneralSecurityException, IOException
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, 0, keyBytes.length, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
cipher.init(mode, secretKeySpec, ivParameterSpec);
try (FileInputStream fileInputStream = new FileInputStream(in); FileOutputStream fileOutputStream = new FileOutputStream(out))
byte buffer = new byte[1024];
for (int i = 0; i != -1; i = fileInputStream.read(buffer))
byte updateBytes = cipher.update(buffer, 0, i);
if (updateBytes != null) fileOutputStream.write(updateBytes);
byte finalBytes = cipher.doFinal();
if (finalBytes != null) fileOutputStream.write(finalBytes);
public static byte generateIv()
SecureRandom secureRandom = new SecureRandom();
byte iv = new byte[16];
secureRandom.nextBytes(iv);
return iv;
public static byte generateKey() throws NoSuchAlgorithmException
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(256);
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
public static void removeCryptoRestriction()
Security.setProperty("crypto.policy", "unlimited");
The file encryption works too.
Thanks in advance!
java cryptography aes
add a comment |Â
up vote
6
down vote
favorite
I've looked at multiple examples of Java AES CBC mode encryption but I couldn't find a good answer that was safe to use. I ended up writing my own util code. My questions is: Is this code safe to use? I've read some stuff about padding oracle attacks but I don't really understand how they work.
I've tested the code and it seems to work properly:
CryptoUtils.removeCryptoRestriction();
String string = "Hello world! This is a test string. Have a nice day!";
byte iv = CryptoUtils.generateIv();
byte key = CryptoUtils.generateKey();
byte encrypted = CryptoUtils.doCrypto(Cipher.ENCRYPT_MODE, key, iv, string.getBytes("UTF-8"));
System.out.println(new String(encrypted));
byte decrypted = CryptoUtils.doCrypto(Cipher.DECRYPT_MODE, key, iv, encrypted);
System.out.println(new String(decrypted));
Output:
��zI FÃÂ��O>��_�@3�÷+|ZI�m���M�4�;���ygr8G�ê8�6u���M
Hello world! This is a test string. Have a nice day!
Here's the util code:
public static byte doCrypto(int mode, byte keyBytes, byte ivBytes, byte bytes) throws GeneralSecurityException
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, 0, keyBytes.length, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
cipher.init(mode, secretKeySpec, ivParameterSpec);
return cipher.doFinal(bytes);
public static void doCrypto(int mode, byte keyBytes, byte ivBytes, File in, File out) throws GeneralSecurityException, IOException
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, 0, keyBytes.length, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
cipher.init(mode, secretKeySpec, ivParameterSpec);
try (FileInputStream fileInputStream = new FileInputStream(in); FileOutputStream fileOutputStream = new FileOutputStream(out))
byte buffer = new byte[1024];
for (int i = 0; i != -1; i = fileInputStream.read(buffer))
byte updateBytes = cipher.update(buffer, 0, i);
if (updateBytes != null) fileOutputStream.write(updateBytes);
byte finalBytes = cipher.doFinal();
if (finalBytes != null) fileOutputStream.write(finalBytes);
public static byte generateIv()
SecureRandom secureRandom = new SecureRandom();
byte iv = new byte[16];
secureRandom.nextBytes(iv);
return iv;
public static byte generateKey() throws NoSuchAlgorithmException
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(256);
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
public static void removeCryptoRestriction()
Security.setProperty("crypto.policy", "unlimited");
The file encryption works too.
Thanks in advance!
java cryptography aes
add a comment |Â
up vote
6
down vote
favorite
up vote
6
down vote
favorite
I've looked at multiple examples of Java AES CBC mode encryption but I couldn't find a good answer that was safe to use. I ended up writing my own util code. My questions is: Is this code safe to use? I've read some stuff about padding oracle attacks but I don't really understand how they work.
I've tested the code and it seems to work properly:
CryptoUtils.removeCryptoRestriction();
String string = "Hello world! This is a test string. Have a nice day!";
byte iv = CryptoUtils.generateIv();
byte key = CryptoUtils.generateKey();
byte encrypted = CryptoUtils.doCrypto(Cipher.ENCRYPT_MODE, key, iv, string.getBytes("UTF-8"));
System.out.println(new String(encrypted));
byte decrypted = CryptoUtils.doCrypto(Cipher.DECRYPT_MODE, key, iv, encrypted);
System.out.println(new String(decrypted));
Output:
��zI FÃÂ��O>��_�@3�÷+|ZI�m���M�4�;���ygr8G�ê8�6u���M
Hello world! This is a test string. Have a nice day!
Here's the util code:
public static byte doCrypto(int mode, byte keyBytes, byte ivBytes, byte bytes) throws GeneralSecurityException
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, 0, keyBytes.length, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
cipher.init(mode, secretKeySpec, ivParameterSpec);
return cipher.doFinal(bytes);
public static void doCrypto(int mode, byte keyBytes, byte ivBytes, File in, File out) throws GeneralSecurityException, IOException
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, 0, keyBytes.length, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
cipher.init(mode, secretKeySpec, ivParameterSpec);
try (FileInputStream fileInputStream = new FileInputStream(in); FileOutputStream fileOutputStream = new FileOutputStream(out))
byte buffer = new byte[1024];
for (int i = 0; i != -1; i = fileInputStream.read(buffer))
byte updateBytes = cipher.update(buffer, 0, i);
if (updateBytes != null) fileOutputStream.write(updateBytes);
byte finalBytes = cipher.doFinal();
if (finalBytes != null) fileOutputStream.write(finalBytes);
public static byte generateIv()
SecureRandom secureRandom = new SecureRandom();
byte iv = new byte[16];
secureRandom.nextBytes(iv);
return iv;
public static byte generateKey() throws NoSuchAlgorithmException
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(256);
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
public static void removeCryptoRestriction()
Security.setProperty("crypto.policy", "unlimited");
The file encryption works too.
Thanks in advance!
java cryptography aes
I've looked at multiple examples of Java AES CBC mode encryption but I couldn't find a good answer that was safe to use. I ended up writing my own util code. My questions is: Is this code safe to use? I've read some stuff about padding oracle attacks but I don't really understand how they work.
I've tested the code and it seems to work properly:
CryptoUtils.removeCryptoRestriction();
String string = "Hello world! This is a test string. Have a nice day!";
byte iv = CryptoUtils.generateIv();
byte key = CryptoUtils.generateKey();
byte encrypted = CryptoUtils.doCrypto(Cipher.ENCRYPT_MODE, key, iv, string.getBytes("UTF-8"));
System.out.println(new String(encrypted));
byte decrypted = CryptoUtils.doCrypto(Cipher.DECRYPT_MODE, key, iv, encrypted);
System.out.println(new String(decrypted));
Output:
��zI FÃÂ��O>��_�@3�÷+|ZI�m���M�4�;���ygr8G�ê8�6u���M
Hello world! This is a test string. Have a nice day!
Here's the util code:
public static byte doCrypto(int mode, byte keyBytes, byte ivBytes, byte bytes) throws GeneralSecurityException
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, 0, keyBytes.length, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
cipher.init(mode, secretKeySpec, ivParameterSpec);
return cipher.doFinal(bytes);
public static void doCrypto(int mode, byte keyBytes, byte ivBytes, File in, File out) throws GeneralSecurityException, IOException
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, 0, keyBytes.length, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
cipher.init(mode, secretKeySpec, ivParameterSpec);
try (FileInputStream fileInputStream = new FileInputStream(in); FileOutputStream fileOutputStream = new FileOutputStream(out))
byte buffer = new byte[1024];
for (int i = 0; i != -1; i = fileInputStream.read(buffer))
byte updateBytes = cipher.update(buffer, 0, i);
if (updateBytes != null) fileOutputStream.write(updateBytes);
byte finalBytes = cipher.doFinal();
if (finalBytes != null) fileOutputStream.write(finalBytes);
public static byte generateIv()
SecureRandom secureRandom = new SecureRandom();
byte iv = new byte[16];
secureRandom.nextBytes(iv);
return iv;
public static byte generateKey() throws NoSuchAlgorithmException
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(256);
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
public static void removeCryptoRestriction()
Security.setProperty("crypto.policy", "unlimited");
The file encryption works too.
Thanks in advance!
java cryptography aes
edited Feb 6 at 16:01
asked Feb 6 at 15:45
Rowin
313
313
add a comment |Â
add a comment |Â
3 Answers
3
active
oldest
votes
up vote
3
down vote
You should move to an authenticated mode of encryption like CCM, EAX, OCB or GCM. The fact that you are generating a random sequence of bytes from the OS's PRNG is sound. It looks like the argument provided to the crypto object is already padding the resulting cipher text of the file stream.
The top bit about removing the restrictions is suspect.
Padding attacks are a side channel attack in crypto that allows an attacker to perform cryptanalysis on a block of cipher text because the plain text input would determine the resulting cipher texts length. By padding the resulting cipher text it makes that side channel more difficult.
How do you think cryptanalysis works exactly?
â jas-
Feb 7 at 7:56
Typo, was supposed to be CCM
â jas-
Feb 7 at 16:58
In many cases the authentication modes are not available, in particular Apple unfortunately does not provide the mentioned modes for iOS. A Padding Oracle requires padding which allows the attacker to recover the text (less the first block) but not the key if padding errors are returned and sufficient access is allowed. Modes that do not require padding such as GCM can not suffer from such an attack.
â zaph
Feb 7 at 17:50
add a comment |Â
up vote
2
down vote
There are several ways to prevent a Padding Oracle attack use one or more of the following:
- Use authenticated encryption such as GCM mode or encrypt-then-MAC.
- Do not report padding errors.
- Do not allow an attacker to use your code to decrypt.
- If the attacker can use your decryption seriously rate-limit access.
add a comment |Â
up vote
2
down vote
Not directly related to the security of this (I'm not remotely qualified to talk about it anyways):
CryptoUtils.doCrypto(Cipher.ENCRYPT_MODE, ...);
CryptoUtils.doCrypto(Cipher.DECRYPT_MODE, ...);
^^ this is an example of a state machine in a method. You want to avoid these in high-level languages like java. They don't make for a nice interface in usage. Instead you want an encrypt
and a decrypt
. This pushes the "mode" parameter into the name of your method.
If it's easier to implement the code itself as a state-machine, that's just fine, as long as you do so internally.
public static void encrypt(byte keyBytes, byte ivBytes, byte cleartext) throws GeneralSecurityException
return doCrypto(Cipher.ENCRYPT_MODE, keyBytes, ivBytes, cleartext);
If you were to aim for a more general-purpose cryptography utility class, you could expose an overload that allows the user to specify which cipher they want to use.
If you decide to do so, you can pass the cipher.getAlgorithm()
to the SecretKeySpec
constructor.
I like how you used try-with-resources
to handle the IO in the file-encryption. But there is a somewhat cleaner way than manually handling the bytes you have there.
Consider the following methods:
public static void encrypt(byte keyBytes, byte ivBytes, Path in, Path out) throws GeneralSecurityException, IOException
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, cipher.getAlgorithm());
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
try (OutputStream os = new CipherOutputStream(Files.newOutputStream(out), cipher))
Files.copy(in, os);
public static void decrypt(byte keyBytes, byte ivBytes, Path in, Path out) throws GeneralSecurityException, IOException
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, cipher.getAlgorithm());
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
try (InputStream is = new CipherInputStream(Files.newInputStream(in), cipher))
Files.copy(is, out);
Here we can notice some duplication and extract the Cipher initialization routine into a separate method to simplify the code to:
public static void encrypt(byte keyBytes, byte ivBytes, Path in, Path out) throws GeneralSecurityException, IOException
Cipher cipher = initializeCipher(Cipher.ENCRYPTION_MODE, keyBytes, ivBytes);
try (OutputStream os = new CipherOutputStream(Files.newOutputStream(out), cipher))
Files.copy(in, os);
Note that I explicitly changed from File
to the newer (and more modern Path
from the java.nio
package)
Be aware that even here you can allow the user to specify the Cipher by allowing them to pass a String as cipherSpec
or something along these lines.
On that note: you can use that initialization method even in the code that en/decrypts byte directly.
Lastly I wanted to be a bit annoyed that neither generateIv
nor generateKey
provide any means to specify the number of bytes used. While 256-bit AES is generally considered secure, some users might want to opt for the extra security of using a 512-bit key. Also I think a 16-bit IV is probably a bit too short.
Additionally I'd recommend using SecureRandom.getInstanceStrong()
over new SecureRandom()
because this gets you a definitively strong SecureRandom instead of "the first that was available".
add a comment |Â
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
3
down vote
You should move to an authenticated mode of encryption like CCM, EAX, OCB or GCM. The fact that you are generating a random sequence of bytes from the OS's PRNG is sound. It looks like the argument provided to the crypto object is already padding the resulting cipher text of the file stream.
The top bit about removing the restrictions is suspect.
Padding attacks are a side channel attack in crypto that allows an attacker to perform cryptanalysis on a block of cipher text because the plain text input would determine the resulting cipher texts length. By padding the resulting cipher text it makes that side channel more difficult.
How do you think cryptanalysis works exactly?
â jas-
Feb 7 at 7:56
Typo, was supposed to be CCM
â jas-
Feb 7 at 16:58
In many cases the authentication modes are not available, in particular Apple unfortunately does not provide the mentioned modes for iOS. A Padding Oracle requires padding which allows the attacker to recover the text (less the first block) but not the key if padding errors are returned and sufficient access is allowed. Modes that do not require padding such as GCM can not suffer from such an attack.
â zaph
Feb 7 at 17:50
add a comment |Â
up vote
3
down vote
You should move to an authenticated mode of encryption like CCM, EAX, OCB or GCM. The fact that you are generating a random sequence of bytes from the OS's PRNG is sound. It looks like the argument provided to the crypto object is already padding the resulting cipher text of the file stream.
The top bit about removing the restrictions is suspect.
Padding attacks are a side channel attack in crypto that allows an attacker to perform cryptanalysis on a block of cipher text because the plain text input would determine the resulting cipher texts length. By padding the resulting cipher text it makes that side channel more difficult.
How do you think cryptanalysis works exactly?
â jas-
Feb 7 at 7:56
Typo, was supposed to be CCM
â jas-
Feb 7 at 16:58
In many cases the authentication modes are not available, in particular Apple unfortunately does not provide the mentioned modes for iOS. A Padding Oracle requires padding which allows the attacker to recover the text (less the first block) but not the key if padding errors are returned and sufficient access is allowed. Modes that do not require padding such as GCM can not suffer from such an attack.
â zaph
Feb 7 at 17:50
add a comment |Â
up vote
3
down vote
up vote
3
down vote
You should move to an authenticated mode of encryption like CCM, EAX, OCB or GCM. The fact that you are generating a random sequence of bytes from the OS's PRNG is sound. It looks like the argument provided to the crypto object is already padding the resulting cipher text of the file stream.
The top bit about removing the restrictions is suspect.
Padding attacks are a side channel attack in crypto that allows an attacker to perform cryptanalysis on a block of cipher text because the plain text input would determine the resulting cipher texts length. By padding the resulting cipher text it makes that side channel more difficult.
You should move to an authenticated mode of encryption like CCM, EAX, OCB or GCM. The fact that you are generating a random sequence of bytes from the OS's PRNG is sound. It looks like the argument provided to the crypto object is already padding the resulting cipher text of the file stream.
The top bit about removing the restrictions is suspect.
Padding attacks are a side channel attack in crypto that allows an attacker to perform cryptanalysis on a block of cipher text because the plain text input would determine the resulting cipher texts length. By padding the resulting cipher text it makes that side channel more difficult.
edited Feb 7 at 16:57
answered Feb 6 at 17:00
jas-
20517
20517
How do you think cryptanalysis works exactly?
â jas-
Feb 7 at 7:56
Typo, was supposed to be CCM
â jas-
Feb 7 at 16:58
In many cases the authentication modes are not available, in particular Apple unfortunately does not provide the mentioned modes for iOS. A Padding Oracle requires padding which allows the attacker to recover the text (less the first block) but not the key if padding errors are returned and sufficient access is allowed. Modes that do not require padding such as GCM can not suffer from such an attack.
â zaph
Feb 7 at 17:50
add a comment |Â
How do you think cryptanalysis works exactly?
â jas-
Feb 7 at 7:56
Typo, was supposed to be CCM
â jas-
Feb 7 at 16:58
In many cases the authentication modes are not available, in particular Apple unfortunately does not provide the mentioned modes for iOS. A Padding Oracle requires padding which allows the attacker to recover the text (less the first block) but not the key if padding errors are returned and sufficient access is allowed. Modes that do not require padding such as GCM can not suffer from such an attack.
â zaph
Feb 7 at 17:50
How do you think cryptanalysis works exactly?
â jas-
Feb 7 at 7:56
How do you think cryptanalysis works exactly?
â jas-
Feb 7 at 7:56
Typo, was supposed to be CCM
â jas-
Feb 7 at 16:58
Typo, was supposed to be CCM
â jas-
Feb 7 at 16:58
In many cases the authentication modes are not available, in particular Apple unfortunately does not provide the mentioned modes for iOS. A Padding Oracle requires padding which allows the attacker to recover the text (less the first block) but not the key if padding errors are returned and sufficient access is allowed. Modes that do not require padding such as GCM can not suffer from such an attack.
â zaph
Feb 7 at 17:50
In many cases the authentication modes are not available, in particular Apple unfortunately does not provide the mentioned modes for iOS. A Padding Oracle requires padding which allows the attacker to recover the text (less the first block) but not the key if padding errors are returned and sufficient access is allowed. Modes that do not require padding such as GCM can not suffer from such an attack.
â zaph
Feb 7 at 17:50
add a comment |Â
up vote
2
down vote
There are several ways to prevent a Padding Oracle attack use one or more of the following:
- Use authenticated encryption such as GCM mode or encrypt-then-MAC.
- Do not report padding errors.
- Do not allow an attacker to use your code to decrypt.
- If the attacker can use your decryption seriously rate-limit access.
add a comment |Â
up vote
2
down vote
There are several ways to prevent a Padding Oracle attack use one or more of the following:
- Use authenticated encryption such as GCM mode or encrypt-then-MAC.
- Do not report padding errors.
- Do not allow an attacker to use your code to decrypt.
- If the attacker can use your decryption seriously rate-limit access.
add a comment |Â
up vote
2
down vote
up vote
2
down vote
There are several ways to prevent a Padding Oracle attack use one or more of the following:
- Use authenticated encryption such as GCM mode or encrypt-then-MAC.
- Do not report padding errors.
- Do not allow an attacker to use your code to decrypt.
- If the attacker can use your decryption seriously rate-limit access.
There are several ways to prevent a Padding Oracle attack use one or more of the following:
- Use authenticated encryption such as GCM mode or encrypt-then-MAC.
- Do not report padding errors.
- Do not allow an attacker to use your code to decrypt.
- If the attacker can use your decryption seriously rate-limit access.
answered Feb 6 at 19:21
zaph
1764
1764
add a comment |Â
add a comment |Â
up vote
2
down vote
Not directly related to the security of this (I'm not remotely qualified to talk about it anyways):
CryptoUtils.doCrypto(Cipher.ENCRYPT_MODE, ...);
CryptoUtils.doCrypto(Cipher.DECRYPT_MODE, ...);
^^ this is an example of a state machine in a method. You want to avoid these in high-level languages like java. They don't make for a nice interface in usage. Instead you want an encrypt
and a decrypt
. This pushes the "mode" parameter into the name of your method.
If it's easier to implement the code itself as a state-machine, that's just fine, as long as you do so internally.
public static void encrypt(byte keyBytes, byte ivBytes, byte cleartext) throws GeneralSecurityException
return doCrypto(Cipher.ENCRYPT_MODE, keyBytes, ivBytes, cleartext);
If you were to aim for a more general-purpose cryptography utility class, you could expose an overload that allows the user to specify which cipher they want to use.
If you decide to do so, you can pass the cipher.getAlgorithm()
to the SecretKeySpec
constructor.
I like how you used try-with-resources
to handle the IO in the file-encryption. But there is a somewhat cleaner way than manually handling the bytes you have there.
Consider the following methods:
public static void encrypt(byte keyBytes, byte ivBytes, Path in, Path out) throws GeneralSecurityException, IOException
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, cipher.getAlgorithm());
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
try (OutputStream os = new CipherOutputStream(Files.newOutputStream(out), cipher))
Files.copy(in, os);
public static void decrypt(byte keyBytes, byte ivBytes, Path in, Path out) throws GeneralSecurityException, IOException
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, cipher.getAlgorithm());
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
try (InputStream is = new CipherInputStream(Files.newInputStream(in), cipher))
Files.copy(is, out);
Here we can notice some duplication and extract the Cipher initialization routine into a separate method to simplify the code to:
public static void encrypt(byte keyBytes, byte ivBytes, Path in, Path out) throws GeneralSecurityException, IOException
Cipher cipher = initializeCipher(Cipher.ENCRYPTION_MODE, keyBytes, ivBytes);
try (OutputStream os = new CipherOutputStream(Files.newOutputStream(out), cipher))
Files.copy(in, os);
Note that I explicitly changed from File
to the newer (and more modern Path
from the java.nio
package)
Be aware that even here you can allow the user to specify the Cipher by allowing them to pass a String as cipherSpec
or something along these lines.
On that note: you can use that initialization method even in the code that en/decrypts byte directly.
Lastly I wanted to be a bit annoyed that neither generateIv
nor generateKey
provide any means to specify the number of bytes used. While 256-bit AES is generally considered secure, some users might want to opt for the extra security of using a 512-bit key. Also I think a 16-bit IV is probably a bit too short.
Additionally I'd recommend using SecureRandom.getInstanceStrong()
over new SecureRandom()
because this gets you a definitively strong SecureRandom instead of "the first that was available".
add a comment |Â
up vote
2
down vote
Not directly related to the security of this (I'm not remotely qualified to talk about it anyways):
CryptoUtils.doCrypto(Cipher.ENCRYPT_MODE, ...);
CryptoUtils.doCrypto(Cipher.DECRYPT_MODE, ...);
^^ this is an example of a state machine in a method. You want to avoid these in high-level languages like java. They don't make for a nice interface in usage. Instead you want an encrypt
and a decrypt
. This pushes the "mode" parameter into the name of your method.
If it's easier to implement the code itself as a state-machine, that's just fine, as long as you do so internally.
public static void encrypt(byte keyBytes, byte ivBytes, byte cleartext) throws GeneralSecurityException
return doCrypto(Cipher.ENCRYPT_MODE, keyBytes, ivBytes, cleartext);
If you were to aim for a more general-purpose cryptography utility class, you could expose an overload that allows the user to specify which cipher they want to use.
If you decide to do so, you can pass the cipher.getAlgorithm()
to the SecretKeySpec
constructor.
I like how you used try-with-resources
to handle the IO in the file-encryption. But there is a somewhat cleaner way than manually handling the bytes you have there.
Consider the following methods:
public static void encrypt(byte keyBytes, byte ivBytes, Path in, Path out) throws GeneralSecurityException, IOException
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, cipher.getAlgorithm());
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
try (OutputStream os = new CipherOutputStream(Files.newOutputStream(out), cipher))
Files.copy(in, os);
public static void decrypt(byte keyBytes, byte ivBytes, Path in, Path out) throws GeneralSecurityException, IOException
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, cipher.getAlgorithm());
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
try (InputStream is = new CipherInputStream(Files.newInputStream(in), cipher))
Files.copy(is, out);
Here we can notice some duplication and extract the Cipher initialization routine into a separate method to simplify the code to:
public static void encrypt(byte keyBytes, byte ivBytes, Path in, Path out) throws GeneralSecurityException, IOException
Cipher cipher = initializeCipher(Cipher.ENCRYPTION_MODE, keyBytes, ivBytes);
try (OutputStream os = new CipherOutputStream(Files.newOutputStream(out), cipher))
Files.copy(in, os);
Note that I explicitly changed from File
to the newer (and more modern Path
from the java.nio
package)
Be aware that even here you can allow the user to specify the Cipher by allowing them to pass a String as cipherSpec
or something along these lines.
On that note: you can use that initialization method even in the code that en/decrypts byte directly.
Lastly I wanted to be a bit annoyed that neither generateIv
nor generateKey
provide any means to specify the number of bytes used. While 256-bit AES is generally considered secure, some users might want to opt for the extra security of using a 512-bit key. Also I think a 16-bit IV is probably a bit too short.
Additionally I'd recommend using SecureRandom.getInstanceStrong()
over new SecureRandom()
because this gets you a definitively strong SecureRandom instead of "the first that was available".
add a comment |Â
up vote
2
down vote
up vote
2
down vote
Not directly related to the security of this (I'm not remotely qualified to talk about it anyways):
CryptoUtils.doCrypto(Cipher.ENCRYPT_MODE, ...);
CryptoUtils.doCrypto(Cipher.DECRYPT_MODE, ...);
^^ this is an example of a state machine in a method. You want to avoid these in high-level languages like java. They don't make for a nice interface in usage. Instead you want an encrypt
and a decrypt
. This pushes the "mode" parameter into the name of your method.
If it's easier to implement the code itself as a state-machine, that's just fine, as long as you do so internally.
public static void encrypt(byte keyBytes, byte ivBytes, byte cleartext) throws GeneralSecurityException
return doCrypto(Cipher.ENCRYPT_MODE, keyBytes, ivBytes, cleartext);
If you were to aim for a more general-purpose cryptography utility class, you could expose an overload that allows the user to specify which cipher they want to use.
If you decide to do so, you can pass the cipher.getAlgorithm()
to the SecretKeySpec
constructor.
I like how you used try-with-resources
to handle the IO in the file-encryption. But there is a somewhat cleaner way than manually handling the bytes you have there.
Consider the following methods:
public static void encrypt(byte keyBytes, byte ivBytes, Path in, Path out) throws GeneralSecurityException, IOException
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, cipher.getAlgorithm());
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
try (OutputStream os = new CipherOutputStream(Files.newOutputStream(out), cipher))
Files.copy(in, os);
public static void decrypt(byte keyBytes, byte ivBytes, Path in, Path out) throws GeneralSecurityException, IOException
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, cipher.getAlgorithm());
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
try (InputStream is = new CipherInputStream(Files.newInputStream(in), cipher))
Files.copy(is, out);
Here we can notice some duplication and extract the Cipher initialization routine into a separate method to simplify the code to:
public static void encrypt(byte keyBytes, byte ivBytes, Path in, Path out) throws GeneralSecurityException, IOException
Cipher cipher = initializeCipher(Cipher.ENCRYPTION_MODE, keyBytes, ivBytes);
try (OutputStream os = new CipherOutputStream(Files.newOutputStream(out), cipher))
Files.copy(in, os);
Note that I explicitly changed from File
to the newer (and more modern Path
from the java.nio
package)
Be aware that even here you can allow the user to specify the Cipher by allowing them to pass a String as cipherSpec
or something along these lines.
On that note: you can use that initialization method even in the code that en/decrypts byte directly.
Lastly I wanted to be a bit annoyed that neither generateIv
nor generateKey
provide any means to specify the number of bytes used. While 256-bit AES is generally considered secure, some users might want to opt for the extra security of using a 512-bit key. Also I think a 16-bit IV is probably a bit too short.
Additionally I'd recommend using SecureRandom.getInstanceStrong()
over new SecureRandom()
because this gets you a definitively strong SecureRandom instead of "the first that was available".
Not directly related to the security of this (I'm not remotely qualified to talk about it anyways):
CryptoUtils.doCrypto(Cipher.ENCRYPT_MODE, ...);
CryptoUtils.doCrypto(Cipher.DECRYPT_MODE, ...);
^^ this is an example of a state machine in a method. You want to avoid these in high-level languages like java. They don't make for a nice interface in usage. Instead you want an encrypt
and a decrypt
. This pushes the "mode" parameter into the name of your method.
If it's easier to implement the code itself as a state-machine, that's just fine, as long as you do so internally.
public static void encrypt(byte keyBytes, byte ivBytes, byte cleartext) throws GeneralSecurityException
return doCrypto(Cipher.ENCRYPT_MODE, keyBytes, ivBytes, cleartext);
If you were to aim for a more general-purpose cryptography utility class, you could expose an overload that allows the user to specify which cipher they want to use.
If you decide to do so, you can pass the cipher.getAlgorithm()
to the SecretKeySpec
constructor.
I like how you used try-with-resources
to handle the IO in the file-encryption. But there is a somewhat cleaner way than manually handling the bytes you have there.
Consider the following methods:
public static void encrypt(byte keyBytes, byte ivBytes, Path in, Path out) throws GeneralSecurityException, IOException
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, cipher.getAlgorithm());
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
try (OutputStream os = new CipherOutputStream(Files.newOutputStream(out), cipher))
Files.copy(in, os);
public static void decrypt(byte keyBytes, byte ivBytes, Path in, Path out) throws GeneralSecurityException, IOException
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, cipher.getAlgorithm());
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
try (InputStream is = new CipherInputStream(Files.newInputStream(in), cipher))
Files.copy(is, out);
Here we can notice some duplication and extract the Cipher initialization routine into a separate method to simplify the code to:
public static void encrypt(byte keyBytes, byte ivBytes, Path in, Path out) throws GeneralSecurityException, IOException
Cipher cipher = initializeCipher(Cipher.ENCRYPTION_MODE, keyBytes, ivBytes);
try (OutputStream os = new CipherOutputStream(Files.newOutputStream(out), cipher))
Files.copy(in, os);
Note that I explicitly changed from File
to the newer (and more modern Path
from the java.nio
package)
Be aware that even here you can allow the user to specify the Cipher by allowing them to pass a String as cipherSpec
or something along these lines.
On that note: you can use that initialization method even in the code that en/decrypts byte directly.
Lastly I wanted to be a bit annoyed that neither generateIv
nor generateKey
provide any means to specify the number of bytes used. While 256-bit AES is generally considered secure, some users might want to opt for the extra security of using a 512-bit key. Also I think a 16-bit IV is probably a bit too short.
Additionally I'd recommend using SecureRandom.getInstanceStrong()
over new SecureRandom()
because this gets you a definitively strong SecureRandom instead of "the first that was available".
answered May 8 at 18:18
Vogel612â¦
20.9k345124
20.9k345124
add a comment |Â
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f186925%2fjava-aes-cbc-encryption-decryption%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password