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.
Related
I encrypted multiple files in one file in order.
Now I want to decrypt only one of those files. for this purpose I did this:
I got all bytes of encrypted files as array Like this: (first index: byte offset of every file in the encrypted file, second index: length of file bytes in the encrypted file)
int[] file1 = new int[] {0 ,109624}
int[] file2 = new int[] {109624,40480}
int[] file3 = new int[] {150104,153640}
int[] file4 = new int[] {303744,24320}
Now for getting specific file from the encrypted file and decrypt it I should to read the encrypted file. First of all, skip the bytes of previous files.
for skipping them I use this method (for example I want to encrypt file2):
int fromByte = file2[0];
int fileLength = file2[1];
FileInputStream fis = new FileInputStream(sourceFile);
CipherInputStream cis = new CipherInputStream(fis,cipher);
long skipBytes = cis.skip(fromByte);
Then it reads byte to byte of source file in "while" loop and when length of read file bytes equals to fileLength, it breaks loop.
I brought all the codes at the end here.
When I decrypt first file (from byte:0 and file length:109624 and it dosn't need to "skip bytes"), It works well.
But when I decrypt other files (for example for encrypt file2 it needs to skipt 109624 byte from source file), It doesn't work well and created file is not decrypted.
Why it dosn't work for other files?
Here's my codes:
public void decryptFile(File sourceFile, File targetFile, int fromByte, int lengthToRead, SecretKeySpec keySpec) {
try {
FileInputStream fis = new FileInputStream(sourceFile);
FileOutputStream fos = new FileOutputStream(targetFile);
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE,keySpec);
CipherInputStream cis = new CipherInputStream(fis,cipher);
int readPart,currentLength = 0, bufferSize = 8;
long skipBytes;
byte[] bytes = new byte[bufferSize];
skipBytes = cis.skip(fromByte);
while ((readPart = cis.read(bytes)) != -1) {
fos.write(bytes,0,readPart);
currentLength += bufferSize;
if (currentLength >= lengthToRead) {
break;
}
}
cis.close();
fos.flush();
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
}
Here's My codes for encrypt multiple files in on file:
public void encryptFile(File[] sourceFiles, File targetFile, SecretKeySpec keySpec) {
try {
FileOutputStream fos = new FileOutputStream(targetFile);
FileInputStream fis;
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE,keySpec);
CipherOutputStream cos = new CipherOutputStream(fos,cipher);
int readPart,bufferSize = 8;
byte[] bytes = new byte[bufferSize];
for (File sourceFile : sourceFiles) {
fis = new FileInputStream(sourceFile);
while ((readPart = fis.read(bytes)) != -1) {
cos.write(bytes, 0, readPart);
}
fis.close();
}
cos.flush();
cos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
}
I realized the problem, After many reviews.
There are two problem in this codes:
problem 1: in skip method. we should check the skipBytes and if it is not equal to fromByte, it must repeat. for this purpose add these codes:
while (skipBytes<fromByte) {
fromByte -= skipBytes;
skipBytes = cis.skip(fromByte);
}
problem 2: in encrypt function. if the file length is not multiple of 8, encryption method add some bytes until its length is multiple of 8. for fix this problem we should to add those bytes to the file ourselves before encrypt it. for this purpose add these codes before encrypt every file:
int remaining = (int) (sourceFile.length() % 8);
if (remaining>0) {
BufferedWriter bw = new BufferedWriter(new FileWriter(sourceFile,true));
while (remaining<8) {
bw.write(" ");
remaining++;
}
bw.close();
}
This codes modifies source file. If you want to don't modifying source file, instead of above codes, use this codes after ending encryption while loop:
int remainder = (int)(sourceFile.length() % 8);
if (remainder>0) {
StringBuilder sb = new StringBuilder("");
while (remainder<8) {
sb.append(" ");
remainder++;
}
byte[] additionBytes = sb.toString().getBytes(Charset.forName("UTF-8"));
cos.write(additionBytes, 0, additionBytes.length);
}
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).
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();
Hi I am working on AES encryption and decryption in my project.
I have a .mp4 file which is encrypted using "AES/CBC/pkcs5padding".
I have the key and iv values which is used to encrypt the first 256 bytes of the audio file.
I need to decrypt the file's first 256 bytes using the same algorithm , key and iv values.
I have followed some links (link1,link2).
I got a audio player demo and tried to implement my part(AES encryption and decryption) as demo.
Below I explained the code what I have done.
This method reads the data from encrypted file which is under res/raw folder.
private void readFile() throws IOException, InvalidKeyException,
NoSuchAlgorithmException, NoSuchPaddingException,
InvalidAlgorithmParameterException, IllegalBlockSizeException,
BadPaddingException {
Context context = getApplicationContext();
/*
* InputStream is = getResources().openRawResource(
* getResources().getIdentifier("raw/encrypted", "raw",
* getPackageName())); String text = "";
*
* int size = is.available(); byte[] buffer = new byte[size];
* is.read(buffer);
*/
InputStream inStream = context.getResources().openRawResource(
R.raw.encrypted);
// get string from file
byte[] music = new byte[256];
for (int i = 0; i <= inStream.available(); i = i + 255) {
music = convertStreamToByteArray(inStream, i);
byte[] bytesToWrite = new byte[256];
bytesToWrite = music;
if (i == 0) {
bytesToWrite = AES256Cipher.decrypt(iv.getBytes("UTF-8"),
key.getBytes("UTF-8"), music);
// writeFirstSetOfBytes("decrypted.mp4");
}
writeFirstSetOfBytes(bytesToWrite);
}
}
the decrypt method got from the link1. Here I passed the key and iv values mentioned above.
public static byte[] decrypt(byte[] ivBytes, byte[] keyBytes,
byte[] textBytes) throws java.io.UnsupportedEncodingException,
NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, InvalidAlgorithmParameterException,
IllegalBlockSizeException, BadPaddingException {
AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivBytes);
// SecretKeySpec newKey = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE,
new SecretKeySpec(keyBytes, "AES"), ivSpec);
return cipher.doFinal(textBytes);
}
This method used to get the byte array from the input stream.
public static byte[] convertStreamToByteArray(InputStream is, int size)
throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buff = new byte[256];
int i = Integer.MAX_VALUE;
while ((i = is.read(buff, size, buff.length)) > 0) {
baos.write(buff, 0, i);
}
return baos.toByteArray(); // be sure to close InputStream in calling
// function
}
This method writes the received byte array into a destination file(decrypted.mp4)
private void writeFirstSetOfBytes(byte[] byteToWrite) {
File file = new File(
Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
"/decrypted.mp4");
FileOutputStream stream = null;
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// writing data into file
try {
stream = new FileOutputStream(file);
stream.write(byteToWrite);
stream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
I am getting the following error.
javax.crypto.IllegalBlockSizeException: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length
How can I achieve my requirement ?
How do I decrypt the first 256 bytes of data only?
Is there any code behaves wrongly ?
Is there any third party library avilable ?
If need more clarification, kindly let me know.
According to Wikipedia, PKCS#5 is a special variant of PKCS#7 that is defined for exactly 64-bit blocks (while PKCS#7 works for any block size), but AES uses 128-bit blocks only (or larger, but this was not included in the standard), thus the mismatch. Try another padding scheme.
I want to encrypt / decrypt files ( of reasonable size ). I have my code working corrects using AES/CBC/PKCS5Padding . The problem is that it takes really long time to encrypt big files. SO now I am planning to use openSSL.
Is there a link that explains how to use openssl from a java app? How can I integrate it to my java app?
Thanks a lot for any links / points in this regard.
Thanks for your help and time
My code using BC:
public class BouncyCastleProvider_AES_CBC {
public Cipher encryptcipher, decryptCipher;
String TAG = "DataEncryptDecrypt";
private static final String RANDOM_ALGORITHM = "SHA1PRNG";
// The default block size
public static int blockSize = 16;
// Buffer used to transport the bytes from one stream to another
byte[] buf = new byte[blockSize]; //input buffer
byte[] obuf = new byte[512]; //output buffer
// The key
byte[] key = null;
// The initialization vector needed by the CBC mode
byte[] IV = null;
public BouncyCastleProvider_AES_CBC(String passwd){
//for a 192 key you must install the unrestricted policy files
// from the JCE/JDK downloads page
key =passwd.getBytes();
key = "SECRETSECRET_1SE".getBytes();
Log.i( "SECRETSECRET_1SECRET_2", "length"+ key.length);
//default IV value initialized with 0
IV = new byte[blockSize];
InitCiphers();
}
public BouncyCastleProvider_AES_CBC(String pass, byte[] iv){
//get the key and the IV
IV = new byte[blockSize];
System.arraycopy(iv, 0 , IV, 0, iv.length);
}
public BouncyCastleProvider_AES_CBC(byte[] pass, byte[]iv){
//get the key and the IV
key = new byte[pass.length];
System.arraycopy(pass, 0 , key, 0, pass.length);
IV = new byte[blockSize];
System.arraycopy(iv, 0 , IV, 0, iv.length);
}
public void InitCiphers()
{
try {
//1. create the cipher using Bouncy Castle Provider
encryptcipher =
Cipher.getInstance("AES/CBC/PKCS5Padding", "BC");
//2. create the key
SecretKey keyValue = new SecretKeySpec(key,"AES");
//3. create the IV
AlgorithmParameterSpec IVspec = new IvParameterSpec(IV);
//4. init the cipher
encryptcipher.init(Cipher.ENCRYPT_MODE, keyValue, IVspec);
encryptcipher.getOutputSize(100);
//1 create the cipher
decryptCipher =
Cipher.getInstance("AES/CBC/PKCS5Padding", "BC");
//2. the key is already created
//3. the IV is already created
//4. init the cipher
decryptCipher.init(Cipher.DECRYPT_MODE, keyValue, IVspec);
}
catch(Exception ex) {
ex.printStackTrace();
}
}
public String encryptData(String inputFileName) {
String outFilename = null;
File inputFile = new File(inputFileName);
try {
// step 3 - not needed, as we have all the blocks on hand
// step 4 - call doFinal()
outFilename = ".".concat(CommonUtils.getHash(inputFile.getName()));
InputStream fis;
OutputStream fos;
fis = new BufferedInputStream(new FileInputStream(inputFileName));
fos = new BufferedOutputStream(new FileOutputStream(
inputFile.getParent() + "/" + outFilename));
Log.i(TAG, "Output path:" + inputFile.getParent() + "/" + outFilename);
int bufferLength = (inputFile.length()>10000000?10000000:1000);
byte[] buffer = new byte[bufferLength];
int noBytes = 0;
byte[] cipherBlock = new byte[encryptcipher
.getOutputSize(buffer.length)];
int cipherBytes;
while ((noBytes = fis.read(buffer)) != -1) {
cipherBytes = encryptcipher.update(buffer, 0, noBytes,
cipherBlock);
fos.write(cipherBlock, 0, cipherBytes);
}
// always call doFinal
cipherBytes = encryptcipher.doFinal(cipherBlock, 0);
fos.write(cipherBlock, 0, cipherBytes);
// close the files
fos.close();
fis.close();
Log.i("encrpty", "done");
inputFile.delete();
}
catch (Exception ex) {
ex.printStackTrace();
}
return inputFile.getParent() + "/" + outFilename;
}
public void decryptData(String inputFileName, String outputFileName) {
InputStream fis;
OutputStream fos;
try {
fis = new BufferedInputStream(new FileInputStream(
inputFileName));
fos = new BufferedOutputStream(new FileOutputStream(
outputFileName));
byte[] buffer = new byte[blockSize*100];
int noBytes = 0;
byte[] cipherBlock = new byte[decryptCipher
.getOutputSize(buffer.length)];
int cipherBytes;
while ((noBytes = fis.read(buffer)) != -1) {
cipherBytes = decryptCipher.update(buffer, 0, noBytes,
cipherBlock);
fos.write(cipherBlock, 0, cipherBytes);
}
// allways call doFinal
cipherBytes = decryptCipher.doFinal(cipherBlock, 0);
fos.write(cipherBlock, 0, cipherBytes);
// close the files
fos.close();
fis.close();
new File(inputFileName).delete();
Log.i("decrypt", "done");
} catch (Exception ex) {
ex.printStackTrace();
}
}
public byte[] generateSalt() {
byte[] salt = new byte[16];
try {
SecureRandom random = SecureRandom.getInstance(RANDOM_ALGORITHM);
random.nextBytes(salt);
}
catch(Exception ex) {
ex.printStackTrace();
}
return salt;
}
}
The Guardian Project has build files for Android. Once you build it, you need write a simple JNI wrapper that does the encryption/decryption using OpenSSL APIs (EVP, etc), then call this from your app. You need to include he openssl and your JNI wrapper in the app and load them on startup using System.loadLibrary().
https://github.com/guardianproject/openssl-android
Additionally:
don't derive a password from a string directly, use a proper derivation algorithm.
don't use a fixed IV, especially all zeros