Java AES Decryption: random chars & message at the end - java

I have a problem with decrypting a message using AES. At the end when I expect a message, e.g.
ala123
Instead of that I receive sth like:
...6�b}\7�k�8�vFP�8~%��_zժF��FW��O_e���ó������������ala123
The message I pass to encryption is built as:
cipher key is SHA-256 from AES_TOKEN
cipher IV is some characters, which are then stored in the message (at the beginnig)
decrypted message is wrapped up into Base64
The question is why at the end I eventually receive my expected message, but with a lot of rubbish chars at the beggining?
My encryption code is:
private static final String AES_TOKEN = "my_very_secret_token";
// encrypted is base64 string
public String decrypt(String encrypted) throws Exception {
byte[] decrypted = Base64.getDecoder().decode(encrypted);
return new String(aesDecrypt(decrypted), "UTF-8");
}
private byte[] aesDecrypt(byte[] message) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] token = MessageDigest.getInstance("SHA-256").digest(AES_TOKEN.getBytes());
SecretKeySpec secretKey = new SecretKeySpec(token, "AES");
IvParameterSpec iv = new IvParameterSpec(Arrays.copyOf(message, 16));
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
return cipher.doFinal(message);
}

It looks like you aren't removing the IV from the beginning of message after reading it in to iv. That would explain the garbage being at the start of the decrypted message.

Related

RSA Decryption JAVA BadPadding

I am trying to integrate an API where I have to send the data in RSA encrypted and BASE64 encoded form. In response I get a BASE64 String which is supposed to be decoded and decrypted at my end. I am able to send the data in encrypted and encoded form. Also I am getting the response BASE64 String but I am NOT ABLE TO DECRYPT the string. In my decryption I am getting following error:
Exception in thread "main" java.lang.RuntimeException: javax.crypto.BadPaddingException: Decryption error
I have used following for encryption:
String result=new Gson().toJson(parameters);
byte[] cleartext = result.getBytes("UTF-8");
PublicKey publicKey=readPublicKey(path);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] ciphertext=cipher.doFinal(cleartext);
String securePayload = Base64.getEncoder().encodeToString(ciphertext);
Parameters is a (String,String) map and I have put the parameters in it that are required to POST in the API. After encryption I am sending "securePayload" in POST request and in response I am getting a BASE64 encoded string which I am supposed to decode and decrypt.
For decryption I am using following code:
public static String getDecrypted(String data, PrivateKey key) {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] dataArray=Base64.getDecoder().decode(data);
byte[] decryptedbytes = cipher.doFinal(dataArray);
return new String(decryptedbytes);
}
Above function I am using in following code to decrypt the String "data" which I am getting in API response:
PrivateKey pvtKey= readFromPrivateKey("C:\\Users\\Administrator\\Desktop\\myPrivateKey.key");
String result=getDecrypted(data, pvtKey);
To read the private key I have used following code:
public static PrivateKey readFromPrivateKey(String path) {
String privateKeyContent = new String(Files.readAllBytes(Paths.get(path)));
privateKeyContent = privateKeyContent.replaceAll(System.lineSeparator(), "").replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "").replaceAll("\\s+", "");
System.out.println(privateKeyContent);
KeyFactory kf = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec keySpecPKCS8 = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyContent));
PrivateKey privKey = kf.generatePrivate(keySpecPKCS8);
return privKey;
}
Please help in the same. What wrong am I doing? I have checked that the Public and Private key pair used at my end is correct.

Few strings are failed to encrypt using AES256 in Java

This specific case is coming whenever i tried to encrypt single char strings in application. When i tried to encrypt the same string with main method then i was able to encrypt it. But when i run with the application then this specific issue is coming. I felt this is strange because it is working with the main method.
My encryption code will be as follows.
public static String encryptWithAES256(String strToEncrypt) throws Exception
{
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] encodedhash = digest.digest(KEY.getBytes(StandardCharsets.UTF_8));
IvParameterSpec ivspec = new IvParameterSpec(Arrays.copyOf(KEY.getBytes(),16));
SecretKeySpec secretKey = new SecretKeySpec(encodedhash, AES_ENCRYPTION_ALGORITHM);
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivspec);
return new String(Base64.encodeBase64(cipher.doFinal(strToEncrypt.getBytes(CHARACTER_ENCODING))));
}

