How to encrypt and decrypt file for Android Kitkat and higher? - android

I am googling and testing solution for a while and so far no success. There is always some problem with it. Following code is "working" (meaning do not show any error while it is running) on Android Kitkat and higher, but decrypted files are not readable. Why?
final static byte[] iv = new byte[16];//ADDED
final static int buffer = 102400;
final static String encryptionType = "AES/CFB8/NoPadding";//CHANGED TO DIFFERENT TYPE
static void encrypt(String password, File fileInput, File fileOutput) throws Exception {
IvParameterSpec ivParams = new IvParameterSpec(iv);//ADDED
FileInputStream fis = new FileInputStream(fileInput);
FileOutputStream fos = new FileOutputStream(fileOutput);
SecretKeySpec sks = new SecretKeySpec(password.getBytes("UTF-8"), encryptionType);
Cipher cipher = Cipher.getInstance(encryptionType);
//cipher.init(Cipher.ENCRYPT_MODE, sks);REPLACED
cipher.init(Cipher.ENCRYPT_MODE, sks, ivParams);
CipherOutputStream cos = new CipherOutputStream(fos, cipher);
int b;
byte[] d = new byte[buffer];
while ((b = fis.read(d)) != -1) {
cos.write(d, 0, b);
}
cos.flush();
cos.close();
fis.close();
}
static void decrypt(String password, File fileInput, File fileOutput) throws Exception {
IvParameterSpec ivParams = new IvParameterSpec(iv);//ADDED
FileInputStream fis = new FileInputStream(fileInput);
FileOutputStream fos = new FileOutputStream(fileOutput);
SecretKeySpec sks = new SecretKeySpec(password.getBytes("UTF-8"), encryptionType);
Cipher cipher = Cipher.getInstance(encryptionType);
//cipher.init(Cipher.ENCRYPT_MODE, sks);REPLACED
cipher.init(Cipher.DECRYPT_MODE, sks, ivParams);
CipherInputStream cis = new CipherInputStream(fis, cipher);
int b;
byte[] d = new byte[buffer];
while ((b = cis.read(d)) != -1) {
fos.write(d, 0, b);
}
fos.flush();
fos.close();
cis.close();
}
EDIT: After I changed type to "AES/CFB8/NoPadding", it seems to be ok, there is no error in process, but decrypted file is not readable.

The problem in the decrypt method is caused by this line:
cipher.init(Cipher.ENCRYPT_MODE, sks);
the mode needs to Cipher.DECRYPT_MODE, so the line should be
cipher.init(Cipher.DECRYPT_MODE, sks);
Other issues are the use of the long obsolete DESede algorithm, the lack of any IV generation and handling, the absence of a good password-based key derivation algorithm, and the lack of any MAC on the ciphertext. Correctly using AES GCM mode with proper nonce generation and handling, and use of PBKDF2 (which is available on Android and Oracle Java) would represent significant improvements.
You don't supply an IV, so one is generated for you automatically. You must find a way to transmit this IV to the recipient. Typically the IV/Nonce is prepending to the ciphertext and stripped off by the recipient in order to decrypt the data. CipherInputStream/CipherOutputStream does not do this for you, so you must do it on your own.

I finally solve this problem by using shorter password. I am not sure why, but on Android 7 and 8, there is no problem with long password, but same password on Android 4.4 leads to crazy errors and brake encryption.

Related

Android Cipher encrypt in Android 8.1

I have this code to encrypt a string input. If i encrypt with same input on other devices os lower 8.1 then i get same value. However, when I try on the 8.1 devices, I get a completely different string.
No exeption thrown. I found the Android 8.1 Cryptography updates with KeyGenerator:AES. How can i fix to generate a same value with other devices Os lower 8.1 ?
public static String cryptAESGungHo(String input)
{
byte[] gh_key = getKeyGungho("gh_key").getBytes();
byte[] gh_iv = getKeyGungho("gh_iv").getBytes();
IvParameterSpec ivSpecs = new IvParameterSpec(gh_iv);
byte[] crypted = null;
try{
SecretKeySpec skey = new SecretKeySpec(gh_key, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skey, ivSpecs);
try
{
crypted = cipher.doFinal(input.getBytes("UTF-16"));
}
catch(Exception e)
{}
}catch(Exception e){
}
String cr= new String(crypted);
return cr;
}
try changing UTF-16 to UTF-16LE or UTF-16BE
crypted = cipher.doFinal(input.getBytes("UTF-16LE"));
You can use
byte[] data = str.getBytes("UTF-16LE");
Log.i("api", Arrays.toString(data));
To Check the cipher input on Android 8.1.0 whether is the same as on other Android OS.
please change this line and check
From
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
To
Cipher cipher = Cipher.getInstance("AES");

