Android KeyStore 存储密码

以前KeyStore的概念就是存储app签名的地方,如果你查Android KeyStore,出来的资料都是如何给app生成签名什么的,但是这里的意义却不相同。Android KeyStore系统允许你存储加密密钥,并且难以从设备中导出,并且可以指明key的使用规则,例如只有用户验证后,才可以使用key等。

防止导出的方法:

  • Key material never enters the application process.
  • Key material may be bound to the secure hardware(e.g., Trusted Execution Environment(TEE), Secure Element(SE)) of the Android device.

可以通过以下代码获取你的KeyStore。

try {
    KeyStore mKeyStore = KeyStore.getInstance(“YourType");
} catch (KeyStoreException e) {
    return;
}

KeyStore.getInstance(参数)中的参数可以传以下内容:

  • “AndroidKeyStore”:这里要先区分下AndroidKeyStore和Android KeyStore,虽然这两个一样,但是后者中间多了个空格,意义是不一样的,前者是子集,后者是父集,后者包含前者。而AndroidKeyStore主要是用来存储一些密钥key的,存进该处的key可以为其设置KeyProtection,例如只能通过用户验证才能取出key使用等。这些key是存在系统里的,不是在app的目录下,并且每个app不能访问其他app的key,如果app1创建了key1,并且存储的时候命名为temp,app2去通过temp去访问key,是获取不到的!!
  • KeyStore.getDefaultType():该函数返回的是一个字符串,在java下,返回的是JKS,在Android下,返回的是BKS( 生成android使用的BKS证书)。(注:android 系统中使用的证书要求以BKS的库文件结构保存,通常情况下,我们使用java的keytool只能生成jks的证书库。读取key可以通过psw来读取)。当你使用这个keystore的时候,其文件存放在data(沙盒中)。
  • 其他情况,还有一些其他的keystore,我目前暂不清楚,但有看到过传一些其他的参数。

那key分为哪些呢:

  • Symmetric key(对称密钥): secret key
  • Asymmetric key(非对称密钥): public key && private key

如何生成key,可以通过以下两个类:

  • KeyGenerator(KeyPairGenerator): 根据一些指定的参数,例如算法,补码模式等参数,来生成一个新的key。
  • KeyFactory: 当服务器想告知客户端key,只是将key的byte数组传过来,可以通过这个类来还原key。

    当然生成key的时候,你要说明,可以通过KeySpec的子类来说明。

看一个生成key和还原key的例子:

//对称key即SecretKey创建和导入,假设双方约定使用DES算法来生成对称密钥
KeyGeneratorkeyGenerator = KeyGenerator.getInstance("DES");
//设置密钥长度。注意,每种算法所支持的密钥长度都是不一样的。DES只支持64位长度密钥
keyGenerator.init(64);
//生成SecretKey对象,即创建一个对称密钥,并获取二进制的书面表达
SecretKey secretKey = keyGenerator.generateKey();
byte[] keyData =secretKey.getEncoded();
//日常使用时,一般会把上面的二进制数组通过Base64编码转换成字符串,然后发给使用者
String keyInBase64 =Base64.encodeToString(keyData,Base64.DEFAULT);
 e(TAG,“==>secret key: encrpted data =”+ bytesToHexString(keyData));
 e(TAG,"==>secrety key:base64code=" + keyInBase64 +“  key:alg=" + secretKey.getAlgorithm());

//假设对方收到了base64编码后的密钥,首先要得到其二进制表达式,用二进制数组构造KeySpec对象。对称key使用SecretKeySpec类
 byte[] receivedKeyData =Base64.decode(keyInBase64,Base64.DEFAULT);
SecretKeySpec keySpec =new SecretKeySpec(receivedKeyData,”DES”);
//创建对称Key导入用的SecretKeyFactory
SecretKeyFactorysecretKeyFactory = SecretKeyFactory.getInstance(”DES”);
//根据KeySpec还原Key对象,即把key的书面表达式转换成了Key对象
SecretKey receivedKeyObject = secretKeyFactory.generateSecret(keySpec);
byte[]encodedReceivedKeyData = receivedKeyObject.getEncoded();
e(TAG,"==>secret key: received key encoded data ="+bytesToHexString(encodedReceivedKeyData));

实际应用:

如果app想存储一段字符Content(类似密码或者token)在客户端,如何利用keyStore保证其安全性,防止被他人读取。

解决方案步骤:

1.如果将Content存入KeyStore,那么只能通过将Content作为Key存入KeyStore中,刚刚讲过生成key的时候有两种方法,一种是生成key,一种是还原key(keyFactory),因为我们这里已经知道content,那么是还原key,通过KeyFactory。

2.该key属于SecretKey,因为没有私钥和公钥的概念。应该使用PBE算法(Password Based Encryption基于密码加密)来生成。

3.接下来就是生成key的过程。刚刚说过生成key的时候,需要指明key的一些参数,通过KeySpec的子类。查了一下,正好有PBEKeySpec!

上代码:

//根据内容生成secret key
PBEKeySpec keySpec = new PBEKeySpec(content.toCharArray());
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWITHSHA1ANDDES");
SecretKey key = keyFactory.generateSecret(keySpec);
KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(key);
//存储key
keyStore.setEntry(keyName, entry, new KeyStore.PasswordProtection(psw.toCharArray()));

PBEWITHSHA1ANDDES这个是加密的算法,主要的算法是PBE,加之sha1和des。

4。接下来就是存储在哪个KeyStore。因为我们生成的是SecretKey,如果存在AndroidKeyStore中,会抛出异常。因为AndroidKeyStore对于SecretKey只支持AES和HMAC。

所以目前只能存在BKS这个keyStore中,也就是数据是放在沙盒中的。

完整的生成和存储key的代码:

File file = new File(context.getFilesDir(), "temp");
if (!file.exists()) {
     keyStore.load(null, null);
     file.createNewFile();
     return;
}
FileInputStream in = new FileInputStream(file);
keyStore.load(in, psw.toCharArray());
in.close();
PBEKeySpec keySpec = new PBEKeySpec(token.toCharArray());
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWITHSHA1ANDDES");
SecretKey key = keyFactory.generateSecret(keySpec);
KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(key);
KeyProtection.Builder builder = new KeyProtection.Builder(KeyProperties.PURPOSE_VERIFY);
builder.setUserAuthenticationRequired(true);
mKeyStore.setEntry(“alias", entry, builder.build());
mkeyStore.store(null);

在上述代码中,在load keyStore的时候,为其指定了密码(psw.toCharArray())。在存数据的时候,也为其添加了密码保护。

现在数据已经存好了,但是不安全啊,因为如果只要将手机Root,就可以拿到沙盒中的数据。而且psw是app写死的,如果别人知道了我们的psw呢???岌岌可危!!

5.现在增加安全性!前面已经提到如果将key存放在AndroidKeyStore中,可以为key设置一些保护(KeyProtection),比如说用户验证过才可以使用key,那我们可以利用这点来增加安全性。

首先我们生成一个key,并指定在用户验证后才可以使用这个key,并且存放在AndroidKeyStore中。使用这个key去加密content,然后将加密后的内容存储在BKS中。

当你取出的时候,先使用指纹验证,然后取出AndroidKeyStore中的key,再取出BKS中的内容,用key解密。

用这种方案较为安全!

看代码:

在AndroidKeyStore中生成key

void generateKey() {
    LogUtil.d("LocalAndroidKeyStore生成加密密钥...");
    //这里使用AES + CBC + PADDING_PKCS7,并且需要用户验证方能取出
    try {
        final KeyGenerator generator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
        mStore.load(null);
        final int purpose = KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_ENCRYPT;
        final KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(SdkData.accountId, purpose);
        builder.setUserAuthenticationRequired(true);
        builder.setBlockModes(KeyProperties.BLOCK_MODE_CBC);
        builder.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7);
        generator.init(builder.build());
        generator.generateKey();
        LogUtil.d("LocalAndroidKeyStore生成加密密钥成功");
    } catch (Exception e) {
        LogUtil.d("LocalAndroidKeyStore生成加密密钥失败");
        e.printStackTrace();
    }
}

取出key为content加密:

mKeyStore.load(null);
final SecretKey key = (SecretKey) mKeyStore.getKey(AES_KEY_NAME, null);
if (key == null) return;
final Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"+ KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7);
cipher.init(Cipher.ENCRYPT_MODE, key);
final FingerprintManager.CryptoObject crypto = new FingerprintManager.CryptoObject(cipher);
mFingerprintManager.authenticate(crypto, null, 0, new SimpleAuthenticationCallback() {
            @Override
            public void onAuthenticationSucceeded(final FingerprintManager.AuthenticationResult result) {
                final Cipher cipher = result.getCryptoObject().getCipher();
                byte[] encrypted = cipher.doFinal(token.getBytes());
                byte[] IV = cipher.getIV();
                String result= Base64.encodeToString(encrypted, Base64.URL_SAFE);
                Log.d("tag", “result:” + result);
            }
     }, new Handler());

取出key为加密的内容解密:

 mKeyStore.load(null);
final SecretKey key = (SecretKey) mKeyStore.getKey(AES_KEY_NAME, null);
if (key == null) return;
final Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"+ KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7);
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(mIV));
final FingerprintManager.CryptoObject crypto = new FingerprintManager.CryptoObject(cipher);
mFingerprintManager.authenticate(crypto, null, 0, new SimpleAuthenticationCallback() {
            @Override
            public void onAuthenticationSucceeded(final FingerprintManager.AuthenticationResult result) {
                      final Cipher cipher = result.getCryptoObject().getCipher();
                      Log.d("tag", "Base 64 of data to decrypt is:\n" + Base64.encodeToString(encryptedToken, Base64.URL_SAFE) + "\n");
                      try {
                                 byte[] decrypted = cipher.doFinal(mEncrypted.getBytes());
                                 Log.d("tag", "Decrypted data is:\n" + Base64.encodeToString(decrypted, Base64.URL_SAFE) + "\n");
                     } catch (IllegalBlockSizeException | BadPaddingException e) {}
            }
   }, new Handler());
时间: 2024-08-29 06:07:07

Android KeyStore 存储密码的相关文章

转:修改Android签名证书keystore的密码、别名alias以及别名密码

转自:http://blog.k-res.net/archives/1671.html 二月 5, 2014  |  Posted by K-Res 之前在测试Eclipse ADT的Custom debug keystore自定义调试证书的时候,发过一篇关于调试证书规格的博文:Eclipse ADT的Custom debug keystore所需证书规格,提到过自定义调试证书的密码和alias命名以及alias密码都是有规矩的.其实Android应用开发接入各种SDK时会发现,有很多SDK是需

修改Android签名证书keystore的密码、别名alias以及别名密码

之前在测试Eclipse ADT的Custom debug keystore自定义调试证书的时候,发过一篇关于调试证书规格的博文:Eclipse ADT的Custom debug keystore所需证书规格,提到过自定义调试证书的密码和alias命名以及alias密码都是有规矩的.其实Android应用开发接入各种SDK时会发现,有很多SDK是需要靠package name和keystore的指纹hash来识别的(百度地图SDK.Facebook SDK等等-),这样如果使用默认自动生成的de

Android数据存储-通过SharedPreferences实现记住密码的操作

在Android中登陆中,为了实现用户的方便,往往需要根据用户的需要进行记住密码的操作,所以,在Android数据存储中SharedPreferences恰恰可以实现这一点 下面,小编将带领大家通过SharedPreferences实现记住密码的操作 一.首先:介绍下什么是SharedPreferences? SharedPreferences是Android平台上一个轻量级的存储类,用来保存应用的一些常用配置. 二.介绍了SharedPreferences,我们将通过记住SharedPrefe

android默认debug.keystore的密码

在Eclipse里面编译生成的APK中有一个签名的,它默认的key是debug.keystore,它默认的路径是: C:\Users\<用户名>\.android\debug.keystore 这个key的密码是:android ================================================================ 我们可以输入如下命令来查看其详细信息: keytool -list -keystore debug.keystore 输入密码:andro

Android Keystore 对称-非对称加密

Android 提供了 KeyStore 等可以长期存储和检索加密密钥的机制,Android KeyStore 系统特别适合于存储加密密钥. “AndroidKeyStore” 是 KeyStore 的一个子集,存进 AndroidKeyStore 的 key 将受到签名保护,并且这些 key 是存在系统里的,而不是在 App 的 data 目录下,依托于硬件的 KeyChain 存储,可以做到 private key 一旦存入就无法取出, 每个 App 自己创建的 key,别的应用是访问不到的

Android——数据存储(四种方式之一)SharedPrefereces

Android--数据存储(四种方式) 1.SharedPrefereces   轻量级.XML  存储文件名,数据保存在data/data/basepackage/shared_prefs/myopt.xml中   实例-收藏-记住密码自动登录 //一种轻量级的数据存储方式//通过KEY 存入数据--putxxxx(key,value) 取出数据--getxxxx(key  default)   2.读写SD卡  SD的根目录  适用于数据流读写 3.SQLite  轻量级.dp文件多用于手机

从零开始学android&lt;数据存储(1)SharedPreferences属性文件.三十五.&gt;

在android中有五种保存数据的方法,分别是: Shared Preferences Store private primitive data in key-value pairs. 对应属性的键值对属性文件存储 Internal Storage Store private data on the device memory. 设备内存存储 External Storage Store public data on the shared external storage. 外部存储器存储,如内

Android KeyStore Stack Buffer Overflow (CVE-2014-3100)

/* 本文章由 莫灰灰 编写,转载请注明出处. 作者:莫灰灰    邮箱: [email protected] */ 1. KeyStore Service 在Android中,/system/bin/keystore进程提供了一个安全存储的服务.在过去的版本中,其他程序主要用过UNIX socket的守护进程/dev/socket/keystore去访问这个服务.然而,现在我们可以通过Binder机制去访问它. 每一个Android用户都有一块其私有的安全存储区域.所有秘钥信息使用一个随机ke

Android SharedPreferences存储

一 概念 SharedPreferences存储方式是Android中存储轻量级数据的一种方式.SharedPreferences存储主要用来存储一些简单的配置信息,内部以Map方式进行存储,因此需要使用键值对提交和保存数据,保存的数据以xml格式存放在本地的/data/data/<package name>/shares_prefs文件夹下. 二 特点 1,        使用简单,便于存储轻量级的数据: 2,        只支持Java基本数据类型,不支持自定义数据类型: 3,