AES encryption Cipher.init() crashing - java

I'm trying to encrypt a file using AES in Java, but it's not working. I put in some prints to see where it failed, the first two are printing but not the third. I checked that the key and IV are the right sizes, 256 bits and 128 bits respectively. If they are both valid I don't know what else could be going wrong. Are there any glaring mistakes in my code? I haven't done this before. Any help is greatly appreciated!
public static String encryptAES(byte[] data, byte[] key, byte[] iv) throws Exception {
Key k = new SecretKeySpec(key, "AES");
System.out.println("key set");
Cipher c = Cipher.getInstance("AES/CBC/PKCS5PADDING");
System.out.println("cipher created");
c.init(Cipher.ENCRYPT_MODE, k, new IvParameterSpec(iv));
System.out.println("cipher initialised");
byte[] encryptedDataBytes = c.doFinal(data);
String encryptedData = Base64.getEncoder().encodeToString(encryptedDataBytes);
return encryptedData;
}

Related

AES256 can not decrypt previous encrypted data after restarting server

Currently, I'm having a problem that I don't know how to solve. It's the decryption and encryption of the string using AES256. Everything was working fine until I restarted the server and I couldn't decode the previous data.
I tried saving the salt and IVParameter to decrypt for next time, but it doesn't work.
private static final String SECRET_KEY = "my_key";
private static final byte[] SALT;
private static final SecureRandom random;
private static final IvParameterSpec ivspec;
static {
random = new SecureRandom();
SALT = new byte[16];
random.nextBytes(SALT);
byte[] bytesIV = new byte[16];
random.nextBytes(bytesIV);
ivspec = new IvParameterSpec(bytesIV);
}
public static String encrypt(String stringToEncrypt) {
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(SECRET_KEY.toCharArray(), SALT, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKeySpec secretKey = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivspec);
return Base64.getEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes(StandardCharsets.UTF_8)));
} catch (Exception e) {
System.out.println("Error while encrypting: " + e);
}
return null;
}
public static String decrypt(String stringToDecrypt) {
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(SECRET_KEY.toCharArray(), SALT, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKeySpec secretKey = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivspec);
return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)));
} catch (Exception e) {
System.out.println("Error while decrypting: " + e);
}
return null;
}
SecureRandom initialises itself differently everytime you instantiate it. I.e., it will also create a different sequence of random values each time. Even if you initialise SALT with a fixed initial value, in the next step you overwrite it again by calling random.nextBytes(SALT). Either don't do that or instantiate SecureRandom with a seed, so it creates the same sequence of random numbers every time. But this is kind of counter-productive. Similarly, you also randomise IvParameterSpec.
You only need the nextBytes() result, if you want to generate new salt or IV values for a multiple users or a sequence of distinct encryption/decryption actions. AES being a symmetric cypher, you need to make sure that when decrypting a message, you use the same salt and IV (if any) which were used for encryption. Try this in order to get identical encryption results:
static {
random = new SecureRandom(); // not used in this example
SALT = "I am so salty!".getBytes(StandardCharsets.UTF_8);
byte[] bytesIV = "my super fancy IV".getBytes(StandardCharsets.UTF_8);
ivspec = new IvParameterSpec(Arrays.copyOfRange(bytesIV, 0, 16));
}
Of course, in the example above I am assuming that actually salt and IV were initially created randomly, then securely saved or transmitted to the recipient, and then loaded/received and used to decrypt the message. In a real-world scenario, you would transmit or store salt and IV asymmetrically encrypted (using public-key cryptography), while the message itself (which usually is much bigger than secret key, salt and IV) is encrypted using the much faster and more efficient symmetric AES256 algorithm.
P.S.: The Arrays.copyOfRange(bytesIV, 0, 16) is necessary, because in contrast to the salt the IV must be exactly 16 bytes long. The salt is more flexible.
Update: Actually, it is not necessary to encrypt salt and IV. They just make sure that the same input and secret key do not yield the same encrypted message in order to make attacks based on known cleartext more difficult. This is also why e.g. when storing salted hashes in a database, you store the salt values as cleartext along with the salted password hash (not the password itself!), because you need them every time you want to validate a user password.

BadPaddingException when decrypting using a different Cipher object