How to use 256 bit aes encryption in android

I am using the following code to encrypt a file in sd card.
void encrypt() throws IOException, NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException {
String myString = getOutputFile();
File myFile = new File(myString);
FileInputStream inputStream = new FileInputStream(myFile);
File encodedfile = new File(path,"filename" + ".mp4");
FileOutputStream outputStream = new FileOutputStream(encodedfile);
SecretKeySpec sks = new SecretKeySpec("MyDifficultPassw".getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, sks);
CipherOutputStream cos = new CipherOutputStream(outputStream, cipher);
int b;
byte[] d = new byte[8];
while((b = inputStream.read(d)) != -1){
cos.write(d, 0, b);
}
cos.flush();
cos.close();
inputStream.close();
As i am new to cryptography, i don't know whether am using 256 bit encryption. Am i using a 256 bit encryption. If not what code should i add to make it a 256 bit encryption
No, you are using 128 bit encryption, as your password is 16 ASCII characters. Combine that with the default character set of Android (UTF-8) and the result of getBytes() will be key data of 16 bytes, or 128 bits.
Not that it matters if it is 128 bit or 256 bit. If you directly store the password or key in your code, use a password as key or if you rely on the default ECB mode of encryption, then your code is not secure.
Learn about key management, use at least CBC mode encryption and create a fully random AES key of 256 bits (32 bytes). The AES keysize (as used within Cipher) fully depends on the key in Java / Android.

Decryption Output has strange symbols

I am working on encrypting (and later decrypting) strings in .Net and Java (on Android), using AES encryption , in the .Net side every thing is OK, in the Android(Java) side the output string of the posted code has unknown symbols.
String stdiv = "1234567890123456";
String txtinput = txtview1.getText().toString();
String mainkey = "0000999988887777";
byte[] key;
key = mainkey.getBytes("UTF8");
byte[] iv = stdiv.getBytes("UTF8");
byte[] input = txtinput.getBytes("UTF8");
Cipher cipher;
cipher = Cipher.getInstance("AES/CBC/PKCS7PADDING");
SecretKeySpec keyspec = new SecretKeySpec(key, "AES" );
IvParameterSpec paramspec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, keyspec, paramspec);
byte[] result = cipher.doFinal(input);
String str=new String(result,"UTF8");
txtview2.setText(str);

BadPaddingException in Android while decryption of images encrypted with stand alone java program