String encrypted by Java AES, Decrypt in Python Issue

I have a Java code to encrypt(AES encryption with key) a JSON and store it in Oracle database LONG RAW column.
I have a python code to read the data from that column and decrypt. Below is the code i am using for decryption.
The code is running fine but i am not able to see the JSON after decryption instead some unreadable string is getting printed.
c = conn.cursor()
c.execute(u'select KEY_VALUE from TEST1')
encoded = "";
for row in c:
encoded = base64.b64encode(row[0])
print(encoded)
key = 'F50D518354690A8630BCE683B7AC8F55'
aes = AES.new(key, AES.MODE_CBC, 16 * b'\0')
print(aes.decrypt(encoded))
conn.close()
Can you please point where am i wrong.
Also the Encoded String is getting printed fine and matches the value in Oracle database.
I tried using AES.MODE_ECB since the java code was not iv to encrypt but still the same issue
Below is the Encryption and Decryption Code in java. I want to replicate the decrypt in python.
decrypt
public static String decryptText(byte[] byteCipherText, SecretKey secKey) throws Exception {
// AES defaults to AES/ECB/PKCS5Padding in Java 7
System.out.println(secKey.toString());
Cipher aesCipher = Cipher.getInstance("AES");
aesCipher.init(Cipher.DECRYPT_MODE, secKey);
byte[] bytePlainText = aesCipher.doFinal(byteCipherText);
return new String(bytePlainText);
}
encrypt
public static byte[] encryptText(String plainText, String key) throws Exception {
// AES defaults to AES/ECB/PKCS5Padding in Java 7
System.out.println("key is "+key);
SecretKey secKey=decodeKeyFromString(key);
Cipher aesCipher = Cipher.getInstance("AES");
aesCipher.init(Cipher.ENCRYPT_MODE, secKey);
byte[] byteCipherText = aesCipher.doFinal(plainText.getBytes());
return byteCipherText;
}
The decodeKeyFromString method has just the below line :
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
I Even tried writing the encrypted value as blob in oracle and then doing decryption but still the same garbage values.
for row in c:
encoded=row[0]
encrypted= open(blobpath,'wb')
encrypted.write(encoded.read())
encrypted.close()
with open('encrypted.txt', 'r') as myfile:
data=myfile.read().replace('\n', '')
key='F50D518354690A8630BCE683B7AC8F55'
aes = AES.new(key, AES.MODE_ECB)
e=unpad(aes.decrypt(base64.b64encode(data)))

Part of Decrypt non-sense

