Security "Crypto" provider deprecated in Android N
Posted by Sergio Giro, software engineer
If your Android app derives keys using the SHA1PRNG algorithm from the Crypto provider, you must start using a real key derivation function and possibly re-encrypt your data.
The Java Cryptography Architecture allows developers to create an instance of a class like a cipher, or a pseudo-random number generator, using calls like:
On Android, we don’t recommend specifying the provider. In general, any call to the Java Cryptography Extension (JCE) APIs specifying a provider should only be done if the provider is included in the application or if the application is able to deal with a possible ProviderNotFoundException.
Unfortunately, many apps depend on the now removed “Crypto” provider for an anti-pattern of key derivation.
This provider only provided an implementation of the algorithm “SHA1PRNG” for instances of SecureRandom. The problem is that the SHA1PRNG algorithm is not cryptographically strong. For readers interested in the details, On statistical distance based testing of pseudo random sequences and experiments with PHP and Debian OpenSSL, Section 8.1, by Yongge Wang and Tony Nicol, states that the “random” sequence, considered in binary form, is biased towards returning 0s, and that the bias worsens depending on the seed.
As a result, in Android N we are deprecating the implementation of the SHA1PRNG algorithm and the Crypto provider altogether. We’d previously covered the issues with using SecureRandom for key derivation a few years ago in Using Cryptography to Store Credentials Safely. However, given its continued use, we will revisit it here.
A common but incorrect usage of this provider was to derive keys for encryption by using a password as a seed. The implementation of SHA1PRNG had a bug that made it deterministic if setSeed() was called before obtaining output. This bug was used to derive a key by supplying a password as a seed, and then using the “random” output bytes for the key (where “random” in this sentence means “predictable and cryptographically weak”). Such a key could then be used to encrypt and decrypt data.
In the following, we explain how to derive keys correctly, and how to decrypt data that has been encrypted using an insecure key. There’s also a full example, including a helper class to use the deprecated SHA1PRNG functionality, with the sole purpose of decrypting data that would be otherwise unavailable.
Keys can be derived in the following way:
- If you’re reading an AES key from disk, just store the actual key and don’t go through this weird dance. You can get a SecretKey for AES usage from the bytes by doing:
SecretKey key = new SecretKeySpec(keyBytes, “AES”);
- If you’re using a password to derive a key, follow Nikolay Elenkov’s excellent tutorial with the caveat that a good rule of thumb is the salt size should be the same size as the key output. It looks like this:
That’s it. You should not need anything else.
To make transitioning data easier, we covered the case of developers that have data encrypted with an insecure key, which is derived from a password every time. You can use the helper class InsecureSHA1PRNGKeyDerivator in the example app to derive the key.
You can then re-encrypt your data with a securely derived key as explained above, and live a happy life ever after.
Note 1: as a temporary measure to keep apps working, we decided to still create the instance for apps targeting SDK version 23, the SDK version for Marshmallow, or less. Please don’t rely on the presence of the Crypto provider in the Android SDK, our plan is to delete it completely in the future.
Note 2: Because many parts of the system assume the existence of a SHA1PRNG algorithm, when an instance of SHA1PRNG is requested and the provider is not specified we return an instance of OpenSSLRandom, which is a strong source of random numbers derived from OpenSSL.
除非另有声明，本博客所有文章采用的授权方式为 自由转载-非商用-非衍生-保持署名 ，转载请务必注明出处，谢谢。