Few days ago, In "Pre-launch report for APK" in Google Play Console, it start to flag me
Unsafe encryption
Detected in APK ???
Your app contains unsafe cryptographic encryption patterns. Please see this Google Help Centre article for details.
Vulnerable classes:
c.j.a.s.J.b
However, since the early day of APK, I do not change anything in encryption code/ description code. Hence, I'm not sure why Google starts to warn me on recent APK?
Any idea how to resolve? As, the information for vulnerable classes c.j.a.s.J.b
is not helpful.
I try to use Proguard + mapping.txt to retrace c.j.a.s.J.b
but able to figure what class is that.
Any idea how I can get rid of Google security warning?
The google play suggests with vulnerable classes with the function name, you can see in the dialog.
Review your app for statically computed keys, initialization vectors, and/or salts that are used in cryptographic encryption operations and ensure that these values are constructed safely
For example :
public byte[] encryptionUtil(String key, String iv, byte[] plainText) {
Cipher cipher = Cipher.getInstance(“AES/GCM/NoPadding”);
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), “AES”);
GCMParameterSpec paramSpec = new GCMParameterSpec(256, iv.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, keySpec, paramSpec);
return cipher.doFinal(plainText);
}
And you are calling a function as:
byte[] cipherText = encryptionUtil(“abcdef...”, “010203040506”, plainText);
Here your encryption key “abcdef...”
is provides as a static string. A statically computed value is a value that is the same on every execution of your app. Statically computed cryptographic values can be extracted from your app and used to attack your app’s encrypted data.
So you can use EncryptedSharedPreferences to store locally data
Reference link https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences
OR
For more details: Remediation for Unsafe Cryptographic Encryption
-
security-crypto library forcing to update min-sdk-version 26. Any solution for the lower version? Nov 6 '19 at 7:03
I think you are using some encryption/decryption code with statically stored key. A statically computed value is a value that is the same on every execution of your app. Statically computed cryptographic values can be extracted from your app and used to attack your app’s encrypted data. So Google give this warning to change that stored key with dynamically generated key. For that you can generate different key on every launch. To solve this problem generate dynamic encryption/decryption key on every launch. For that you can find more info here https://developer.android.com/jetpack/androidx/releases/security
-
1What if you need to decrypt some data that was previously encrypted with a former key? Sep 19 '19 at 5:36
-
-
2How is it possible to have a single public key that can decrypt a message encrypted with a different private key? Aren't the keys generated as a standalone pair? Sep 19 '19 at 11:20
-
I think, it's not related to original asked questions. Plz ask separate question. but you can get your questions answer from here.– MaheshSep 23 '19 at 7:39https://stackoverflow.com/questions/58002913/your-app-contains-unsafe-cryptographic-encryption-patterns-how-i-can-get-rid#########################################################################################################################################################3This question already has answers here:"Your app contains unsafe cryptographic encryption patterns" - How I can get rid of this warning? (2 answers)Closed 2 years ago.
I'm encrypting the password for
firebase
sign in, it's working well but I received a warning in google play console thatyour app contains unsafe cryptographic encryption patterns
how can I get rid of it ??I'm trying it on android studio.
public static class AESCrypt { private static final String ALGORITHM = "AES"; private static final String KEY = "1Hbfh667adfDEJ78"; public static String encrypt(String value) throws Exception { Key key = generateKey(); Cipher cipher = Cipher.getInstance(AESCrypt.ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, key); byte [] encryptedByteValue = cipher.doFinal(value.getBytes("utf-8")); String encryptedValue64 = Base64.encodeToString(encryptedByteValue, Base64.DEFAULT); return encryptedValue64; } public static String decrypt(String value) throws Exception { Key key = generateKey(); Cipher cipher = Cipher.getInstance(AESCrypt.ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, key); byte[] decryptedValue64 = Base64.decode(value, Base64.DEFAULT); byte [] decryptedByteValue = cipher.doFinal(decryptedValue64); String decryptedValue = new String(decryptedByteValue,"utf-8"); return decryptedValue; } private static Key generateKey() throws Exception { Key key = new SecretKeySpec(AESCrypt.KEY.getBytes(),AESCrypt.ALGORITHM); return key; }
3The main issues are that you use a cipher with no integrity and a hard coded cryptographic key. If you analyse your source with Find Security Bugs you get CIPHER_INTEGRITY and HARD_CODE_KEY warning:
The cipher does not provide data integrity [com.lloyds.keystorage.AESCrypt] At AESCrypt.java:[line 25] CIPHER_INTEGRITY The cipher does not provide data integrity [com.lloyds.keystorage.AESCrypt] At AESCrypt.java:[line 15] CIPHER_INTEGRITY Hard coded cryptographic key found [com.lloyds.keystorage.AESCrypt] At AESCrypt.java:[line 35] HARD_CODE_KEY
The solution is to use a cipher that includes a Hash based Message Authentication Code (HMAC) to sign the data:
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
And to store the secret key in separate configuration files or keystores.
Below is the whole class after a full refactoring:
import android.util.Base64 import static java.nio.charset.StandardCharsets.UTF_8; import java.security.Key; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; public class AESCrypt { private static final String TRANSFORMATION = "AES/GCM/NoPadding"; public static String encrypt(String value) throws Exception { Key key = generateKey(); Cipher cipher = Cipher.getInstance(TRANSFORMATION); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] encryptedByteValue = cipher.doFinal(value.getBytes(UTF_8)); return Base64.encodeToString(encryptedByteValue, Base64.DEFAULT); } public static String decrypt(String value) throws Exception { Key key = generateKey(); Cipher cipher = Cipher.getInstance(TRANSFORMATION); cipher.init(Cipher.DECRYPT_MODE, key); byte[] decryptedValue64 = Base64.decode(value, Base64.DEFAULT); byte[] decryptedByteValue = cipher.doFinal(decryptedValue64); return new String(decryptedByteValue, UTF_8); } private static Key generateKey() { return new SecretKeySpec(Configuration.getKey().getBytes(UTF_8), TRANSFORMATION); } }
-
But,
java.util.Base64
requires API level 26. What's the reason of usingjava.util.Base64
orandroid.util.Base64
? Sep 22 '19 at 22:32 -
Also, what is the implementation of
Configuration.getKey()
? If it is always returning the same value for different devices, will Google flag warning again? Sep 22 '19 at 22:37 -
@CheokYanCheng, can you share a link to the resource showing that you need the level 26 for a Java 8 class
java.util.Base64
?– BorisSep 23 '19 at 10:51 -
1See developer.android.com/reference/java/util/Base64.Encoder (Added in API level 26) Sep 23 '19 at 10:58
-
1
Not the answer you're looking for? Browse other questions tagged java android firebase-authentication or ask your own question.
https://stackoverflow.com/questions/58026804/unsafe-cryptographic-encryption-patterns-how-to-solve-it
-
com.java.android.sample.Java...
?