本文介紹 Jetpack-Security 庫(kù)的使用,安全地管理密鑰并對(duì)文件和 SharedPreferences 進(jìn)行加密。(注:僅支持 minSdkVersion 23+)
SharedPreferences 是一個(gè)用于存儲(chǔ)小量鍵值數(shù)據(jù)很好的工具,但當(dāng)存儲(chǔ)一些敏感數(shù)據(jù)時(shí),SharedPreferences 存儲(chǔ)的鍵值數(shù)據(jù)是明文的,對(duì)于敏感數(shù)據(jù),我們應(yīng)該進(jìn)行加密,所以我們一般有幾種做法:
- 使用 Android 密鑰庫(kù)自己寫(xiě)加密來(lái)包裝 SharedPreferences
- 使用第三方庫(kù),封裝的SharedPreferences
以上都不是很靠譜的做法,現(xiàn)在 Jetpack-Security 庫(kù)的出現(xiàn),讓 SharedPreferences 存儲(chǔ)加密更容易和方便,但僅支持 minSdkVersion 23+。
使用
在應(yīng)用或模塊的 build.gradle 文件中添加所需工件的依賴項(xiàng):
dependencies {
implementation "androidx.security:security-crypto:1.0.0"
}
在添加了依賴之后,我們要進(jìn)行在 Android KeyStore 創(chuàng)建一個(gè)加密 master key 和 store。
val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)
在這里我們指定了一個(gè)默認(rèn)的 key,AES256_GCM_SPEC,用來(lái)創(chuàng)建 master key。
最后我們需要?jiǎng)?chuàng)建 EncryptedSharedPreferences,它對(duì) SharedPreferences 進(jìn)行了包裝并且會(huì)為我們處理所有的加密。
const val FILE_NAME = "app_share"
EncryptedSharedPreferences.create(
FILE_NAME,
masterKeyAlias,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
在創(chuàng)建中我們制定了 SharedPreferences 的文件名,創(chuàng)建的 masterKeyAlias ,以及context。后面的兩個(gè)參數(shù)是 key 和 value 加密的 scheme,它們是庫(kù)提供的選項(xiàng)。
在創(chuàng)建了 EncryptedSharedPreferences 實(shí)例后,我們可以正常使用 SharedPreferences 讀取和存儲(chǔ),最終代碼如下:
val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)
const val FILE_NAME = "app_share"
val sp = EncryptedSharedPreferences.create(
FILE_NAME,
masterKeyAlias,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
// save a value
sp.edit().putString("sp_key","data").apply()
// read a value
sp.getString("sp_key","defalut")
校驗(yàn)數(shù)據(jù)
我們?nèi)z查下數(shù)據(jù)是否被加密了,一般 SharedPreferences 的文件數(shù)據(jù)在 /data/data/{packageName}/shared_prefs/{SharedPreferences文件名} 。
正常的 SharedPreferences,文件內(nèi)容如下:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="sp_key">data</string>
</map>
在使用了 EncryptedSharedPreferences 加密過(guò)后,文件內(nèi)容如下:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="__androidx_security_crypto_encrypted_prefs_key_keyset__">12a901ef1070f09bff3ce84e02def76f45b4a7b83f9700d97dba0bcd7d4555c777df36417605da8c00609c2e563f496b91f89a126e3ecf5623ea25f5d051bd44bbff3a7f5fd8654e6570b2e568b08e46f8fcefce2161ceebf9808425dbc30fa42035bd59ddf1de49482034bccf3c7198888b857389ee8b5f12104b3306c271fc85770cf3f5db70a5215213d07840adb86ee73ccc96ad7069ddf22cedc55674d94d719628ba5982c4aa8357381a4408b8c88a9403123c0a30747970652e676f6f676c65617069732e636f6d2f676f6f676c652e63727970746f2e74696e6b2e4165735369764b6579100118b8c88a94032001</string>
<string name="ATKCpDjubqZ3BEBOJqWfnwHRjbuLoAfVjuKK">ATvCdd9NsncgS7HDfM3baFgwRvAYOrdzAZwaXD1I0mRjI31WsbVvi5E=</string>
<string name="__androidx_security_crypto_encrypted_prefs_value_keyset__">12880176be20810bda48152c5a724c2e1653b60e9c66bfd7a4be40c9d3a1073e3efd2572a77373ed1b71c2fdaf586c1aeeb39eb230b906ffb2d69cecf820916b7a1c6e6f0c532274e045c924f674bb3437103fa914a0219c72c4ef23750398aef93dcfd0945d78d4ee8e8efbcab7a317234458836c32709516479179b4cf0401187d823f5caeeb487678521a4408dfeb89de03123c0a30747970652e676f6f676c65617069732e636f6d2f676f6f676c652e63727970746f2e74696e6b2e41657347636d4b6579100118dfeb89de032001</string>
</map>
我們可以看到 key 和 value 被加密了并且存儲(chǔ)了兩個(gè) keyset ,分別是 加密的 key 和 value 。
結(jié)語(yǔ)
Jetpack-Security 庫(kù)對(duì)于 SharedPreferences 的數(shù)據(jù)加密是很有用的工具,對(duì)于敏感數(shù)據(jù),這是一個(gè)很好的加密方法,但僅支持minSdkVersion 23+,雖然現(xiàn)在大多是高版本系統(tǒng)的設(shè)備,對(duì)于低端設(shè)備的兼容就很難受了,所以還是可以嘗試去用。