Encryption done in java using AES and wanted to decrypt in Python, but the result is empty in python (no errors).
See the code before marking as duplicate. I want to detect problem in my code.
java encryption code:
public static String encrypt(String plainText) throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
SecretKey secretKey = keyGenerator.generateKey();
String keyStr = Base64.encodeToString(secretKey.getEncoded(), Base64.NO_WRAP);
byte[] initVector = new byte[16];
(new Random()).nextBytes(initVector);
IvParameterSpec iv = new IvParameterSpec(initVector);
SecretKeySpec skeySpec = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] plainTextByte = plainText.getBytes();
byte[] encryptedByte = cipher.doFinal(plainTextByte);
byte[] messagebytes = new byte[initVector.length + encryptedByte.length];
System.arraycopy(initVector, 0, messagebytes, 0, 16);
System.arraycopy(encryptedByte, 0, messagebytes, 16, encryptedByte.length);
return Base64.encodeToString(messagebytes, Base64.NO_WRAP);
}
Python code
def decrypt(key, message):
"""
Input encrypted bytes, return decrypted bytes, using iv and key
"""
byte_array = message.encode("UTF-8")
iv = byte_array[0:16] # extract the 16-byte initialization vector
messagebytes = byte_array[16:] # encrypted message is the bit after the iv
cipher = AES.new(key.encode("UTF-8"), AES.MODE_CBC, iv)
decrypted_padded = cipher.decrypt(messagebytes)
decrypted = unpad(decrypted_padded)
return decrypted.decode("UTF-8");
I see that the Java is explicitly encoding the string as Base 64 at return Base64.encodeToString(messagebytes, Base64.NO_WRAP);
But i see that you are again encoding in python byte_array = message.encode("UTF-8") where in you will have to decode the encrypted message
# Standard Base64 Decoding
decodedBytes = base64.b64decode(encodedStr)
decodedStr = str(decodedBytes, "utf-8")
And only then Decrypt the decoded message.
Related
I have implemented a CBC Mode AES encryption and decryption mechanism in which I am generating Random IV and Random Secret key for each encryption attempt which is recommended.
Now, I have saved my key in a separate file and IV in another file, but after going through the different forums I have found that the IV should not be kept secure and shall be appended with the Ciphertext while encryption and at the time of decryption we can get the 16 bytes plucked out from that cipher byte array..
Now, I tried a snippet of code to achieve the same, but the result were not good as the first block was not encrypting properly; however the rest of the block does.
Can someone tell me whats wrong with my approach?
Any help will be highly appreciated thanks :).
public static byte[] encrypt (byte[] plaintext,SecretKey key,byte[] IV ) throws Exception {
//Get Cipher Instance
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
//Create SecretKeySpec
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
//Create IvParameterSpec
IvParameterSpec ivSpec = new IvParameterSpec(IV);
System.out.println( "IV encrypt= " + ivSpec );
//Initialize Cipher for ENCRYPT_MODE
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
//Perform Encryption
byte[] cipherText = cipher.doFinal(plaintext);
ByteArrayOutputStream b = new ByteArrayOutputStream();
b.write(IV);
b.write( cipherText );
return b.toByteArray();
}
--------------------------------------------------------------------------
public static String decrypt (byte[] cipherText, SecretKey key ) throws Exception
{
//Get Cipher Instance
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
//Create SecretKeySpec
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
byte[] iv = Arrays.copyOfRange( cipherText , 0, 16);
//Create IvParameterSpec
IvParameterSpec ivSpec = new IvParameterSpec(iv);
//Initialize Cipher for DECRYPT_MODE
cipher.init(Cipher.DECRYPT_MODE, keySpec,ivSpec);
//Perform Decryption
byte[] decryptedText = cipher.doFinal(cipherText);
return new String(decryptedText);
}
----------------------------------------------------------------------------
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
// Generate Key
SecretKey key = keyGenerator.generateKey();
// Generating IV.
byte[] IV = new byte[16];
SecureRandom random = new SecureRandom();
random.nextBytes(IV);
System.out.println("Original Text : " + plainText);
byte[] cipherText = encrypt(plainText.getBytes("UTF-8") ,key, IV);
String decryptedText = decrypt(cipherText,key, IV);
System.out.println("DeCrypted Text : "+decryptedText);
RESULT
Original Text : This is a plain text which need to be encrypted by AES Algorithm with CBC Mode
DeCrypted Text : ûª¯Î¥pAï2EÞi+¼‹Ý$8ŶÄDDNâOæàtext which need to be encrypted by AES Algorithm with CBC Mode
Just because you copy out the IV here:
byte[] iv = Arrays.copyOfRange( cipherText , 0, 16);
Doesn't mean it isn't still present when you try to decrypt it during:
byte[] decryptedText = cipher.doFinal(cipherText);
You should decrypt everything in ciphertext except for the first 16 bytes. At the moment you're also performing AES decryption on the IV - which is why you're getting garbage.
I want to encrypt and decrypt a String from an EditText using AndroidKeyStore. My problem is that at the decrypt process is get a BadPaddingException.
Key generator code:
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT).
setBlockModes(KeyProperties.BLOCK_MODE_GCM).setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE).build();
keyGenerator.init(keyGenParameterSpec);
keyGenerator.generateKey();
Encryption code:
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
KeyStore.SecretKeyEntry secretKeyEntry = (KeyStore.SecretKeyEntry) keyStore.getEntry(ALIAS, null);
SecretKey secretKey = secretKeyEntry.getSecretKey();
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
cipherIV = cipher.getIV();
plainText.setText(new String(cipher.doFinal(plainText.getText().toString().getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8));
Decryption code:
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
final KeyStore.SecretKeyEntry secretKeyEntry = (KeyStore.SecretKeyEntry) keyStore.getEntry(ALIAS, null);
final SecretKey secretKey = secretKeyEntry.getSecretKey();
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
final GCMParameterSpec spec = new GCMParameterSpec(128, cipherIV);
cipher.init(Cipher.DECRYPT_MODE, secretKey, spec);
byte[] decrypted = cipher.doFinal(plainText.getText().toString().getBytes(StandardCharsets.UTF_8));
plainText.setText(new String(decrypted, StandardCharsets.UTF_8));
This line is in error:
plainText.setText(new String(cipher.doFinal(plainText.getText().toString().getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8));
If we break it apart, we have something like
byte [] cipherBytes = cipher.doFinal(plainText.getText().toString().getBytes(StandardCharsets.UTF_8));
plainText.setText(new String(cipherBytes, StandardCharsets.UTF_8);
The problem is that cipherBytes is a sequence of arbitrary bytes rather than the characters of a string. The String constructor will silently replace invalid characters with something else, a process which corrupts the data.
If you want to display the cipher bytes or otherwise send it to a character oriented channel you must encode it. Typically encodings are base64 or hex. To decrypt the String you must then decode it to bytes first and then decrypt it.
Example:
byte [] cipherBytes = cipher.doFinal(plainText.getText().toString().getBytes(StandardCharsets.UTF_8));
plainText.setText(Base64.encodeToString(cipherBytes, Base64.DEFAULT));
And on decrypt:
byte[] cipherBytes = Base64.decode(plainText.getText().toString(), Base64.DEFAULT);
byte[] decrypted = cipher.doFinal(cipherBytes);
byte[] decrypted = cipher.doFinal(plainText.getText().toString().getBytes(StandardCharsets.UTF_8));
This line may not work well because of the call of getBytes(StandardCharsets.UTF_8). If your EditText is hex representation, try to convert it to string and then invoke getBytes(). E.g.
public static byte[] convertHexStringToByteArray(String hexString) {
int l = hexString.length();
byte[] data = new byte[l/2];
for (int i = 0; i < l; i += 2) {
data[i/2] = (byte)((Character.digit(hexString.charAt(i), 16) << 4) + Character.digit(hexString.charAt(i+1), 16));
}
return data;
}
Currently having issues with the decryption of an AES hash.
When i encrypt using AES256 i get the following result (After base64 encoding)
07sKQfb9dN86XAMxFmVKHQAAAAAAAAAAAAAAAAAAAAA=
I believe my issue is with the AAAAAAAA etc. for some reason I think anyway the padding isn't being removed during decryption.
My code
public String encrypt(String key, String initVector, String value) throws Exception {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"), 0, initVector.getBytes().length);
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), 0, key.getBytes().length, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = new byte[32];
cipher.doFinal(value.getBytes(), 0, value.getBytes().length, encrypted, 0);
System.out.println("Base64: " + Base64.encode(encrypted));
System.out.println("Hex: " + bytesToHex(encrypted));
return Base64.encode(encrypted);
}
public String decrypt(String key, String initVector, String encrypted) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"), 0, initVector.getBytes().length);
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), 0, key.getBytes().length, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] decrypted = new byte[64];
cipher.doFinal(Base64.decode(encrypted), 0, Base64.decode(encrypted).length, decrypted, 0);
return new String(decrypted);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
What i am expecting when i encode base64 07sKQfb9dN86XAMxFmVKHQ=
String i am encrypting: Test
Secret key: password12345678password12345678
byte[] decrypted = new byte[64];
you created a 64 byte array but the decrypted value may be shorter (Java doesn't use zero string terminator unlike C does). So you should not make assumption over the parameter length
try using
encrypted = cipher.doFinal(value.getBytes());
and
decrypted = cipher.doFinal(Base64.decode(encrypted));
I use aes-256-cbc for data encrypt/decrypt.
When decrypt data with php code i'm not getting an error.
Php code like this:
$key = 'd7df7d66bc110ba2e03a3a647ecd8c3f';
$iv = '73e8c03a24b4bb1e';
$encrypt_method = "AES-256-CBC";
$encrypted_text = "K0JOVFQreEQ4TlRjWGwyNXBhejRtQWVWU2doVkRsUjkvaEhQamlRdWRIWWpQQ0dVWmNpNTJXR3hhbXdWbDN4WGJpcGZTblpHRVpyeGpDY2lLc3o4cGJEeHdwdmZDTUlxalRyeDdVYlB4SmJtVUhEQkVSN3VZSklWb2h0dlZkYmY=";
$output = openssl_decrypt(base64_decode($data), $encrypt_method, $key, 0, $iv);
output:
{"Sayfa":"b2pneVd4bUcQT09","TelNo":"905373603204","Sifre":"9cbf8a4dcb8e30682b927f352d6559a0"}
But when decrypt data with android ( java ) code i getting an error like this:
javax.crypto.IllegalBlockSizeException:Cipher functions:OPENSSL_internal:WRONG_FINAL_BLOCK_LENGTH
Java code :
String key = "d7df7d66bc110ba2e03a3a647ecd8c3f";
String iv = "73e8c03a24b4bb1e";
String encryptedText = "K0JOVFQreEQ4TlRjWGwyNXBhejRtQWVWU2doVkRsUjkvaEhQamlRdWRIWWpQQ0dVWmNpNTJXR3hhbXdWbDN4WGJpcGZTblpHRVpyeGpDY2lLc3o4cGJEeHdwdmZDTUlxalRyeDdVYlB4SmJtVUhEQkVSN3VZSklWb2h0dlZkYmY=";
AESUtil.decrypt(encryptedText,key,iv);
public String decrypt(String encryptedText, String key, String iv) {
byte[] cipheredBytes = Base64.decode(encryptedText,Base64.DEFAULT);
byte[] keyBytes = getKeyBytes(key);
byte[] ivBytes = getIvBytes(iv);
return new String(decrypt(cipheredBytes, keyBytes, ivBytes), "UTF-8");
}
public byte[] decrypt(byte[] cipherText, byte[] key, byte [] initialVector) {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(initialVector);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
return cipher.doFinal(cipherText);
}
private byte[] getIvBytes(String iv) {
byte[] keyBytes= new byte[16];
byte[] parameterKeyBytes= iv.getBytes(characterEncoding);
System.arraycopy(parameterKeyBytes, 0, keyBytes, 0, Math.min(parameterKeyBytes.length, keyBytes.length));
return keyBytes;
}
private byte[] getKeyBytes(String key){
byte[] keyBytes= new byte[32];
byte[] parameterKeyBytes= key.getBytes(characterEncoding);
System.arraycopy(parameterKeyBytes, 0, keyBytes, 0, Math.min(parameterKeyBytes.length, keyBytes.length));
return keyBytes;
}
where is the error in java code?
I don't have Android, and on regular Java (Oracle JavaSE 8) after replacing that Base64 decoder with the Oracle one I don't get that error, but I do get BadPaddingException because your data is actually double base64 encoded. In your PHP this works because you decode explicitly once, then openssl_decrypt with options=0 does the second decode.
In Java if I base64-decode the data twice (and use key and IV directly as bytes, as PHP did, even though they look like they should be hex-decoded) it does decrypt successfully and as expected. According to the doc on developer.android.com I think Android's Base64.DEFAULT is the same as Oracle's default, but I can't verify that myself.
I am doing a simple implementation for AES Algorithm.
// Get the Key Generator
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); // 192 and 256 bits may not be available
// Generate the secret key specs.
SecretKey secret = kgen.generateKey();
byte[] raw = secret.getEncoded();
String key = new String(Base64.encodeBase64(raw));
I am saving "key" in database.
Now again in another operation i am fetching a "key" from database n trying to decrypt data. i call decryption function as
String dencryptReq = Utils.decrypt2(new String(Base64.decodeBase64(secretKeyInformation.getSecretKey().getBytes())),Base64.decodeBase64(encryptReq.getBytes()) );
public static String decrypt2(String key, byte[] encrypted)
throws GeneralSecurityException {
byte[] raw = Base64.decodeBase64(key.getBytes());
if (raw.length != 16) {
throw new IllegalArgumentException("Invalid key size.");
}
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec,
new IvParameterSpec(new byte[16]));
byte[] original = cipher.doFinal(encrypted);
return new String(original, Charset.forName("US-ASCII"));
}
But it is throwing me invalid key size exception.
If i do in one time this without saving in databse and fetching from database it is working fine.
I have tried your code with some modifications I have used apache commons codec library for Base64 conversion,
/* Derive the key, given password and salt. */
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec("password".toCharArray(), "salt".getBytes(), 65536, 128);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
/* 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! My data is here.. !".getBytes("UTF-8"));
System.out.println("cipher :"+new String(ciphertext));
/*String-key convertion */
String stringKey=Base64.encodeBase64String(secret.getEncoded());//To String key
byte[] encodedKey = Base64.decodeBase64(stringKey.getBytes());
SecretKey originalKey = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");// Convert from string
/* Decrypt the message, given derived key and initialization vector. */
Cipher cipher1 = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher1.init(Cipher.DECRYPT_MODE, originalKey, new IvParameterSpec(iv));
String plaintext = new String(cipher1.doFinal(ciphertext), "UTF-8");
System.out.println(plaintext);
This code worked perfectly in my system.