Please excuse the hackjob! Still new at coding and was utilizing a lot of sys outs to troubleshoot, and this is my first post at StackOverflow. Thank you for the help!
So I've been working on trying to encrypt String objects using the javax.crypto.Cipher API, and I have found some success, but only when it is using the same instance of the Cipher object. However, for the purposes of my project, I am encrypting text (String) to and decrypting text from a text file, and will not be accessing the same Cipher object every time.
I believe the issue is not from converting between the byte arrays and Strings, since the Base64 encoder seems to have taken care of that problem. The output of the byte arrays are identical pre-encoding and post-decoding, so that should isolate it as an issue during the decryption phase. What can be done so that my decryptPW method can use a different Cipher instance (with the same arguments passed) and not trigger a BadPaddingException?
private static String encryptPW(String pw){
byte[] pwBytes = pw.getBytes();
byte[] keyBytes = "0123456789abcdef".getBytes();
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
try {
Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding");
ciph.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] encryptedBytes = ciph.doFinal(pwBytes);
pw = Base64.getEncoder().encodeToString(ciph.doFinal(pwBytes));
for (byte b : encryptedBytes){
System.out.print(b);
}
System.out.println();
} catch (Exception e){
e.printStackTrace();
}
return pw;
}
private static String decryptPW(String pw){
byte[] pwBytes = Base64.getDecoder().decode(pw.getBytes());
for (byte b : pwBytes){
System.out.print(b);
}
System.out.println();
byte[] keyBytes = "0123456789abcdef".getBytes();
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
try {
Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding");
ciph.init(Cipher.DECRYPT_MODE, keySpec, ciph.getParameters());
pw = new String(ciph.doFinal(pwBytes));
} catch (Exception e){
e.printStackTrace();
}
return pw;
}
Again, thank you!
As you use CBC mode, you need to save the random Initialization Vector (IV) from the encryption cipher and provide it to the decryption cipher.
You can get it from the encryption cipher after init like:
ciph.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] iv = ciph.getIV();
And provide it to the decrypt cipher with something like:
ciph.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(iv));
You need to come up with a method of saving the IV. A common way is to prefix the encrypted data with the IV, so it's available when you need to initialize your decrypt cipher. It don't need to be secret.

AES-CTR double encryption reverses the ciphertext to plaintext

When I try to encrypt the ciphertext again with the same key, it produces the original plaintext.
Algoritm used is AES with COUNTER MODE. Key and IV remains the same.
Is this the way the algorithm is supposed to behave? And if, what is the use of Cipher.ENCRYTMODE which is to be given as the first parameter of Cipher.init()?
Here is the sample program with which I tested,
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class EncryptionTest {
public static void main(String[] args) throws Exception {
SecretKeySpec key = null;
IvParameterSpec ivSpec = null;
byte[] keyBytes = "usethiskeyusethiusethiskeyusethi".getBytes();
byte[] ivBytes = "usethisIusethisI".getBytes();
key = new SecretKeySpec(keyBytes, "AES"); //No I18N
ivSpec = new IvParameterSpec(ivBytes);
Cipher AesCipher = Cipher.getInstance("AES/CTR/NoPadding");
byte[] byteText = "Your Plain Text Here".getBytes();
AesCipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] byteCipherText = AesCipher.doFinal(byteText);
System.out.println("Encrypted : " + new String(byteCipherText));
AesCipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] bytePlainText = AesCipher.doFinal(byteCipherText);
System.out.println("Double Encrypted : " + new String(bytePlainText));
}
}
Yes, that is expected behavior. The CTR mode of operation for block ciphers makes a stream cipher out of a block cipher. Since stream ciphers work in a way that they generate a keystream and XOR the keystream with the plaintext to produce the ciphertext:
plaintext XOR AES-CTR(nonce, key) = ciphertext
The XOR operation works in a way where XORing x with a key k twice results in x again:
x ^ k ^ k = x
This is the reason why encryption and decryption are exactly the same operation for block ciphers in CTR mode (sans nonce generation and putting it into the ciphertext).
If you don't want that the encryption and decryption algorithm are the same, then you should use a different mode such as CBC, but there is nothing wrong with this kind of thing.
Beware that for CTR mode to be secure, you have to use a different nonce/IV under the same key for every encryption.

JAVA: AES Decryption

