Android JetPack Security简介

是什么

Jetpack Security 是 Google I/O 2019 发布的安全组件库。Security构成简单,主要包含EncryptedFileEncryptedSharedPreferences两个类,分别用来对FileSharedPreferences的读写进行加密解密处理。Security要求min SDK version 23。

EncryptedFile 封装了Google的加密库tink的逻辑,提供FileInputStreamFileOutputStream,可以更安全的进行流的读写。
EncryptedSharedPreferencesSharedPreferences包装类,通过两种方式自动加密键/值:
Key加密使用的是确定性的加密算法,使得秘钥可以被加密
Value加密使用AES-256 GCM加密,不确定加密

Jetpack Security (JetSec)加密库为 Files 和 SharedPreferences 对象的加密操作提供了抽象支持。该库使用了安全且运用广泛的[密码学原语 cryptographic primitives),强化了AndroidKeyStore的使用。使用 EncryptedFile 和 EncryptedSharedPreferences 可以让您在本地保护可能包含敏感数据、API 密钥、OAuth 令牌和其他类型机密信息的文件。

从 5.0 开始,Android 会默认对用户数据分区的内容进行加密,那您为什么还需要加密应用中的数据呢?这是因为在某些场合中,您可能需要额外的保护。如果您的应用使用共享存储(shared storage),则应该对数据进行加密。如果您的应用处理敏感信息,包括但不限于个人身份可识别信息 (Personally Identifiable Information, PII)、健康记录、财务信息或企业数据,那么您的应用应该对其主目录中的数据进行加密。如果可能,我们建议您将此类信息与生物验证操作绑定,以提供额外的保护。

Jetpack Security 基于Tink,而 Tink 是 Google 的一个开源并支持跨平台的安全项目。如果您需要常规加密、混合加密或类似的安全措施,那么 Tink 可能适用于您的项目。Jetpack Security 的数据结构与 Tink 完全兼容。

2018年9月3日,Google宣布了一个新的加密库叫Tink,称 Tink 旨在提供安全、易于使用和难以误用的加密 API,它是基于现有的加密库 BoringSSL( 是谷歌创建的 OpenSSL 分支) 和 Java Cryptography Architecture(Java加密体系结构),加入了Project Wycheproof(它包含一系列安全测试,用来检测加密库(cryptographic libraries)软件是否存在各种已知漏洞。这些软件负责对存储于设备、或互联网上传输的数据进行加密。)项目发现的弱点反制措施。

当前稳定版 1.0.0

谷歌官方文档推荐的 应用安全性最佳做法

如果您存储的数据特别敏感或私密,请考虑使用 Security 库中提供的 EncryptedFile 对象,而不要使用 File 对象。

使用

声明依赖项

如需添加 Security 的依赖项,您必须将 Google Maven 代码库添加到项目中。如需了解详情,请参阅 Google 的 Maven 代码库

在应用或模块的 build.gradle 文件中添加所需工件的依赖项:

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"
}

秘钥管理

Security库秘钥管理分为两个部分:

秘钥集合(Key set)
包含一个或多个秘钥来加密文件或SharedPreferences数据,存储在SharedPreferences中。
主密钥(Master Key)
用来加密所有秘钥集合,存储在Android Keystore系统中
使用Android Keystore的包装类MasterKeys只用两行就可以制作Master Key。

val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
val mainKeyAlias= MasterKeys.getOrCreate(keyGenParameterSpec)

EncryptedSharedPreferences

普通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())

key和value都被明文保存在xml中 SPNormal

在这里插入图片描述

在这里插入图片描述

使用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())

key和value都被明文保存在xml中 SPEncrypted

在这里插入图片描述

性能对比

性能上有10倍以上的劣化

EncryptedFile

Write File

例如向text文件中中写入 "MY SUPER SECRET INFORMATION"字符串

        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

通过EncryptedFile可以输出明文”MY SUPER SECRET INFORMATION“;
仅使用BufferedReader则会输出不可读的密文

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.
}

在这里插入图片描述

完整工具类代码

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, "")
}

猜你喜欢

转载自blog.csdn.net/zhangshiwen11/article/details/121576012