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).
Related
I Hope that you can help me related my code.
Im trying to pass parameters IV to Encrypt/ Decryt
Encrypt Funcion:
I try to concatenate text; IV
encryptedText.setText(Base64.encodeToString(vals, Base64.DEFAULT) + ";" + (Base64.encodeToString(encryptionIv, Base64.DEFAULT)));
I think that is working fine , as the image represents.
public void encryptString(String alias) {
try {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
SecretKey secretKey = (SecretKey) keyStore.getKey(alias, null);
String initialText = startText.getText().toString();
if(initialText.isEmpty()) {
Toast.makeText(this, "Enter text in the 'Initial Text'", Toast.LENGTH_LONG).show();
return;
}
Cipher inCipher = Cipher.getInstance("AES/GCM/NoPadding");
inCipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] src= initialText.getBytes("UTF-8");
byte[] iv = inCipher.getIV();
assert iv.length == 12;
byte[] cipherText = inCipher.doFinal(src);
assert cipherText.length == src.length + 16;
byte[] message = new byte[12 + src.length + 16];
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
CipherOutputStream cipherOutputStream = new CipherOutputStream(
outputStream, inCipher);
cipherOutputStream.write(src);
cipherOutputStream.close();
byte [] vals = outputStream.toByteArray();
encryptedText.setText(Base64.encodeToString(vals, Base64.DEFAULT));
} catch (Exception e) {
Toast.makeText(this, "Exception " + e.getMessage() + " occurred", Toast.LENGTH_LONG).show();
Log.e(TAG, Log.getStackTraceString(e));
}
}
[![enter image description here][1]][1]
Could you helpme with an example to get decryption works fine?
Thanks for youl help
Updated: Hello , Im trying to pass the IV as the example:
My code here:
public void encryptString(String alias) {
try {
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);
RSAPublicKey publicKey = (RSAPublicKey) privateKeyEntry.getCertificate().getPublicKey();
String initialText = startText.getText().toString();
if(initialText.isEmpty()) {
Toast.makeText(this, "Enter text in the 'Initial Text' widget", Toast.LENGTH_LONG).show();
return;
}
Cipher inCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL");
inCipher.init(Cipher.ENCRYPT_MODE, publicKey);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
CipherOutputStream cipherOutputStream = new CipherOutputStream(
outputStream, inCipher);
cipherOutputStream.write(initialText.getBytes("UTF-8"));
cipherOutputStream.close();
byte [] vals = outputStream.toByteArray();
encryptedText.setText(Base64.encodeToString(vals, Base64.DEFAULT));
} catch (Exception e) {
Toast.makeText(this, "Exception " + e.getMessage() + " occurred", Toast.LENGTH_LONG).show();
Log.e(TAG, Log.getStackTraceString(e));
}
}
[![enter image description here][2]][2]
NEW Updated:
Encryption code
public void encryptString(String alias) {
try {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
SecretKey secretKey = (SecretKey) keyStore.getKey(alias, null);
String initialText = startText.getText().toString();
if(initialText.isEmpty()) {
Toast.makeText(this, "Enter text in the 'Initial Text'", Toast.LENGTH_LONG).show();
return;
}
Cipher inCipher = Cipher.getInstance("AES/GCM/NoPadding");
inCipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] src= initialText.getBytes("UTF-8");
byte[] iv = inCipher.getIV();
assert iv.length == 12;
byte[] cipherText = inCipher.doFinal(src);
assert cipherText.length == src.length + 16;
byte[] message = new byte[12 + src.length + 16];
System.arraycopy(iv, 0, message, 0, 12);
System.arraycopy(cipherText, 0, message, 12, cipherText.length);
//ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
//CipherOutputStream cipherOutputStream = new CipherOutputStream(
// outputStream, inCipher);
//cipherOutputStream.write(message);
//cipherOutputStream.close();
//byte [] vals = outputStream.toByteArray();
encryptedText.setText(Base64.encodeToString(message, Base64.DEFAULT));
} catch (Exception e) {
Toast.makeText(this, "Exception " + e.getMessage() + " occurred", Toast.LENGTH_LONG).show();
Log.e(TAG, Log.getStackTraceString(e));
}
}
Decryption code: ( Not working)
public void decryptString(String alias) {
try {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
SecretKey secretKey = (SecretKey) keyStore.getKey(alias, null);
String cipherText = encryptedText.getText().toString();
byte[] src = cipherText.getBytes();
byte[] message = new byte[12 + src.length + 16];
Cipher output = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec params = new GCMParameterSpec(128, message, 0, 12);
output.init(Cipher.DECRYPT_MODE, secretKey, params);
// output.doFinal(message, 12, message.length - 12);
CipherInputStream cipherInputStream = new CipherInputStream(
new ByteArrayInputStream(Base64.decode(cipherText, Base64.DEFAULT)), output);
ArrayList<Byte> values = new ArrayList<>();
int nextByte;
while ((nextByte = cipherInputStream.read()) != -1) {
values.add((byte)nextByte);
}
byte[] bytes = new byte[values.size()];
for(int i = 0; i < bytes.length; i++) {
bytes[i] = values.get(i).byteValue();
}
String finalText = new String(bytes, 0, bytes.length, "UTF-8");
decryptedText.setText(finalText);
} catch (Exception e) {
Toast.makeText(this, "Exception " + e.getMessage() + " occurred", Toast.LENGTH_LONG).show();
Log.e(TAG, Log.getStackTraceString(e));
}
}
Could you help me with the error shows in the image?
[![enter image description here][3]][3]
<<---UPDATED SOLUTION-->
Encript function:
public void encryptString(String alias) {
try {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
SecretKey secretKey = (SecretKey) keyStore.getKey(alias, null);
String initialText = startText.getText().toString();
if(initialText.isEmpty()) {
Toast.makeText(this, "Enter text in the 'Initial Text'", Toast.LENGTH_LONG).show();
return;
}
Cipher inCipher = Cipher.getInstance("AES/GCM/NoPadding");
inCipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] src= initialText.getBytes("UTF-8");
byte[] iv = inCipher.getIV();
assert iv.length == 12;
byte[] cipherText = inCipher.doFinal(src);
assert cipherText.length == src.length + 16;
byte[] message = new byte[12 + src.length + 16];
System.arraycopy(iv, 0, message, 0, 12);
System.arraycopy(cipherText, 0, message, 12, cipherText.length);
encryptedText.setText(Base64.encodeToString(message, Base64.DEFAULT));
} catch (Exception e) {
Toast.makeText(this, "Exception " + e.getMessage() + " occurred", Toast.LENGTH_LONG).show();
Log.e(TAG, Log.getStackTraceString(e));
}
}
Decrypt Function:
public void decryptString(String alias) {
try {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
SecretKey secretKey = (SecretKey) keyStore.getKey(alias, null);
String cipherText = encryptedText.getText().toString();
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] nonceCiphertextTag = Base64.decode(cipherText, Base64.DEFAULT);
GCMParameterSpec params = new GCMParameterSpec(128, nonceCiphertextTag, 0, 12);
cipher.init(Cipher.DECRYPT_MODE, secretKey, params);
byte[] decrypted = cipher.doFinal(nonceCiphertextTag, 12, nonceCiphertextTag.length - 12);
String finalText = new String(decrypted, 0, decrypted.length, "UTF-8");
decryptedText.setText(finalText);
} catch (Exception e) {
Toast.makeText(this, "Exception " + e.getMessage() + " occurred", Toast.LENGTH_LONG).show();
Log.e(TAG, Log.getStackTraceString(e));
}
}
Thanks a lot for the help #Topaco
In the latest posted encryption code, nonce, ciphertext and tag are concatenated and Base64 encoded, in the following called nonceCiphertextTagB64.
When decrypting
nonceCiphertextTagB64 must first be Base64 decoded to nonceCiphertextTag,
the nonce must be encapsulated in a GCMParameterSpec instance; the nonce are the first 12 bytes of nonceCiphertextTag,
a cipher instance for AES/GCM/NoPadding must be created, which is initialized with the key and the GCMParameterSpec instance in decryption mode,
the concatenation of ciphertext and tag is decrypted; the concatenation of ciphertext and tag is the data after the nonce.
In the following example implementation, with respect to a repro, the 32 bytes key 01234567890123456789012345678901 is used (instead of a key from the key store). The ciphertext was created with the posted code for encryption using the same key and the plaintext The quick brown fox jumps over the lazy dog:
...
String nonceCiphertextTagB64 =
"UpyxMnzPrw+zJGANL4wBbaC+UX5KnMKYwhR0u2iF/GDCcU38YKWlvJp1zJ+mrN0POP7lz9y+eweH\n" +
"vIxAH4R4U3At8T0fVe0=";
SecretKeySpec secretKey = new SecretKeySpec("01234567890123456789012345678901".getBytes(StandardCharsets.UTF_8),"AES");
byte[] nonceCiphertextTag = Base64.decode(nonceCiphertextTagB64, Base64.DEFAULT);
GCMParameterSpec params = new GCMParameterSpec(128, nonceCiphertextTag, 0, 12);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, params);
byte[] decrypted = cipher.doFinal(nonceCiphertextTag, 12, nonceCiphertextTag.length - 12);
System.out.println(new String(decrypted, StandardCharsets.UTF_8)); // The quick brown fox jumps over the lazy dog
...
The output is:
The quick brown fox jumps over the lazy dog
I am trying to get File Decryption working in Android. The file i have has been encrypted from python using Crypto.Cipher AES: full code:
import os, binascii, struct
from Crypto.Cipher import AES
def encrypt_file():
chunksize=64*1024
iv = "96889af65c391c69"
k1 = "cb3a44cf3cb120cc7b8b3ab777f2d912"
file = "tick.png"
out_filename = "entick.png"
dir = os.path.dirname(__file__)+"\\"
print(iv)
encryptor = AES.new(key, AES.MODE_CBC, iv)
in_filename = dir+file
filesize = os.path.getsize(in_filename)
with open(in_filename, 'rb') as infile:
with open(out_filename, 'wb') as outfile:
outfile.write(struct.pack('<Q', filesize))
outfile.write(iv)
while True:
chunk = infile.read(chunksize)
if len(chunk) == 0:
break
elif len(chunk) % 16 != 0:
chunk += ' ' * (16 - len(chunk) % 16)
outfile.write(encryptor.encrypt(chunk))
if __name__ == "__main__":
encrypt_file()
Android Decryption function (main):
private static File main(String fname, File enfile, String IV, String key) {
try {
byte[] bkey = key.getBytes("UTF-8");
byte[] bIV = IV.getBytes("UTF-8");
Log.d("ByteLen","bkey:"+Integer.toString(bkey.length));
Log.d("ByteLen","bIV:"+ Integer.toString(bIV.length));
File aesFile;
aesFile = enfile;
Log.d("AESFILELENGTH", "aes length: " + aesFile.length());
File aesFileBis = new File(String.valueOf(Environment.getExternalStorageDirectory().toPath()), "tick.png"); //to be replaced with fname
FileInputStream fis;
FileOutputStream fos;
CipherInputStream cis;
SecretKeySpec secretKey = new SecretKeySpec(bkey, "AES");
Cipher decrypt = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivSpec = new IvParameterSpec(bIV);
decrypt.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
fis = new FileInputStream(aesFile);
cis = new CipherInputStream(fis, decrypt);
fos = new FileOutputStream(aesFileBis);
try {
byte[] mByte = new byte[8];
int i = cis.read(mByte);
Log.i("MBYTE", "mbyte i: " + i);
while (i != -1) {
fos.write(mByte, 0, i);
i = cis.read(mByte);
}
} catch (IOException e) {
e.printStackTrace();
}
fos.flush();
fos.close();
cis.close();
fis.close();
return aesFileBis;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
The Crypto.Cipher module inserts the IV into the file as bytes 8-24 so i created this method to extract them:
private String IV(File enfile) throws UnsupportedEncodingException, FileNotFoundException {
int size = 24;
byte bytes[] = new byte[size];
byte tmpBuff[] = new byte[size];
if(enfile.canRead()){
//run decryption code
FileInputStream fis= new FileInputStream(enfile);
try {
int read = fis.read(bytes, 0, size);
if (read < size) {
int remain = size - read;
while (remain > 0) {
read = fis.read(tmpBuff, 0, remain);
System.arraycopy(tmpBuff, 0, bytes, size - remain, read);
remain -= read;
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
String IV = new String(bytes, "US-ASCII");
IV = IV.substring(8,24);
return IV;
}
From the Decrypt function i have checked and verified the key is 32 bytes long and the iv is 16 bytes long and both are the correct IV and Key. I know I am switching from a byte array to string and back again but that's just for testing.
I have looked at a few posts regarding this issue and so far have only found posts relating to the key being the wrong byte size or for decrpyting Strings and not files and therefor switching base64 encoding doesn't seem to apply. I think the issue is to do with the way Crypto.Cipher is padding the files as the first 8 byes look like junk (SO and NULL bytes) then there are 16 bytes of IV.
Thanks to the comment i added the Padding module from crypto: https://github.com/dlitz/pycrypto/blob/master/lib/Crypto/Util/Padding.py
im my python code i added:
from Crypto.Util.py3compat import * #solves bchr error
i also copied the pad() function from the Padding.py to the end of my code.
in the file writing function:
with open(in_filename, 'rb') as infile:
with open(out_filename, 'wb') as outfile:
outfile.write(iv) ##IV becomes the first 16 bytes, not using struct.pack() anymore
while True:
chunk = infile.read(chunksize)
if len(chunk) == 0:
break
elif len(chunk) % 16 != 0:
chunk += ' ' * (16 - len(chunk) % 16)
outfile.write(encryptor.encrypt(pad(chunk, 16))) ##added padding here
Finally in the Java code i removed the IV finder function and updated the main function:
private static File main(String fname, File enfile, String key) {
try {
FileInputStream fis;
File aesFile;
aesFile = enfile;
byte[] bkey = key.getBytes("UTF-8");
fis = new FileInputStream(aesFile);
byte[] IV = new byte[16];
for(Integer i =0; i < 16; i++){
IV[i] = (byte) fis.read();
}
Log.e("IV:",""+new String(IV, "US-ASCII"));
Log.d("ByteLen","bkey:"+Integer.toString(bkey.length));
Log.d("ByteLen","bIV:"+ Integer.toString(IV.length));
aesFile = enfile;
Log.d("AESFILELENGTH", "aes length: " + aesFile.length());
File aesFileBis = new File(String.valueOf(Environment.getExternalStorageDirectory().toPath()), "file.png"); //to be replaced with fname
FileOutputStream fos;
CipherInputStream cis;
SecretKeySpec secretKey = new SecretKeySpec(bkey, "AES");
Cipher decrypt = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivSpec = new IvParameterSpec(IV);
decrypt.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
cis = new CipherInputStream(fis, decrypt);
fos = new FileOutputStream(aesFileBis);
try {
byte[] mByte = new byte[8];
int i = cis.read(mByte);
Log.i("MBYTE", "mbyte i: " + i);
while (i != -1) {
fos.write(mByte, 0, i);
i = cis.read(mByte);
}
} catch (IOException e) { e.printStackTrace();}
fos.flush();
fos.close();
cis.close();
fis.close();
return aesFileBis;
}catch(Exception e) {e.printStackTrace(); }
return null;
}
The new parts of the code take the first 16 bytes from the FileInputStream and puts them into a byte array to be used as the IV, the rest are then decrypted using CBC/PKCS5Padding.
Hope this answer can be useful for anyone else.
Till kitkat, encryption/decryption is working good but in lollipop it decrypts only partial data.
I don't have problem with encryption because I encrypted a file with lollipop and decrypts it with kitkat it works fine but not vice versa.
Here is the code.
Encryption code
Encrypt(BufferedInputStream is, File destfile, String passcode) {
bis = is;
try {
fos = new FileOutputStream(destfile);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
dest = new BufferedOutputStream(fos, 1024);
this.passcode = passcode;
}
static void encrypt() throws IOException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException {
// Length is 16 byte
SecretKeySpec sks = new SecretKeySpec(passcode.getBytes(), "AES");
// Create cipher
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, sks);
// Wrap the output stream
CipherOutputStream cos = new CipherOutputStream(fos, cipher);
// Write bytes
int b;
byte[] d = new byte[1024];
while ((b = bis.read(d)) != -1) {
cos.write(d, 0, b);
}
// Flush and close streams.
cos.flush();
cos.close();
bis.close();
}
Decryption code
public Decrypt(String path, String pathcode) {
// TODO Auto-generated constructor stub
filepath = path;
try {
fis = new FileInputStream(new File(path));
this.passcode = pathcode;
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
static String decrypt() throws IOException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException {
SecretKeySpec sks = new SecretKeySpec(passcode.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, sks);
CipherInputStream cis = new CipherInputStream(fis, cipher);
int size = fis.available();
byte[] resdata = new byte[size];
cis.read(resdata, 0, size);
String newres = new String(resdata, "UTF-8").trim();
//write("decrypted_file.xhtml",newres);
if(fis!=null)
{
fis.close();
}
if(cis!=null)
cis.close();
return newres;
}
What's the problem in this code? Do I need to do anything more?
available() doesn't necessarily return the length of the entire stream, just the estimated number of bytes that can be read without blocking. So, use a ByteArrayOutputStream to store the bytes and then covert to a byte array:
CipherInputStream cis = new CipherInputStream(fis, cipher);
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int bytesRead;
byte[] data = new byte[1024];
while ((bytesRead = cis.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, bytesRead);
}
buffer.flush();
byte[] resdata = buffer.toByteArray();
String newres = new String(resdata, "UTF-8").trim();
I've never worked with encryption before. Actually I know nothing about encryption. I have a file encrypted with openssl tool using params:
openssl aes-256-cbc -nosalt -in fileIn -out fileOUT -p -k KEY
I need to decrypt it into memory but I don't know how. Can anyone provide me the code related to encryption?
Here's class I have written to decrypt a string encoded with params above (if I remmeber it correct):
public class CipherUtils {
public static byte[] getKey(String password, byte[] salt) {
try {
byte[] passwordSalt = EncodingUtils.getAsciiBytes(password);
passwordSalt = concatenateByteArrays(passwordSalt, salt);
byte[] hash1 = getHashForHash(null, passwordSalt);
byte[] hash2 = getHashForHash(hash1, passwordSalt);
byte[] key = concatenateByteArrays(hash1, hash2);
return key;
} catch (Exception e) {
return null;
}
}
public static byte[] getIV(String password, byte[] salt) {
try {
byte[] passwordSalt = EncodingUtils.getAsciiBytes(password);
passwordSalt = concatenateByteArrays(passwordSalt, salt);
byte[] hash1 = getHashForHash(null, passwordSalt);
byte[] hash2 = getHashForHash(hash1, passwordSalt);
byte[] hash3 = getHashForHash(hash2, passwordSalt);
return hash3;
} catch (Exception e) {
return null;
}
}
private static byte[] getHashForHash(byte[] hash, byte[] passwordSalt) {
try {
byte[] hashMaterial = concatenateByteArrays(hash, passwordSalt);
MessageDigest md = MessageDigest.getInstance("MD5");
return md.digest(hashMaterial);
} catch (Exception e) {
return null;
}
}
private static byte[] concatenateByteArrays(byte[] a, byte[] b) {
if (a == null)
return b;
if (b == null)
return a;
byte[] result = new byte[a.length + b.length];
System.arraycopy(a, 0, result, 0, a.length);
System.arraycopy(b, 0, result, a.length, b.length);
return result;
}
}
Salt is an empty bytearray in this case. It uses apache-commons-compress.jar.
Here's usage example:
byte[] key = CipherUtils.getKey(password, null);
byte[] IV = CipherUtils.getIV(password, null);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"),
new IvParameterSpec(IV));
cis = new CipherInputStream(is, cipher);
Where is is an InputStream of encrypted data.
this may helps you
public void encrypt() throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException {
// Here you read the cleartext.
FileInputStream fis = new FileInputStream("data/cleartext");
// This stream write the encrypted text. This stream will be wrapped by
// another stream.
FileOutputStream fos = new FileOutputStream("data/encrypted");
// Length is 16 byte
SecretKeySpec sks = new SecretKeySpec("yourkey".getBytes(), "AES");
// Create cipher
Cipher cipher = Cipher.getInstance("AES/CBC");
cipher.init(Cipher.ENCRYPT_MODE, sks);
// Wrap the output stream
CipherOutputStream cos = new CipherOutputStream(fos, cipher);
// Write bytes
int b;
byte[] d = new byte[8];
while ((b = fis.read(d)) != -1) {
cos.write(d, 0, b);
}
// Flush and close streams.
cos.flush();
cos.close();
fis.close();
}
Decrypt
public void decrypt() throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException {
FileInputStream fis = new FileInputStream("data/encrypted");
FileOutputStream fos = new FileOutputStream("data/decrypted");
SecretKeySpec sks = new SecretKeySpec("yourkey".getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC");
cipher.init(Cipher.DECRYPT_MODE, sks);
CipherInputStream cis = new CipherInputStream(fis, cipher);
int b;
byte[] d = new byte[8];
while((b = cis.read(d)) != -1) {
fos.write(d, 0, b);
}
fos.flush();
fos.close();
cis.close();
}
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();
}
}
}