I am working a chat in android, here I am using the next methods for key generation, encrypt and decrypt messages. The problem is that when I send a message for example "hola" in the other side I get "holgAAAAAAAAAAAAAAA". Could you help to fix this?.
private byte[] K;
public void setK(){
KeyGenerator KeyGen=KeyGenerator.getInstance("AES");
KeyGen.init(128);
SecretKey key=KeyGen.generateKey();
K = key.getEncoded();
}
public String encrypt(byte[] input){
try {
IvParameterSpec iv = new IvParameterSpec(Base64.decode("Hola".getBytes(), Base64.DEFAULT));
SecretKeySpec key = new SecretKeySpec(K, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
int ctLength = cipher.update(input, 0, input.length, cipherText, 0);
ctLength += cipher.doFinal(cipherText, ctLength);
return Base64.encodeToString(cipherText, Base64.DEFAULT);
} catch (Exception e) {
Log.e(JUAN, "failed to encrypt ", e);
}
return null;
}
public String decrypt(byte[] input){
try {
IvParameterSpec iv = new IvParameterSpec(Base64.decode("Hola".getBytes(), Base64.DEFAULT));
SecretKeySpec key = new SecretKeySpec(K, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] plainText = new byte[cipher.getOutputSize(input.length)];
int ctLength = cipher.update(input, 0, input.length, plainText, 0);
ctLength += cipher.doFinal(plainText, ctLength);
return Base64.encodeToString(plainText, Base64.DEFAULT);
} catch (Exception e) {
Log.e(JUAN, "failed to decrypt ", e);
}
return null;
}
EDIT
Here is my calling, for example to encrypt "Hola".
encrypt(Base64.decode("Hola".getBytes(), Base64.DEFAULT));
decrypt(Base64.decode(ciphertext, Base64.DEFAULT));
There are multiple problems with your code:
Your input and output types of the decryption function are reversed. If you encrypt a byte[], you should get one out when you decrypt it. If your ciphertext is a Base64 String then the decryption method should take such a String and not a byte[].
String encrypt(byte[] plaintext) {
...
return Base64.encodeToString(cipher.doFinal(plaintext), Base64.DEFAULT);
}
byte[] encrypt(String ciphertext) {
...
return cipher.doFinal(Base64.decode(ciphertext.getBytes("UTF-8"), Base64.DEFAULT));
}
You're passing a single plaintext and ciphertext into their respective method, but then use cipher.update() and cipher.doFinal(). This is not necessary. You should use a single cipher.doFinal() call without a previous buffer. Encryption example:
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cipherText = cipher.doFinal(plaintext);
Since "Hola" is supposed to be what the user typed in, it doesn't make any sense to decode from Base 64. Not all strings that someone types in are valid Base 64 encoded so that they can be decoded. You shouldn't decode the input at all, but pass it directly into the encrypt() function.
Using String#getBytes() is unsafe, because it uses the default Charset of the system. The decryption might not succeed when a different default Charset is used on the receiving system. You should specify the Charset yourself and get the String from the byte[] after decryption:
String ciphertext = encrypt(plaintext.getBytes("UTF-8"));
String recoveredPlaintext = new String(decrypt(ciphertext), "UTF-8");
You're not using your static IV.
Security Issues:
You're using ECB mode. Don't do this! It's not semantically secure. Use at least CBC mode with a random IV. The IV doesn't have to be hidden, so you can simply prepend it to the ciphertext.
You're not authenticating the ciphertext. Your system might be vulnerable to the padding oracle attack. You should either use an encrypt-then-MAC approach with a strong MAC like HMAC-SHA256 or use an authenticated mode of operation for AES like GCM or EAX.
Use for example this library by Isaac Potoczny-Jones of which is compatible with Android. It supports AES-CBC with a random IV and ciphertext authentication with HMAC-SHA256.
Your code is OK assuming that the parameter input in your public String decrypt(byte[] input) method is successfully Base64 decoded from the cipher text by the caller (because your encrption returns Base64 encoded cipher string). But, in the decrypt() method you are creating a byte array plainText by getOutputSize() method. That makes plainText an array of size of multiple of AES Block Size(16). For your case, plainText is a 16 byte array. So after decrypting and removing the paddings from cipher text the plainText contains the decrypted text with some zeroes, those zeroes are then encoded into AAA...As.
So use
return Base64.encodeToString(plainText, 0, ctLength, Base64.DEFAULT);
instead of
return Base64.encodeToString(plainText, Base64.DEFAULT);
Note: You are using ECB mode, so your IvParameterSpec is useless. Use CBC mode instead.
Edition: Your calling is not OK. Try this
//Encryption side
String text = "hola, hi, anything u want";
byte[] plainText = text.getBytes("UTF-8");
String base64 = encrypt(plainText);
// Decryption side
byte[] cipherText = Base64.decode(base64, Base64.DEFAULT);
String plainEncodedText = decrypt(cipherText);
byte[] plainTextAsByte = Base64.decode(plainEncodedText, Base64.DEFAULT);
String plainTextAgain = new String(plainTextAsByte , "UTF-8");
Now print the plainTextAgain and hope this will work!

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.

Categories

Resources