Using Cryptography to Store Credentials Safely

http://android-developers.blogspot.com/2013/02/using-cryptography-to-store-credentials.html

Posted byTrevor Johns, Android Developer Relations team

Following our talk"Security and Privacy in Android Apps"at Google I/O last year, many people had specific questions about how to use cryptography in Android. Many of those revolved around which APIs to use for a specific purpose. Let's look at how to use cryptography to safely store user credentials, such as passwords and auth tokens, on local storage.

An anti-pattern

A common (but incorrect) pattern that we've recently become aware of is to useSecureRandomas a means of generating deterministic key material, which would then be used to encrypt local credential caches. Examples are not hard to find, such ashere,here,here, and elsewhere.

In this pattern, rather than storing an encryption key directly as a string inside an APK, the code uses a proxy string to generate the key instead — similar to a passphrase. This essentially obfuscates the key so that it's not readily visible to attackers. However, a skilled attacker would be able to easily see around this strategy. We don't recommend it.

The fact is, Android's existing security model already provides plenty of protection for this kind of data. User credentials should be stored with theMODE_PRIVATEflag set and stored in internal storage, rather than on an SD card, since permissions aren't enforced on external storage. Combined with device encryption, this provides protection from most types of attacks targeting credentials.

However, there's another problem with usingSecureRandomin the way described above. Starting with Android 4.2, the defaultSecureRandomprovider is OpenSSL, and a developer can no longer overrideSecureRandom’s internal state. Consider the following code:

 SecureRandom secureRandom = new SecureRandom(); byte[] b = new byte[] { (byte) 1 }; secureRandom.setSeed(b); // Prior to Android 4.2, the next line would always return the same number! System.out.println(secureRandom.nextInt());

The old Bouncy Castle-based implementation allowed overriding the internally generated, /dev/urandom based key for eachSecureRandominstance. Developers which attempted to explicitly seed the random number generator would find that their seed replaces, not supplements, the existing seed (contrary to thereference implementation’s documentation). Under OpenSSL, this error-prone behavior is no longer possible.

Unfortunately, applications who relied on the old behavior will find that the output fromSecureRandomchanges randomly every time their application starts up. (This is actually a very desirable trait for a random number generator!) Attempting to obfuscate encryption keys in this manner will no longer work.

The right way

A more reasonable approach is simply to generate a truly random AES key when an application is first launched:

public static SecretKey generateKey() throws NoSuchAlgorithmException {  // Generate a 256-bit key  final int outputKeyLength = 256;  SecureRandom secureRandom = new SecureRandom();  // Do *not* seed secureRandom! Automatically seeded from system entropy.  KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");  keyGenerator.init(outputKeyLength, secureRandom);  SecretKey key = keyGenerator.generateKey();  return key;}

Note that the securityof this approach relies on safeguarding the generated key, which is is predicated on the security of the internal storage. Leaving the target file unencrypted (but set toMODE_PRIVATE) would provide similar security.

Even more security

If your app needs additional encryption, a recommended approach is to require a passphase or PIN to access your application. This passphrase could be fed into PBKDF2 to generate the encryption key. (PBKDF2 is a commonly used algorithm for deriving key material from a passphrase, using a technique known as "key stretching".) Android provides an implementation of this algorithm insideSecretKeyFactoryasPBKDF2WithHmacSHA1:

public static SecretKey generateKey(char[] passphraseOrPin, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException {  // Number of PBKDF2 hardening rounds to use. Larger values increase  // computation time. You should select a value that causes computation  // to take >100ms.  final int iterations = 1000;   // Generate a 256-bit key  final int outputKeyLength = 256;  SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");  KeySpec keySpec = new PBEKeySpec(passphraseOrPin, salt, iterations, outputKeyLength);  SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);  return secretKey;}

The salt should be a random string, again generated usingSecureRandomand persisted on internal storage alongside any encrypted data. This is important to mitigate the risk of attackers using a rainbow table to precompute password hashes.

Check your apps for proper use of SecureRandom

As mentioned above and in theNew Security Features in Jelly Bean, the default implementation ofSecureRandomis changed in Android 4.2. Usingit to deterministically generate keys is no longer possible.

If you're one of the developers who'sbeen generating keys the wrong way, we recommend upgrading your app today to prevent subtle problems as more users upgrade to devices running Android 4.2 or later

更多相关文章

  1. 代码中设置drawableleft
  2. android 3.0 隐藏 系统标题栏
  3. Android开发中activity切换动画的实现
  4. Android(安卓)学习 笔记_05. 文件下载
  5. Android中直播视频技术探究之—摄像头Camera视频源数据采集解析
  6. 技术博客汇总
  7. android 2.3 wifi (一)
  8. AndRoid Notification的清空和修改
  9. Android中的Chronometer

随机推荐

  1. MVC架构之模型/视图/控制器/容器/门面(静
  2. 【Zabbix】Zabbix基于SNMP监控配置
  3. 迭代器总结
  4. 解刨mvc中pathinfo路由解析原理
  5. 怎么画漂亮鞋子?二次元人物鞋子绘画技巧!
  6. 如何学习画人物头像?二次元人物头像绘画步
  7. 零基础如何学习原画设计?学CG原画设计技巧
  8. Windows配置DNS服务器
  9. curl扩展服务器通讯与mvc小框架实现
  10. Ubuntu18.04..5 配置国内镜像源:解决E: Fa