I'm currently running into problems decrypting my data. The base64 of the encoded string is being stored in the database. So, I'm printing out the encoded string and then trying to run it back through with "DECRYPT" instead of "ENCRYPT". However, I never get a value that the Decrypter method likes, it always gives me an error about parameters or the value not being 16 bytes.
public class crypto {
public static void main(String [] args) {
String s = args[0];
String s1 = args[1];
String ivkey = "thisisasecretkey";
byte[] ivraw = ivkey.getBytes();
SecretKeySpec skeySpec = new SecretKeySpec(ivraw, "AES");
if (s.equalsIgnoreCase("ENCRYPT")) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(s1.getBytes());
System.out.println(new String(Base64.encodeBase64(encrypted)));
} catch (Exception e) {
throw new RuntimeException(e);
}
} else {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(s1.getBytes());
System.out.println(new String(Base64.decodeBase64(encrypted)));
} catch (Exception e) {
e.printStackTrace();
}
}
return;
};
}
command:crypto "ENCRYPT" "password"
output: 5eQvSzPG1TE2AybgCmeV6A==
command:crytpo "DECRYPT" "5eQvSzPG1TE2AybgCmeV6A=="
output: java.security.InvalidKeyException: Parameters missing
I'm aware of the security flaws, that's not what I'm asking about and I would prefer answers/comments not get cluttered with best practices.
You should do base 64 decoding, and you should do that before decrypting.
You are not including the initialization vector (IV).
AES in CBC mode has both a 16 byte IV and the 16 byte symmetric key.
String IV = "AAAAAAAAAAAAAAAA"; // generate this randomly
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(IV.getBytes()));
byte[] encrypted = cipher.doFinal(s.getBytes());
Edit: as it turns out, encryption does not require a IV to be provided (as owlstead pointed out), but decryption does. The best bet would be to be explicit and use IV in both encryption and decryption. Change your decryption function to include the IV, and you will run into the other error in your code that owlstead pointed out.

RSA and AES Decrypt and Encrypt problem

I have generated on my android application a pair of RSA Keys.
I receive from a web service
- an AES Key, encrypted with my RSA public key
- a String encoded with the AES key.
So I must do the following:
- decrypt the AES Key
- decrypt the string with the obtained AES Key.
To generate the RSA Keys I did:
keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(size);
keypair = keyGen.genKeyPair();
privateKey = keypair.getPrivate();
publicKey = keypair.getPublic();
On RSA decrypt I use :
public static byte[] decryptRSA( PrivateKey key, byte[] text) throws Exception
{
byte[] dectyptedText = null;
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, key);
dectyptedText = cipher.doFinal(text);
return dectyptedText;
}
On AES decrypt I use:
public static byte[] decryptAES(byte[] key, byte[] text) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(text);
return decrypted;
}
So, in my code, to obtain the decrypted AES Key I do
byte[] decryptedAESKey = sm.decryptRSA(key, Base64.decode(ReceivedBase64EncryptedAESKey));
byte[] decryptedString = sm.decryptAES(decryptedAESKey, Base64.decode(ReceivedEncryptedAESString));
On the end I get a null for decryptedString.
What am I doing wrong ?
Well, the thing is that the key decrypted was 8 byte long and I had to make it 16 byte to be AES 128 bits compatible
So, I made a method to convert it back
private static byte[] GetKey(byte[] suggestedKey)
{
byte[] kRaw = suggestedKey;
ArrayList<Byte> kList = new ArrayList<Byte>();
for (int i = 0; i < 128; i += 8)
{
kList.add(kRaw[(i / 8) % kRaw.length]);
}
byte[] byteArray = new byte[kList.size()];
for(int i = 0; i<kList.size(); i++){
byteArray[i] = kList.get(i);
}
return byteArray;
}
And the rewritten decrypt method:
public static byte[] decryptAES(byte[] key, byte[] text) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(GetKey(key), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding","BC");
byte [] iv = new byte[cipher.getBlockSize()];
for(int i=0;i<iv.length;i++)iv[i] = 0;
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivSpec);
byte[] decrypted = cipher.doFinal(text);
return decrypted;
}
I'm not sure what language or libraries you are using (looks like Java?), but some really general things to try:
Did you get the encrypted string, ok? Check the length of ReceivedEncryptedAESString and the output of the Base64.decode to check they look alright.
AES decryption can't fail so it must be a problem in the library initialisation. Check the value/state of cipher after the construction step and the init step.
Try a simpler testcase: ignore the RSA encryption and just try to decrypt something using your Cipher object.

Categories

Resources