"Unsafe Cipher Mode" in Google Play Console - android

I’m using Cipher.getInstance(“AES”) for password encryption and decryption, and it’s working well as expected. But when uploading the APK to the Play Store, I get an “Unsafe Cipher Mode” error. To resolve this, I have made the Google-recommended changes below.
Encryption:
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec keySpec = null;
keySpec = new SecretKeySpec(Arrays.copyOf("unique id".getBytes("UTF-8"), 16), "AES");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] encryptedBytes = cipher.doFinal(stringToEncrypt.getBytes());
byte[] encodedBase64Value = Base64.encode(encryptedBytes, Base64.DEFAULT);
return new String(encodedBase64Value, "UTF-8");
Decryption:
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec keySpec = null;
keySpec = new SecretKeySpec(Arrays.copyOf("unique id".getBytes("UTF-8"), 16), "AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec);
byte[] decodedBase64Value = Base64.decode(stringToDecrypt.getBytes("UTF-8"), Base64.DEFAULT);
byte[] decryptedBytes = cipher.doFinal(decodedBase64Value);
return new String(decryptedBytes, "UTF-8");
I have tried the below solutions.
Sol1:
As suggested by the Google Help Center article(https://support.google.com/faqs/answer/10046138) for this error, I have changed the configuration to,
Cipher.getInstance("AES/GCM/NoPadding"); for both encryption and decryption.
Sol2&3:
unsafe cryptographic encryption patterns , How to solve it?
"Your app contains unsafe cryptographic encryption patterns" - How I can get rid of this warning?
After all of these above mentioned solutions, I ended up with the following exception at the time of decryption.
“java.security.InvalidAlgorithmParameterException: IV must be specified in GCM mode”
Could anyone help me with what exactly I'm missing?
or
Any suggestions on how I can get rid of this Google security warning?

Related

Acceptable way to store encrypted password

I am thinking about a way I can "try to protect" my application data.
Initially I was doing the following:
I have an encryption key that I use to encrypt and decrypt the data received, as well as what will be stored, I used firebase for the backend.
Basically something like this:
private String encriptar(String datos, String password) throws Exception {
SecretKeySpec secretKey = generateKey(password);
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] datosEncriptadosBytes = cipher.doFinal(datos.getBytes());
String datosEncriptadosString = Base64.encodeToString(datosEncriptadosBytes, Base64.DEFAULT);
return datosEncriptadosString;
}
private String desencriptar(String datos, String password) throws Exception {
SecretKeySpec secretKey = generateKey(password);
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] datosDescoficados = Base64.decode(datos, Base64.DEFAULT);
byte[] datosDesencriptadosByte = cipher.doFinal(datosDescoficados);
String datosDesencriptadosString = new String(datosDesencriptadosByte);
return datosDesencriptadosString;
}
private SecretKeySpec generateKey(String password) throws Exception {
MessageDigest sha = MessageDigest.getInstance("SHA-256");
byte[] key = password.getBytes("UTF-8");
key = sha.digest(key);
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
return secretKey;
}
Well, you can see that in all three methods the password value is required for all methods, encryption, decryption and to generate the key.
I had planned to save this password as "Remote confing" in firebase and get it when I use it, but there are many calls to the server. The other option is to store it locally, but if I do it this way, the password would be plain text in a sharedpreferences, for example.
I am somewhat confused with how to approach this.
I am a rookie on the subject (as you may have seen) and would appreciate knowing if my approach is correct in keeping the key and what you would advise me.
Thank you very much in advance.
Firebase takes care of the encryption for you. The data you send in the network does not go through in plain text. To secure your database, consider adding Security Rules.
See https://firebase.google.com/docs/rules

Creating secret keys with same shared secret leads to different results