I am encrypting images in assets folder with following code and trying to decrypt in apk. (I am doing this just to avoid easy copying of images with just unzip of apk file). I know i will have the key as part of apk.
I used and tested below code for encrypting the images with stand alone java program. (I tested them by decrpyting and it is working fine in stand alone java program.
Encryption
byte[] incrept = simpleCrypto.encrypt(KEY, simpleCrypto.getImageFile("E:/aeroplane.png"));
//Store encrypted file in SD card of your mobile with name vincent.mp3.
FileOutputStream fos = new FileOutputStream(new File("E:/out-aeroplane.png"));
fos.write(incrept);
fos.close();
Decryption
byte[] decrpt = simpleCrypto.decrypt(KEY, simpleCrypto.getImageFile("E:/out-aeroplane.png"));
//Store encrypted file in SD card of your mobile with name vincent.mp3.
FileOutputStream fosdecrypt = new FileOutputStream(new File("E:/outdecrypt-aeroplane.png"));
fosdecrypt.write(decrpt);
fosdecrypt.close();
Encrypt Decrypt logic
public byte[] getImageFile(String fileName) throws FileNotFoundException
{
byte[] Image_data = null;
byte[] inarry = null;
try {
File file = new File(fileName);
#SuppressWarnings("resource")
FileInputStream is = new FileInputStream (file); // use recorded file instead of getting file from assets folder.
int length = is.available();
Image_data = new byte[length];
int bytesRead;
ByteArrayOutputStream output = new ByteArrayOutputStream();
while ((bytesRead = is.read(Image_data)) != -1)
{
output.write(Image_data, 0, bytesRead);
}
inarry = output.toByteArray();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return inarry;
}
public byte[] encrypt(String seed, byte[] cleartext) throws Exception {
byte[] rawKey = getRawKey(seed.getBytes());
byte[] result = encrypt(rawKey, cleartext);
// return toHex(result);
return result;
}
public byte[] decrypt(String seed, byte[] encrypted) throws Exception {
byte[] rawKey = getRawKey(seed.getBytes());
byte[] enc = encrypted;
byte[] result = decrypt(rawKey, enc);
return result;
}
//done
private byte[] getRawKey(byte[] seed) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(seed);
kgen.init(128, sr);
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
return raw;
}
private byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(clear);
return encrypted;
}
private byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encrypted);
return decrypted;
}
In my apk file after getting the image as InputStream, I am decrypting them after converting them as byte array. Again I am converting the decrypted byte array to input stream for BitmapFactory.decode. I tried both decodeByteArray and decodeStream. Both not working.
Image is encrypted with stand alone java program and it is decrypted in apk. (If i decrypt in stand alone java program, it is working fine.)
I am getting error saying Failed to decode Stream javax.crypto.BadPaddingException: pad block corrupted
public static Bitmap readBitmap(InputStream input) {
if (input == null)
return null;
try {
String KEY = "kumar";
byte[] inarry =IOUtils.toByteArray(input);
byte[] decrpt = SquarksCryptUtil.decrypt(KEY, inarry);
InputStream cleanStream = null;
cleanStream = new ByteArrayInputStream(decrpt);
// return BitmapFactory.decodeStream(cleanStream);
return BitmapFactory.decodeByteArray(decrpt, 0, decrpt.length);
// return BitmapFactory.decodeStream(input);
} catch (Exception e) {
Log.e(FILE_NAME, "Failed to decode Stream " + e);
return null;
} finally {
close(input);
}
}
The problem that you are facing is because you are assuming that the random number generates the same key on each platform. This issue is due to the fact that there is a very bad example on the internet that uses SecureRandom as a key derivation function, which it is not. SecureRandom is not even well defined if the seed is set directly. You can use PBKDF2 instead of the incorrect key derivation function on both sides, there should be enough pointers on stackoverflow on how to perform PBKDF2 key derivation using Java.
The best thing to do is to decrypt the images on the SE platform and then re-encrypt them correctly using PBKDF2. Currently, the way the key is derived is only specified in the source code of the Sun implementation of "SHA1PRNG". That's not a good foundation at all.
Furthermore you need to assure that there are no platform dependencies during encryption/decryption, as others already have pointed out.

Setting application config *not* user config

I have developed a main app, now I need to develop a second app which is identical to the first but only perform 70% of the functionality. I have modularized the main app's functionality but now I want to be able to turn them on/off base on a configuration file. The values in this configuration file need to be accessible within the context of Activities and Services. A lot of people suggested using SharedPrefences, but I don't need end user to modify this. It's only meant for developer to configure these settings. What's the best approach for this?
Now in android data can be stored in these ways
Now you don’t want to store configuration in shared preference, So i would suggest you to go in 2 ways
Create a SQLite database that is accessible by both apps OR
Create a file that stores the configuration data and is accessible by both apps
Whatever the method that you choose to store the data encrypt it using some encryption algorithm like this
private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(clear);
return encrypted;
}
private static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encrypted);
return decrypted;
}
And invoke them like this:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.PNG, 100, baos); // bm is the bitmap object
byte[] b = baos.toByteArray();
byte[] keyStart = "this is a key".getBytes();
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(keyStart);
kgen.init(128, sr); // 192 and 256 bits may not be available
SecretKey skey = kgen.generateKey();
byte[] key = skey.getEncoded();
// encrypt
byte[] encryptedData = encrypt(key,b);
// decrypt
byte[] decryptedData = decrypt(key,encryptedData);
Now you can use the key to access these values and user would not be able to understand you configuration information
or you can use a web service to save data onto the server and your both apps can use that web service to get the configuration, It wont work offline though :(

Categories

Resources