SecureRandom provider "Crypto" is not available in Android N for deterministic key generation

Users can purchase the "Pro" version of my application. When they do this, I save and verify their purchase as follows.

  • Combine the custom UUID and another unique string.
  • The resulting string is then encrypted using static seed. I do this with SecureRandom.getInstance("SHA1PRNG", "Crypto") - that’s the problem!
  • As a result, the encrypted string is an “unlock code”.
  • Therefore, I always know the expected unique value of the unlock code for the user.
  • When a user buys "Pro", I save the "unlock code" in the database.
  • I check if the user has “Pro”, seeing if the saved “unlock code” is stored in the database of the expected code based on their unique information.

So, not the best system, but everything is confusing enough for my modest application.

The problem is that SecureRandom.getInstance("SHA1PRNG", "Crypto") does not work in N, because "Crypto" is not supported. I found out that relying on specific vendors is bad practice, and Crypto is not supported in N. Unfortunately.

So, I have a problem: I rely on encrypting the value-seed pair to always have the same result. Android N does not support the encryption provider that I use, so I don’t know how to ensure that the encryption output is the same on N as on other devices.

My questions:

  • Can I include "Crypto" in my APK so that it is always available?
  • Can I otherwise provide the same output when encrypting a value-seed pair on Android N?

My code is:

 public static String encrypt(String seed, String cleartext) throws Exception { byte[] rawKey = getRawKey(seed.getBytes(), seed); byte[] result = encrypt(rawKey, cleartext.getBytes()); return toHex(result); // "unlock code" which must always be the same for the same seed and clearText accross android versions } private static byte[] getRawKey(byte[] seed, String seedStr) throws Exception { SecureRandom sr; sr = SecureRandom.getInstance("SHA1PRNG", "Crypto"); // what used to work KeyGenerator kgen = KeyGenerator.getInstance("AES"); sr.setSeed(seed); kgen.init(128, sr); SecretKey skey = kgen.generateKey(); byte[] raw = skey.getEncoded(); return raw; } 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; } public static String toHex(byte[] buf) { if (buf == null) return ""; StringBuffer result = new StringBuffer(2 * buf.length); for (int i = 0; i < buf.length; i++) { appendHex(result, buf[i]); } return result.toString(); } 
+7
android random cryptography android-n secure-random
source share
3 answers

I recently talked to the Android Security team.

In Android N, SHA1PRNG was deleted because we do not have a safe implementation. In particular, calling .setSeed(long) before requesting output from PRNG replaces all entropy in the SecureRandom instance.

This behavior has long been referred to as a security failure (read: it often causes subtle errors in applications), so we decided not to replicate it when replacing the SecureRandom provider.

If you need PRNG, just use new SecureRandom() .

That says ... SecureRandom () is not intended to be used as a key derivation function , as it was in your example. Please, do not do that! Instead, use an algorithm such as PBKDF2, available through SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1") .

We warned developers about this for a while. Please view these posts:

IF YOU REALLY NEED SHA1PRNG, EVEN AFTER ALL THAT ... , then a workaround is to copy the implementation from the Android source, for example, the one mentioned in his answer @ artjom-b.

But please do this only if you need compatibility when porting to PBKDF2 or the like.

+5
source share

Using PRNGs such as SecureRandom to retrieve deterministically data is usually a bad idea because there is a history of breaking changes. It is always recommended to use a specific implementation and include it in the application. In your case, you can simply copy the implementation code.

SecureRandom.getInstance("SHA1PRNG", "Crypto"); looking for the provider "Crypto", which org.apache.harmony.security.provider.crypto.CryptoProvider in Android 5.1.1 . It redirects to org.apache.harmony.security.provider.crypto.SHA1PRNG_SecureRandomImpl as the actual implementation. You can easily copy the code into your project under a different package and be sure to comply with the code license.

Then you can use it as follows:

 sr = new SecureRandom(new your.pkg.SHA1PRNG_SecureRandomImpl(), null); 

The second argument of the provider is not used according to code , but you can create a dummy provider.


The correct way to generate a key from some seed is to use the key derivation function (KDF). If seed has a password, then PBKDF2 is a good KDF when many iterations are given. If seed is key, KBKDF, such as HKDF, is recommended.

+4
source share

I added one class for CryptoProvider, which you can replace SecureRandom.getInstance ("SHA1PRNG", "Crypto"); - SecureRandom.getInstance ("SHA1PRNG", the new CryptoProvider ());

you can link to the following link for a solution, it works for me;

Security "Crypto" provider is deprecated in Android N

+3
source share

All Articles