This information is intended for developers with app(s) that contain unsafe cryptographic encryption patterns. That is, a ciphertext is generated with a statically computed secret key, salt, or initialization vector (IV). Locations of unsafe cryptographic encryption can be found in the Play Console notification for your app. If a location ends with “(in dynamically loaded code)” then the location is in code dynamically loaded by the app or by libraries used by the app. Applications typically use dynamically loaded code through on-demand feature delivery, though other unrecommended techniques exist (some unrecommended techniques also violate the Google Play policy and should not be used). Additionally, packers can transform application code into dynamically loaded code.
How to fix “Unsafe Cryptographic Encryption Patterns” alerts
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 the following code uses a statically computable secret key and a statically computable initialization vector: // Console alert refers to this method
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);
}
// The unsafe key and initialization vector are here and should be changed
byte[] cipherText = encryptionUtil(“abcdef...”, “010203040506”, plainText);
Statically Computed Values
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. Even if you manipulate keys, initialization vectors, and salts in complex ways prior to use, they remain unsafe if these manipulations are the same for every program execution.
Android Best Practices
We recommend that you utilize Jetpack Security for symmetric cryptography. If your app encrypts API keys, personally identifiable information (PII), or other sensitive data, EncryptedSharedPreferences can be used to securely store this data without worrying about the implementation of secret keys, initialization vectors, and salts. Further best practices and examples are available on this page. For securely transmitting data to other systems, developers should use TLS/SSL to secure data in transmission.
Jetpack Security utilizes the Google open source project Tink to handle symmetric authenticated encryption. For advanced use cases concerning lower level cryptographic operations, we suggest using Tink directly.
If the aforementioned Android best practices do not meet your use case, and you are required to explicitly manage keys, initialization vectors, and salts, then we recommend following these standards:
- Secret Keys: Symmetric secret keys must be unpredictable and secret. For encrypting local data, developers should construct secret keys using cryptographically secure randomness (or from user generated data, if using PBEKeySpecs) and store the secret keys using the AndroidKeystore.
- Initialization Vectors: Initialization vectors must be unique and unpredictable across multiple messages but do not need to be secret. Developers should construct initialization vectors using cryptographically secure randomness. Developers should store or transmit the initialization vectors along with the associated ciphertext.
- Salts: Salts must be unique and unpredictable across multiple hashes but do not need to be secret. Developers should construct salts using cryptographically secure randomness. Developers should store or transmit the salts along with the associated hashes.
Next steps
- Update your app using the steps highlighted above.
- Sign in to your Play Console and submit an updated version of your app.