i'm working on Android application which requires to create secure channel between the app and the server(.NET), the first step is we creating shared secret, the app generates key pair (EC) and sends the public key to the server, then the server creates its own key pair and send the public key to the app and save the shared secret created based on app's public and server private keys and the server send back to the app its own public, salt and iv, then the app perform key agreement, the result is that both the android app and the server have string created based on public-private keys.
Later when the android app want to send some encrypted message to the server, i take the generated secret, salt and iv and creates secret key to be able use Cipher to encrypt the message.
The problem is even though the app and the server have exact the same shred secret, salt and iv for some reason android generates different SecretKey than the server (we checked in debug mode to see that the shared secret salt and iv are same).
Here is the code i use to create secret key:
byte[] sharedSecretBytes = Base64.decode(sharedSecretBase64, Base64.DEFAULT);
byte[] ivBytes = Base64.decode(ivBase64, Base64.DEFAULT);
byte[] saltBytes = Base64.decode(saltBase64, Base64.DEFAULT);
String sharedSecret = new String(sharedSecretBytes, "UTF-8");
//i tried to use different encoding, no luck.
//String sharedSecretAscii = new String(sharedSecretBytes, "ASCII");
char[] charArray = sharedSecret .toCharArray();
PBEKeySpec keySpec = new PBEKeySpec(charArray, saltBytes, 1000, 256);
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
SecretKey key = secretKeyFactory.generateSecret(keySpec);
//here the keyBytes are not the same as generated in the server.
byte[] keyBytes = key.getEncoded();
//i saw in some tutorial somene do this, not sure why because we can provide the key as is to the cipher,
//the problem is that both not working.
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getEncoded(), "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] encrypted = cipher.doFinal(logNumber.getBytes("UTF-8"));
String asString = Base64.encodeToString(encrypted, Base64.NO_WRAP);
I also made try to pub hard-coded shared secret as "123abc" in both android and server side and actually it worked so my only guess is that the problem coming from converting the sharedSecrte.toCharArray(), if the sharedSecret contains weird characters can lead java generating bad secret key?
Also we working on iOS app that make exactly same process and have no issue to generate correct keys.
In the end the only thing that worked as expected was to use Bouncy Castle library
What i done is :
PKCS5S2ParametersGenerator generator = new PKCS5S2ParametersGenerator(new SHA256Digest());
generator.init(sharedSecretBytes, saltBytes, 1000);
byte[] dk = ((KeyParameter) generator.generateDerivedParameters(256)).getKey();
SecretKeySpec secretKeySpec = new SecretKeySpec(dk, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] encrypted = cipher.doFinal(logNumber.getBytes("UTF-8"));
Still don't understand why native java.security makes trouble .

Cipher.getInstance() is failing with java.security.NoSuchAlgorithmException: Cannot find any provider supporting AES/ECB/ZeroBytePadding

I have an Android app that encrypts data using AES with ECB and ZeroBytePadding. Everything works fine in that environment: encrypted data gets decrypted in Android without a problem, as follows:
public static String encrypt(byte[] raw, byte[] clear) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/ZeroBytePadding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(clear);
return android.util.Base64.encodeToString(encrypted, android.util.Base64.NO_WRAP);
}
public static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/ZeroBytePadding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encrypted);
return decrypted;
}
However, I recently decided to decrypt the data in a Web app and when I tried to use the same decrypt() method, Cipher.getInstance("AES/ECB/ZeroBytePadding") threw an exception:
java.security.NoSuchAlgorithmException: Cannot find any provider supporting
AES/ECB/ZeroBytePadding
I assume that some Android library is providing a suitable cipher provider that is missing from javax.crypto.Cipher. Has anyone else had this problem or know what I can do about it? Changing the cipher padding to PKCS5PADDING is not an option, due to the many messages that are already encrypted with the earlier options.
Actually there is really not such an algorithm. Look at official java docs here http://docs.oracle.com/javase/7/docs/api/javax/crypto/Cipher.html

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 :(

Common Encryption process for iPhone and Android

hi guys is there any simple way to encrypt and decrypt the images across platforms like decrypting the image in android encrypted in the iPhone and vise-versa.
Thanks in Advance..
You can use 56 bit DES encryption. It is supported both in iphone and android. You cannot use RSA because image may be larger than 127 byte. Two years before when I was trying with AES 128 bit encryption. I found there was limitation of using AES 128 bit encryption and put it in market place. So avoid AES also. java supprots AES. Hence nadorid also supports DES
AES encryption is the best way encrypt a file in android or in IOS.In android I have tried encryption.This link will help you to do in android .The below code will help tou to encrypt a byte array with the key in android.
encryptionKey will be your password
public static byte[] encrypt(byte[] key, byte[] data) throws Exception
{
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(data);
return encrypted;
}
/**
* DEcrypt byte array with given Key using AES Algorithm
* Key can be generated using <Code>getKey()</Code>
* #param key Key that Is used for decrypting data
* #param data Data passed to decrypt
* #return decrypted data
* */
public static byte[] decrypt1(byte[] key, byte[] encrypted) throws Exception
{
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encrypted);
return decrypted;
}
/**
* get the Key for encryption this can be used for while decrypting and encrypting too.
* */
public static byte[] getKey() throws Exception
{
byte[] keyStart = EncrypteDecrypte.encryptionKey.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();
return key;
}

Categories

Resources