I am recently working on file encryption / decryption.
BadPaddingException: EVP_CipherFinal_ex: always occurs when I try to decrypt the file with the same key.
Code snippets will be posted below.
Am I doing something wrong?
thank you for your helps.
Encrypt
public static void encryptFile() {
File file = new File(Environment.getExternalStorageDirectory().getPath() + "/" + TARGET_FILE);
FileInputStream fileInputStream;
FileOutputStream fileOutputStream;
byte[] buffer = new byte[1024 * 8];
IvParameterSpec ivParameterSpec = new IvParameterSpec("1234567890123456".getBytes());
byte[] key = "only for testing".getBytes();
MessageDigest sha;
try {
sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
key = Arrays.copyOf(key, 16); // use only first 128 bit
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
try {
fileInputStream = new FileInputStream(file);
fileOutputStream = new FileOutputStream(Environment.getExternalStorageDirectory().getPath() + "/" + ENCRYPT_FILE);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
//CipherOutputStream cipherOutputStream = new CipherOutputStream(fileOutputStream, cipher);
int read;
while ((read = fileInputStream.read(buffer)) > 0) {
Log.i(TAG, "encrypt read= " + read);
byte[] encryptedData = cipher.doFinal(buffer);
if (encryptedData != null) {
Log.i(TAG, "encrypted size= " + encryptedData.length);
fileOutputStream.write(encryptedData, 0, read);
}
//cipherOutputStream.write(buffer, 0, buffer.length);
}
//cipherOutputStream.flush();
//cipherOutputStream.close();
fileInputStream.close();
fileOutputStream.close();
} catch (IOException | NoSuchAlgorithmException | NoSuchPaddingException
| IllegalBlockSizeException | BadPaddingException
| InvalidAlgorithmParameterException | InvalidKeyException e) {
e.printStackTrace();
}
}
Decrypt
public static void decryptFile() {
File file = new File(Environment.getExternalStorageDirectory().getPath() + "/" + ENCRYPT_FILE);
FileInputStream fileInputStream;
FileOutputStream fileOutputStream;
byte[] buffer = new byte[1024 * 8];
IvParameterSpec ivParameterSpec = new IvParameterSpec("1234567890123456".getBytes());
byte[] key = "only for testing".getBytes();
MessageDigest sha;
try {
sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
key = Arrays.copyOf(key, 16); // use only first 128 bit
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
try {
fileInputStream = new FileInputStream(file);
fileOutputStream = new FileOutputStream(Environment.getExternalStorageDirectory().getPath() + "/" + DECRYPT_FILE);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
//CipherInputStream cipherInputStream = new CipherInputStream(fileInputStream, cipher);
int read;
while ((read = fileInputStream.read(buffer)) > 0) {
Log.i(TAG, "decrypt read= " + read);
byte[] decryptedData = cipher.doFinal(buffer);
if (decryptedData != null) {
Log.i(TAG, "decrypted size= " + decryptedData.length);
fileOutputStream.write(decryptedData, 0, read);
}
//fileOutputStream.write(buffer, 0, buffer.length);
}
fileOutputStream.flush();
fileOutputStream.close();
//cipherInputStream.close();
fileInputStream.close();
} catch (IOException | NoSuchAlgorithmException | NoSuchPaddingException
| IllegalBlockSizeException | BadPaddingException
| InvalidAlgorithmParameterException | InvalidKeyException e) {
e.printStackTrace();
}
}
btw: It will work properly when I use CipherInputStream / CipherOutStream. I want to know if it is possible to use just FileInputStream / FileOutputStream only? thank you.
Edited:
Encrypt function will enlarge byte array about 16 bytes, I've tried increase the buffer size of decryption and still can't get it work.
byte[] buffer = new byte[1024 * 8 + 16];
Log:
I/#_: decrypt read= 8208
javax.crypto.BadPaddingException: EVP_CipherFinal_ex
at com.android.org.conscrypt.NativeCrypto.EVP_CipherFinal_ex(Native Method)
at com.android.org.conscrypt.OpenSSLCipher.doFinalInternal(OpenSSLCipher.java:430)
at com.android.org.conscrypt.OpenSSLCipher.engineDoFinal(OpenSSLCipher.java:466)
at javax.crypto.Cipher.doFinal(Cipher.java:1340)
at CryptoHelper.decryptFile(CryptoHelper.java:128)
Edited Update code here based on #Robert's answer for anyone who encountered the same problems like I did.
Encrypt:
int read;
while ((read = fileInputStream.read(buffer)) > 0) {
Log.i(TAG, "encrypt read= " + read);
byte[] encryptedData = cipher.update(buffer, 0, read);
//byte[] encryptedData = cipher.doFinal(buffer);
if (encryptedData != null) {
Log.i(TAG, "encrypted size= " + encryptedData.length);
fileOutputStream.write(encryptedData, 0, encryptedData.length);
}
//cipherOutputStream.write(buffer, 0, buffer.length);
}
byte[] finals = cipher.doFinal();
Log.i(TAG, "encrypted finals = " + finals.length);
fileOutputStream.write(finals, 0, finals.length);
Decrypt:
int read;
while ((read = fileInputStream.read(buffer)) > 0) {
Log.i(TAG, "decrypt read= " + read);
//byte[] decryptedData = cipher.doFinal(buffer);
byte[] decryptedData = cipher.update(buffer, 0, read);
if (decryptedData != null) {
Log.i(TAG, "decrypted size= " + decryptedData.length);
fileOutputStream.write(decryptedData, 0, decryptedData.length);
}
//fileOutputStream.write(buffer, 0, buffer.length);
}
byte[] finals = cipher.doFinal();
Log.i(TAG, "decrypted finals = " + finals.length);
fileOutputStream.write(finals, 0, finals.length);
Thanks again for Robert's help.
You problem is that you are always calling cipher.doFinal() for each block of data which is wrong as each block will be padded.
If you are en/decrypting data block-wise use cipher.update(...) and after the last block has been processed only call cipher.doFinal() once.
The easier way would be to use a CipherInputStream/CipherOutputStream - it does exactly what I have described for you (doFinal is called when you close the stream).
How to encrypt and decrypt a string in android using AES with Salt+password+iv . I want code equivalent to https://stackoverflow.com/a/27893935/5582162. This link has working code for node.js and iOS but I want same/equivalent code in android format.
Using the above link --
Eg:
RawData: pravin
password: sbifast12
salt: gettingsaltyfoo!
iv: QmBSbUZMUwld31DPrqyVSA==
Encrypted Key should be: AbvDUXUG9p1ysz1a1ZANSA==
Code that I tried:
try {
// byte[] salt = generateSalt();
byte[] salt = saltStr.getBytes();
// Log.i(TAG, "Salt: " + salt.length + " " + HexEncoder.toHex(salt));
PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt, PBE_ITERATION_COUNT, 128);
SecretKeyFactory factory = SecretKeyFactory.getInstance(PBE_ALGORITHM);
SecretKey tmp = factory.generateSecret(pbeKeySpec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
byte[] key = secret.getEncoded();
//Log.i(TAG, "Key: " + HexEncoder.toHex(key));
// PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, ITERATION_COUNT);
Cipher encryptionCipher = Cipher.getInstance(CIPHER_ALGORITHM);
// byte[] encryptionSalt = generateSalt();
// Log.i(TAG, "Encrypted Salt: " + encryptionSalt.length + " " + HexEncoder.toHex(encryptionSalt));
// PBEParameterSpec pbeParamSpec = new PBEParameterSpec(encryptionSalt, 1000);
// byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
Log.i(TAG, encryptionCipher.getParameters() + " ");
// byte[] iv = generateIv();
//byte[] iv ="QmBSbUZMUwld31DPrqyVSA==".getBytes();
IvParameterSpec ivspec = new IvParameterSpec(Arrays.copyOf(iv,16));
encryptionCipher.init(Cipher.ENCRYPT_MODE, secret, ivspec);
byte[] encryptedText = encryptionCipher.doFinal(plainText.getBytes());
// Log.i(TAG, "Encrypted: " + new String(encryptedText));
Log.i(TAG, "Encrypted: " + Base64.encodeToString(encryptedText, Base64.DEFAULT));
Cipher decryptionCipher = Cipher.getInstance(CIPHER_ALGORITHM);
decryptionCipher.init(Cipher.DECRYPT_MODE, secret, ivspec);
byte[] decryptedText = decryptionCipher.doFinal(encryptedText);
Log.i(TAG, "Decrypted....: " + new String(decryptedText));
} catch (Exception e) {
e.printStackTrace();
}
And also...
public class AesEncryption2 {
private static String password = "sbifast12";
/**
* vector array for encryption & decryption
*/
private static byte[] iv = "QmBSbUZMUwld31DPrqyVSA==".getBytes();
private static String IV = "QmBSbUZMUwld31DPrqyVSA==";
private static String salt = "gettingsaltyfoo!";
private static String CIPHERTEXT = "CIPHERTEXT!";
public static void main(String rawData) {
byte[] interop_iv = Base64.decode(IV, Base64.DEFAULT);
// byte[] iv = null;
byte[] ciphertext;
SecretKeySpec secret=null;
try {
secret = generateKey(password.toCharArray(), salt.getBytes());
} catch (Exception e) {
e.printStackTrace();
}
Map result = null;
try {
result = encrypt(rawData, iv, secret);
} catch (Exception e) {
e.printStackTrace();
}
ciphertext = (byte[]) result.get(CIPHERTEXT);
iv = (byte[]) result.get(IV);
System.out.println("Cipher text:" + Base64.encode(ciphertext, Base64.DEFAULT));
System.out.println("IV:" + Base64.encode(iv, Base64.DEFAULT) + " (" + iv.length + "bytes)");
System.out.println("Key:" + Base64.encode(secret.getEncoded(), Base64.DEFAULT));
try {
System.out.println("Deciphered: " + decrypt(ciphertext, iv, secret));
} catch (Exception e) {
e.printStackTrace();
}
// Interop demonstration. Using a fixed IV that is used in the C#
// example
try {
result = encrypt(rawData, interop_iv, secret);
} catch (Exception e) {
e.printStackTrace();
}
ciphertext = (byte[]) result.get(CIPHERTEXT);
iv = (byte[]) result.get(IV);
String text = Base64.encodeToString(ciphertext, Base64.DEFAULT);
System.out.println();
System.out.println("--------------------------------");
System.out.println("Interop test - using a static IV");
System.out.println("The data below should be used to retrieve the secret message by the receiver");
System.out.println("Cipher text: " + text);
System.out.println("IV: " + Base64.encodeToString(iv, Base64.DEFAULT));
try {
decrypt(Base64.decode(text, Base64.DEFAULT), iv, secret);
} catch (Exception e) {
e.printStackTrace();
}
}
public static SecretKeySpec generateKey(char[] password, byte[] salt) throws Exception {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password, salt, 1024, 128);
SecretKey tmp = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(tmp.getEncoded(), "AES");
return secret;
}
public static Map encrypt(String cleartext, byte[] iv, SecretKeySpec secret) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// If the IvParameterSpec argument is omitted (null), a new IV will be
// created
cipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(iv));
AlgorithmParameters params = cipher.getParameters();
byte[] usediv = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] ciphertext = cipher.doFinal(cleartext.getBytes("UTF-8"));
Map result = new HashMap();
result.put(IV, usediv);
result.put(CIPHERTEXT, ciphertext);
return result;
}
public static String decrypt(byte[] ciphertext, byte[] iv, SecretKeySpec secret) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
String plaintext = new String(cipher.doFinal(ciphertext), "UTF-8");
return plaintext;
}
Third method:
private static final String ALGO = "AES";
/**
* key value for encryption & decryption
*/
private static String password = "sbifast12";
private static byte[] iv = "QmBSbUZMUwld31DPrqyVSA==".getBytes();
private static String IV = "QmBSbUZMUwld31DPrqyVSA==";
private static String salt = "gettingsaltyfoo!";
/**
* constructor with two variable parameters
* #param password
* #param iv
*/
/* public AESEncryption(String password, String iv) {
if (password == null || iv == null)
throw new NullPointerException("Encryption values can not be null!");
this.password = password.getBytes();
this.iv = iv.getBytes();
}*/
/**
* encrypt given string data
*
* #param rawdata
* #return
* #throws Exception
*/
public static String encrypt(String rawdata) throws Exception {
if (rawdata == null)
throw new NullPointerException("Raw data can not be null!");
//SecretKeySpec sKey = (SecretKeySpec) generateKeyFromPassword(password, saltBytes);
//SecretKey key = new SecretKeySpec(Base64.decode(salt,Base64.DEFAULT), "AES");
// Key key = generateKey();
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
/* cipher.init(Cipher.ENCRYPT_MODE, generateKeyFromPassword("sbifast12", salt.getBytes()),
new IvParameterSpec(Arrays.copyOf(IV.getBytes("UTF-8"), 16)));
*/
cipher.init(Cipher.ENCRYPT_MODE, generateKeyFromPassword("sbifast12", salt.getBytes()),
new IvParameterSpec(Arrays.copyOf(IV.getBytes(),16)));
byte[] encVal = cipher.doFinal(rawdata.getBytes());
String encryptedValue = Base64.encodeToString(encVal, Base64.DEFAULT);
System.out.println("%%%%%%% Encrypted Text: " + encryptedValue);
return encryptedValue;
}
public static SecretKey generateKeyFromPassword(String password, byte[] saltBytes) throws GeneralSecurityException {
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), saltBytes, 1000, 128);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
// SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithSHA256AND256BITAES");
SecretKey secretKey = keyFactory.generateSecret(keySpec);
return new SecretKeySpec(secretKey.getEncoded(), "AES");
}
/**
* decrypt given string data
*
* #param encryptedData
* #return
* #throws Exception
*/
public String decrypt(String encryptedData) throws Exception {
if (encryptedData == null)
throw new NullPointerException("Encrypted data can not be null!");
Key key = generateKey();
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
byte[] decodedValue = Base64.decode(encryptedData, Base64.DEFAULT);
byte[] decValue = cipher.doFinal(decodedValue);
String decryptedValue = new String(decValue);
return decryptedValue;
}
/**
* key generator
*
* #return
* #throws Exception
*/
private Key generateKey() throws Exception {
Key key = new SecretKeySpec("sbifast12".getBytes(), ALGO);
return key;
}
public class Cryptography {
private static final String TAG = "Cryptography";
//secretKey64 is genereated from salt and password..!!! (taken directly from server side)
private static String secretKey64 = "*****1hrHpnONd8ZJ*****c756ikDmZU1v*****8Xjg=";
private static String secretKeyAlgorithm = "AES";
private static String cipherAlgorithm = "AES/CBC/PKCS7Padding";
private static String iv64 = "QmBSbU*****d31DPr*****==";
private static String charsetName = "UTF-8";
// private static int base64Mode = android.util.Base64.DEFAULT;
private static int base64Mode = android.util.Base64.NO_WRAP;
public static String encrypt(String plainText) throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, InvalidAlgorithmParameterException, UnsupportedEncodingException
, IllegalBlockSizeException, BadPaddingException {
String encrytedText = "";
if (plainText == null) {
return null;
} else {
SecretKey secretKey = new SecretKeySpec(android.util.Base64.decode(secretKey64, 0), secretKeyAlgorithm);
IvParameterSpec iv = new IvParameterSpec(android.util.Base64.decode(iv64, 0));
Cipher cipher = Cipher.getInstance(cipherAlgorithm);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
byte[] dataBytes = plainText.getBytes(charsetName);
encrytedText = Base64.encodeToString(cipher.doFinal(dataBytes), base64Mode);
}
// Log.d(TAG, "ENCRYPT >>>> : " + encrytedText);
return encrytedText;
}
public static String decrypt(String encryptedText) throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, InvalidAlgorithmParameterException, UnsupportedEncodingException
, IllegalBlockSizeException, BadPaddingException, NullPointerException {
String plainText = "";
SecretKey secretKey = new SecretKeySpec(android.util.Base64.decode(secretKey64, 0), secretKeyAlgorithm);
IvParameterSpec iv = new IvParameterSpec(android.util.Base64.decode(iv64, 0));
byte[] dataBytesD = Base64.decode(encryptedText, base64Mode);
Cipher cipherD = Cipher.getInstance(cipherAlgorithm);
cipherD.init(2, secretKey, iv);
//Log.d(TAG, "DECRYPT text:- >>>> : " + encryptedText);
byte[] dataBytesDecrypted = cipherD.doFinal(dataBytesD);
try {
plainText = new String(dataBytesDecrypted);
} catch (NullPointerException e) {
e.printStackTrace();
}
//Log.d(TAG, "DECRYPT >>>> : " + plainText);
return plainText;
}
}
I have found hundreds of examples of Android AES encryption and decryption but I am unable to make them work, even the simplest. The following code does some encryption and decryption but it spits out garbage after decrypting the encrypted text. Can you please take a look and tell me where I am wrong?
Thanks
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128, new SecureRandom());
SecretKey secretKey = keyGenerator.generateKey();
Cipher cipher = Cipher.getInstance("AES");
String plainText = "This is supposed to be encrypted";
String plainKey = Base64.encodeToString(secretKey.getEncoded(), Base64.DEFAULT);
//encrypt
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encryptedBytes = cipher.doFinal(plainText.getBytes());
String encryptedText = Base64.encodeToString(encryptedBytes, Base64.DEFAULT);
//decrypt
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[]decryptedBytes = cipher.doFinal(encryptedBytes);
String decryptedText = Base64.encodeToString(decryptedBytes, Base64.DEFAULT);
#Barend answer works:
Your code works fine. What #greenapps said. Instead of Base64.encodeToString(..), try printing the output of new String(decryptedBytes), you'll see that it's the value you were looking for.
SecretKeySpec sks = null;
try {
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed("Complex Key for encryption".getBytes());
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(128, sr);
sks = new SecretKeySpec((kg.generateKey()).getEncoded(), "AES");
} catch (Exception e) {
Log.e(TAG, "AES secret key spec error");
}
// Encode the original data with AES
byte[] encodedBytes = null;
try {
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.ENCRYPT_MODE, sks);
encodedBytes = c.doFinal(theTestText.getBytes());
} catch (Exception e) {
Log.e(TAG, "AES encryption error");
}
TextView tvencoded = (TextView)findViewById(R.id.textitem2);
tvencoded.setText("[ENCODED]:\n" +
Base64.encodeToString(encodedBytes, Base64.DEFAULT) + "\n");
// Decode the encoded data with AES
byte[] decodedBytes = null;
try {
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.DECRYPT_MODE, sks);
decodedBytes = c.doFinal(encodedBytes);
} catch (Exception e) {
Log.e(TAG, "AES decryption error");
}
TextView tvdecoded = (TextView)findViewById(R.id.textitem3);
tvdecoded.setText("[DECODED]:\n" + new String(decodedBytes) + "\n");
i try to cipher but my outputs after "cipher.init" won't output. Also no exceptions..and I don't know why...(new on android)
public void login() {
LoginData loginData = this.fragmentBox.getUpdatedLoginData();
String finishString = new String();
try {
Cipher cipher = Cipher.getInstance("AES");
byte[] output = null;
SecretKeySpec secretKey = new SecretKeySpec(
Globals.ENCRYPTPW.getBytes(), "AES");
System.out.println(secretKey.getEncoded().toString() + "----" + Globals.ENCRYPTPW);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
output = cipher.doFinal(loginData.getPassword().getBytes());
byte[] encryptedUsernameString = Base64.encode(output, 0);
finishString = new String(encryptedUsernameString, "UTF-8");
} catch (InvalidKeyException e) {
e.getStackTrace();
Log.v("Fehler im Code","");
} catch(Exception e){}
System.out.println("Code: " + finishString);
}
any idea?
When I run the following code in Java the output is a multiple of 128 bits (16 bytes).
The exact same code in Android is no longer a multiple of 16 bytes suggesting that the padding has failed.
Any ideas?
//Compress, Encrypt, Decrypt, Decompress variables
public final static String Secretkey = "###hidden######";
public final static String VectorInitializationKey = "###hidden###";
public final static String transformation = "AES/CFB8/PKCS5Padding";
public static byte[] encryptandCompress(byte[] input)
{
ByteArrayOutputStream ms = new ByteArrayOutputStream();
GZIPOutputStream gos = null; // Added
CipherOutputStream cos= null; // Added
SecretKeySpec skeySpec = new SecretKeySpec(Secretkey.getBytes(), "AES");
Cipher cipher;
try {
cipher = Cipher.getInstance(transformation);
IvParameterSpec iv = new IvParameterSpec(VectorInitializationKey.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
cos = new CipherOutputStream(ms, cipher );
gos= new GZIPOutputStream (cos);
gos.write(input);
gos.close(); // Must be called before we return bytes from ms
cos.close();
byte[] toReturn = ms.toByteArray();
Log.d("FileWriter", "Encrypted and compressed " + input.length + " bytes to " + toReturn.length + " bytes");
Log.d("FileWriter", "Encrypted and compressed " + toReturn.length/16.0 + " blocks written");
return toReturn;
} catch (Exception e) {
Log.e("FileWriter", "Compression failed: " + e.getMessage());
return null;
}
finally
{
try
{
if (gos != null)
{
gos.close();
}
if (cos != null)
{
cos.close();
}
if (ms != null)
{
ms.close();
}
}
catch (IOException e) {
e.printStackTrace();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}