Some SecureRandom Thoughts
August 14th, 2013 | Published in Google Android
Posted by Alex Klyubin, Android Security Engineer
The Android security team has been investigating the root cause of the compromise of a bitcoin transaction that led to the update of multiple Bitcoin applications on August 11.
We have now determined that applications which use the Java Cryptography Architecture (JCA) for key generation, signing, or random number generation may not receive cryptographically strong values on Android devices due to improper initialization of the underlying PRNG. Applications that directly invoke the system-provided OpenSSL PRNG without explicit initialization on Android are also affected. Applications that establish TLS/SSL connections using the HttpClient
and java.net
classes are not affected as those classes do seed the OpenSSL PRNG with values from /dev/urandom
.
Developers who use JCA for key generation, signing or random number generation should update their applications to explicitly initialize the PRNG with entropy from /dev/urandom
or /dev/random
. A suggested implementation is provided at the end of this blog post. Also, developers should evaluate whether to regenerate cryptographic keys or other random values previously generated using JCA APIs such as SecureRandom
, KeyGenerator
, KeyPairGenerator
, KeyAgreement
, and Signature
.
In addition to this developer recommendation, Android has developed patches that ensure that Android’s OpenSSL PRNG is initialized correctly. Those patches have been provided to OHA partners.
We would like to thank Soo Hyeon Kim, Daewan Han of ETRI and Dong Hoon Lee of Korea University who notified Google about the improper initialization of OpenSSL PRNG.
Update: the original code sample below crashed on a small fraction of Android devices due to /dev/urandom
not being writable. We have now updated the code sample to handle this case gracefully.
/* * This software is provided 'as-is', without any express or implied * warranty. In no event will Google be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, as long as the origin is not misrepresented. */ import android.os.Build; import android.os.Process; import android.util.Log; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.security.NoSuchAlgorithmException; import java.security.Provider; import java.security.SecureRandom; import java.security.SecureRandomSpi; import java.security.Security; /** * Fixes for the output of the default PRNG having low entropy. * * The fixes need to be applied via {@link #apply()} before any use of Java * Cryptography Architecture primitives. A good place to invoke them is in the * application's {@code onCreate}. */ public final class PRNGFixes { private static final int VERSION_CODE_JELLY_BEAN = 16; private static final int VERSION_CODE_JELLY_BEAN_MR2 = 18; private static final byte[] BUILD_FINGERPRINT_AND_DEVICE_SERIAL = getBuildFingerprintAndDeviceSerial(); /** Hidden constructor to prevent instantiation. */ private PRNGFixes() {} /** * Applies all fixes. * * @throws SecurityException if a fix is needed but could not be applied. */ public static void apply() { applyOpenSSLFix(); installLinuxPRNGSecureRandom(); } /** * Applies the fix for OpenSSL PRNG having low entropy. Does nothing if the * fix is not needed. * * @throws SecurityException if the fix is needed but could not be applied. */ private static void applyOpenSSLFix() throws SecurityException { if ((Build.VERSION.SDK_INT VERSION_CODE_JELLY_BEAN_MR2)) { // No need to apply the fix return; } try { // Mix in the device- and invocation-specific seed. Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto") .getMethod("RAND_seed", byte[].class) .invoke(null, generateSeed()); // Mix output of Linux PRNG into OpenSSL's PRNG int bytesRead = (Integer) Class.forName( "org.apache.harmony.xnet.provider.jsse.NativeCrypto") .getMethod("RAND_load_file", String.class, long.class) .invoke(null, "/dev/urandom", 1024); if (bytesRead != 1024) { throw new IOException( "Unexpected number of bytes read from Linux PRNG: " + bytesRead); } } catch (Exception e) { throw new SecurityException("Failed to seed OpenSSL PRNG", e); } } /** * Installs a Linux PRNG-backed {@code SecureRandom} implementation as the * default. Does nothing if the implementation is already the default or if * there is not need to install the implementation. * * @throws SecurityException if the fix is needed but could not be applied. */ private static void installLinuxPRNGSecureRandom() throws SecurityException { if (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2) { // No need to apply the fix return; } // Install a Linux PRNG-based SecureRandom implementation as the // default, if not yet installed. Provider[] secureRandomProviders = Security.getProviders("SecureRandom.SHA1PRNG"); if ((secureRandomProviders == null) || (secureRandomProviders.lengthJoin the discussion on
+Android Developers