在我们App开发过程中,可能会涉及到一些敏感和安全数据需要加密的情况,比如登录token的存储。我们往往会使用一些加密算法将这些敏感数据加密之后再保存起来,需要取出来的时候再进行解密。
此时就会有一个问题:用于加解密的Key该如何存储?
- 如果把Key和加密后的数据存到一起,那有一定的安全风险。
- 对Key再进行一次加密,这就陷入了死循环。
为了保证安全性,Android提供了KeyStore
系统来保存Key,本文就浅探一下KeyStore
及其使用方法。
一、什么是KeyStore?如何保证安全性?
1、什么是KeyStore?
先来看看官方对他的定义:
This class represents a storage facility for cryptographic keys and certificates.
这个类代表加密密钥和证书的存储设施
定义其实很简单,他就是用来保存加解密的Key和证书的。
2、如何保证安全性?
安全性保护措施在官方文档里有写(Android 密钥库系统 | Android 开发者 | Android Developers (google.cn)), 我就按自己的理解总结一下:
- Key是保存在手机系统里的,应用进程在获取Key的时候是通过系统进程获取到内存里的。也就是说只能在本应用进程里才能拿到Key,想要把Key提取出来是不可能的。
KeyStore
还可以指定密钥的授权使用方式(比如用户安全锁验证),可保证必须在授权的情况下才能获取到Key。
总的来说就是使用者只能在应用程序运行时使用KeyStore
里存放的Key,除非攻击者拿到源码打断点调试,否则无法知道Key到底是什么。这样就保证了存储Key的安全性。
二、KeyStore的使用
KeyStore
支持的加密算法有很多,其中对部分算法有API Level的要求,具体可以查看Android 密钥库系统 | Android 开发者 | Android Developers (google.cn)
本文就以AES加密算法为例,简单说明一下KeyStore
的使用方式。注意,本文涉及到的代码需要minSdk>=23.
先简单实现一下AES算法的加解密
1、数据加密
object AESUtil {
private const val IV_BLOCK_SIZE = 16
fun encryptAES(encryptBytes: ByteArray, encryptKey: SecretKey): ByteArray?{
try {
//创建密码器
val cipher = Cipher.getInstance("AES/CBC/PKCS7PADDING")
//用密钥初始化Cipher对象
cipher.init(Cipher.ENCRYPT_MODE, encryptKey)
val final = cipher.doFinal(encryptBytes)
// iv占前16位,加密后的数据占后面
return cipher.iv + final
} catch (e: NoSuchPaddingException) {
e.printStackTrace()
} catch (e: NoSuchAlgorithmException) {
e.printStackTrace()
} catch (e: InvalidAlgorithmParameterException) {
e.printStackTrace()
} catch (e: InvalidKeyException) {
e.printStackTrace()
} catch (e: BadPaddingException) {
e.printStackTrace()
} catch (e: IllegalBlockSizeException) {
e.printStackTrace()
}
return null
}
fun decryptAES(decryptBytes: ByteArray, decryptKey: SecretKey): ByteArray? {
try {
// 先取出IV
val iv = decryptBytes.copyOfRange(0, IV_BLOCK_SIZE)
// 取出加密后的数据
val decryptData = decryptBytes.copyOfRange(IV_BLOCK_SIZE, decryptBytes.size)
val cipher = Cipher.getInstance("AES/CBC/PKCS7PADDING")
cipher.init(Cipher.DECRYPT_MODE, decryptKey, IvParameterSpec(iv))
return cipher.doFinal(decryptData)
} catch (e: NoSuchPaddingException) {
e.printStackTrace()
} catch (e: NoSuchAlgorithmException) {
e.printStackTrace()
} catch (e: InvalidAlgorithmParameterException) {
e.printStackTrace()
} catch (e: InvalidKeyException) {
e.printStackTrace()
} catch (e: BadPaddingException) {
e.printStackTrace()
} catch (e: IllegalBlockSizeException) {
e