what is
Jetpack Security
It is a security component library released by Google I/O 2019. The Security structure is simple, mainly includingEncryptedFile
andEncryptedSharedPreferences
two classes, which are used to encrypt and decrypt the reading and writing ofFile
and respectively.SharedPreferences
Security requires min SDK version 23.
EncryptedFile
Encapsulatestink
the logic of Google's encryption library, providesFileInputStream
andFileOutputStream
, and can read and write streams more securely.
EncryptedSharedPreferences
It isSharedPreferences
a wrapper class, which automatically encrypts keys/values in two ways:
Key encryption uses a deterministic encryption algorithm, so that the secret key can be encrypted
Value encryption uses AES-256 GCM encryption, uncertain encryption
The Jetpack Security (JetSec) encryption library provides abstract support for encryption operations on Files and SharedPreferences objects. The library uses secure and widely used [cryptographic primitives cryptographic primitives] to enhance the use of AndroidKeyStore. Using EncryptedFile and EncryptedSharedPreferences allows you to locally protect files that may contain sensitive data, API keys, OAuth tokens, and other types of confidential information.
Since 5.0, Android encrypts the contents of the user data partition by default, so why would you need to encrypt data in your app? This is because in some situations, you may need extra protection. If your app uses shared storage, data should be encrypted. If your app handles sensitive information, including but not limited to Personally Identifiable Information (PII), health records, financial information, or corporate data, your app should encrypt the data in its home directory. Where possible, we recommend that you bind such information to biometric authentication operations for additional protection.
Jetpack Security is based on Tink, an open source and cross-platform security project from Google. If you need general encryption, hybrid encryption, or similar security measures, then Tink might be suitable for your project. Jetpack Security's data structures are fully compatible with Tink.
On September 3, 2018, Google announced a new encryption library called Tink
, which Tink
aims to provide a secure, easy-to-use, and hard-to-misuse encryption API. It is based on the existing encryption library BoringSSL (a branch of OpenSSL created by Google) and Java Cryptography Architecture (Java Cryptography Architecture), joined Project Wycheproof (which includes a series of security tests to detect the presence of various known vulnerabilities in cryptographic libraries software. Data transmitted over the Internet is encrypted.) Countermeasures for weaknesses discovered by the project.
Current stable version 1.0.0
Best Practices for Application Security Recommended by Google Official Documentation
If the data you store is particularly sensitive or private, consider using the object provided in the Security library instead of the object.EncryptedFile
File
use
declare dependencies
To add a dependency on Security, you must add the Google Maven repository to your project. See Google's Maven repository for details .
Add dependencies for required artifacts in your app or module's build.gradle
file :
dependencies {
implementation "androidx.security:security-crypto:1.0.0"
// For Identity Credential APIs
implementation "androidx.security:security-identity-credential:1.0.0-alpha03"
// For App Authentication APIs
implementation "androidx.security:security-app-authenticator:1.0.0-alpha02"
// For App Authentication API testing
androidTestImplementation "androidx.security:security-app-authenticator:1.0.0-alpha01"
}
Key management
Security library key management is divided into two parts:
The key set (Key set)
contains one or more keys to encrypt files or SharedPreferences data, stored in SharedPreferences.
The master key (Master Key)
is used to encrypt all key collections and stored in the Android Keystore system.
Using the Android Keystore wrapper class MasterKeys, only two lines can be used to make a Master Key.
val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
val mainKeyAlias= MasterKeys.getOrCreate(keyGenParameterSpec)
EncryptedSharedPreferences
Regular SharedPreferences
val data = getSharedPreferences("SPNormal", Context.MODE_PRIVATE)
val editor = data.edit()
editor.putInt("IntSave", 10)
editor.apply()
val intSaved = data.getInt("IntSave", 1)
Log.e("IntSave", intSaved.toString())
Both key and value are stored in plain text in xml SPNormal
Use EncryptedSharedPreferences
val sharedPreferences = EncryptedSharedPreferences
.create(
"SPEncrypted",
mainKeyAlias,
this,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
val editor = sharedPreferences.edit()
editor.putInt("IntSave", 10)
editor.apply()
val intSaved = sharedPreferences.getInt("IntSave", 1)
Log.e("IntSave", intSaved.toString())
Both key and value are stored in plain text in xml SPEncrypted
performance comparison
More than 10 times degradation in performance
EncryptedFile
Write File
For example, text
write "MY SUPER SECRET INFORMATION"
a string
val fileToWrite = "my_other_sensitive_data.txt"
val encryptedFile = EncryptedFile.Builder(
File(filesDir, fileToWrite),
this,
mainKeyAlias,
EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()
// Write to a file.
try {
val outputStream: FileOutputStream? = encryptedFile.openFileOutput()
outputStream?.apply {
write("MY SUPER SECRET INFORMATION"
.toByteArray(Charset.forName("UTF-8")))
flush()
close()
}
} catch (ex: IOException) {
// Error occurred opening file for writing.
}
Read File
Through EncryptedFile, the plaintext "MY SUPER SECRET INFORMATION" can be output;
only using BufferedReader will output unreadable ciphertext
val fileToRead = "my_sensitive_data.txt"
lateinit var byteStream: ByteArrayOutputStream
val encryptedFile = EncryptedFile.Builder(
File(context.getFilesDir(), fileToRead),
context,
masterKeyAlias,
EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()
try {
encryptedFile.openFileInput().use {
fileInputStream ->
try {
val sb = StringBuilder()
val br = BufferedReader(InputStreamReader(fileInputStream) as Reader?)
br.readLine()
.forEach {
sb.append(it)
}
br.close()
// 输出 MY SUPER SECRET INFORMATION
Log.d("fileContents", sb.toString())
Toast.makeText(this, sb.toString(), Toast.LENGTH_SHORT).show()
} catch (ex: Exception) {
// Error occurred opening raw file for reading.
} finally {
fileInputStream.close()
}
}
} catch (ex: IOException) {
// Error occurred opening encrypted file for reading.
}
complete tool code
val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
val mainKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)
/**
* 加密文件
*/
fun writeFile(context: Context,fileToWrite:String){
val encryptedFile = EncryptedFile.Builder(
File(context.filesDir, fileToWrite),
context,
mainKeyAlias,
EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()
// Write to a file.
try {
val outputStream: FileOutputStream? = encryptedFile.openFileOutput()
outputStream?.apply {
write("MY SUPER SECRET INFORMATION"
.toByteArray(Charset.forName("UTF-8")))
flush()
close()
}
} catch (ex: IOException) {
// Error occurred opening file for writing.
}
}
/**
*解密文件
*/
fun readFile(context: Context,fileToRead:String){
lateinit var byteStream: ByteArrayOutputStream
val encryptedFile = EncryptedFile.Builder(
File(context.filesDir, fileToRead),
context,
mainKeyAlias,
EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()
try {
encryptedFile.openFileInput().use {
fileInputStream ->
try {
val sb = StringBuilder()
val br = BufferedReader(InputStreamReader(fileInputStream) as Reader?)
br.readLine()
.forEach {
sb.append(it)
}
br.close()
// 输出 MY SUPER SECRET INFORMATION
Log.e("fileContents", sb.toString())
// Toast.makeText(context, sb.toString(), Toast.LENGTH_SHORT).show()
} catch (ex: Exception) {
// Error occurred opening raw file for reading.
} finally {
fileInputStream.close()
}
}
} catch (ex: IOException) {
// Error occurred opening encrypted file for reading.
}
}
/**
* 存储字符串
*
* @param context
* @param key 存储的键
* @param value 存储的值
* @throws Exception
*/
fun saveSPEncrypted(context: Context, key: String, value: String) {
val sharedPreferences = EncryptedSharedPreferences
.create(
"SPEncrypted",
mainKeyAlias,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
val editor = sharedPreferences.edit()
editor.putString(key, value)
editor.apply()
val intSaved = sharedPreferences.getInt("IntSave", 1)
Log.e("IntSave", intSaved.toString())
}
/**
* 获取字符串
*
* @param context
* @param key 存储的键
* @throws Exception
*/
@Throws(Exception::class)
fun getSPEncrypted(context: Context, key: String): String? {
val securityPrefs = EncryptedSharedPreferences.create(
"SPEncrypted",
mainKeyAlias,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
return securityPrefs.getString(key, "")
}