Introduction to Android JetPack Security

what is

Jetpack SecurityIt is a security component library released by Google I/O 2019. The Security structure is simple, mainly including EncryptedFileand EncryptedSharedPreferencestwo classes, which are used to encrypt and decrypt the reading and writing of Fileand respectively. SharedPreferencesSecurity requires min SDK version 23.

EncryptedFileEncapsulates tinkthe logic of Google's encryption library, provides FileInputStreamand FileOutputStream, and can read and write streams more securely.
EncryptedSharedPreferencesIt is SharedPreferencesa 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 Tinkaims 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.EncryptedFileFile

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.gradlefile :

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

insert image description here

insert image description here

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

insert image description here

performance comparison

More than 10 times degradation in performance

EncryptedFile

Write File

For example, textwrite "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.
}

insert image description here

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

Guess you like

Origin blog.csdn.net/zhangshiwen11/article/details/121576012
Recommended