经过深入研究
EncryptedSharedPreferences
和
EncryptedFile
的实现,我成功创建了一个
CryptoHelper
类,它使用Jetpack Security中2个类相同的方法,提供了一种加密、解密、签名和验证
ByteArray
的方式。
import android.content.Context
import androidx.security.crypto.MasterKeys
import com.google.crypto.tink.Aead
import com.google.crypto.tink.DeterministicAead
import com.google.crypto.tink.KeyTemplate
import com.google.crypto.tink.KeyTemplates
import com.google.crypto.tink.PublicKeySign
import com.google.crypto.tink.PublicKeyVerify
import com.google.crypto.tink.aead.AeadConfig
import com.google.crypto.tink.daead.DeterministicAeadConfig
import com.google.crypto.tink.integration.android.AndroidKeysetManager
import com.google.crypto.tink.signature.SignatureConfig
import java.io.IOException
import java.security.GeneralSecurityException
@Suppress("unused")
class CryptoHelper(
private val aead: Aead,
private val deterministicAead: DeterministicAead,
private val signer: PublicKeySign,
private val verifier: PublicKeyVerify,
) {
class Builder(
private val context: Context,
) {
private var masterKeyAlias: String = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
private var keysetPrefName = KEYSET_PREF_NAME
private var keysetAlias = KEYSET_ALIAS
private var aeadKeyTemplate: KeyTemplate
private var deterministicAeadKeyTemplate: KeyTemplate
private var signKeyTemplate: KeyTemplate
init {
AeadConfig.register()
DeterministicAeadConfig.register()
SignatureConfig.register()
aeadKeyTemplate = KeyTemplates.get("AES256_GCM")
deterministicAeadKeyTemplate = KeyTemplates.get("AES256_SIV")
signKeyTemplate = KeyTemplates.get("ECDSA_P256")
}
fun setMasterKey(masterKey: String): Builder {
this.masterKeyAlias = masterKey
return this
}
fun setKeysetPrefName(keysetPrefName: String): Builder {
this.keysetPrefName = keysetPrefName
return this
}
fun setKeysetAlias(keysetAlias: String): Builder {
this.keysetAlias = keysetAlias
return this
}
fun setAeadKeyTemplate(keyTemplate: KeyTemplate): Builder {
this.aeadKeyTemplate = keyTemplate
return this
}
fun setDeterministicAeadKeyTemplate(keyTemplate: KeyTemplate): Builder {
this.deterministicAeadKeyTemplate = keyTemplate
return this
}
fun setSignKeyTemplate(keyTemplate: KeyTemplate): Builder {
this.signKeyTemplate = keyTemplate
return this
}
@Throws(GeneralSecurityException::class, IOException::class)
fun build(): CryptoHelper {
val aeadKeysetHandle = AndroidKeysetManager.Builder()
.withKeyTemplate(aeadKeyTemplate)
.withSharedPref(context, keysetAlias + "_aead__", keysetPrefName)
.withMasterKeyUri(KEYSTORE_PATH_URI + masterKeyAlias)
.build().keysetHandle
val deterministicAeadKeysetHandle = AndroidKeysetManager.Builder()
.withKeyTemplate(deterministicAeadKeyTemplate)
.withSharedPref(context, keysetAlias + "_daead__", keysetPrefName)
.withMasterKeyUri(KEYSTORE_PATH_URI + masterKeyAlias)
.build().keysetHandle
val signKeysetHandle = AndroidKeysetManager.Builder()
.withKeyTemplate(signKeyTemplate)
.withSharedPref(context, keysetAlias + "_sign__", keysetPrefName)
.withMasterKeyUri(KEYSTORE_PATH_URI + masterKeyAlias)
.build().keysetHandle
val aead = aeadKeysetHandle.getPrimitive(Aead::class.java)
val deterministicAead = deterministicAeadKeysetHandle.getPrimitive(DeterministicAead::class.java)
val signer = signKeysetHandle.getPrimitive(PublicKeySign::class.java)
val verifier = signKeysetHandle.publicKeysetHandle.getPrimitive(PublicKeyVerify::class.java)
return CryptoHelper(aead, deterministicAead, signer, verifier)
}
}
fun encrypt(plainText: ByteArray, associatedData: ByteArray = ByteArray(0)): ByteArray =
aead.encrypt(plainText, associatedData)
fun decrypt(ciphertext: ByteArray, associatedData: ByteArray = ByteArray(0)): ByteArray =
aead.decrypt(ciphertext, associatedData)
fun encryptDeterministically(plainText: ByteArray, associatedData: ByteArray = ByteArray(0)): ByteArray =
deterministicAead.encryptDeterministically(plainText, associatedData)
fun decryptDeterministically(ciphertext: ByteArray, associatedData: ByteArray = ByteArray(0)): ByteArray =
deterministicAead.decryptDeterministically(ciphertext, associatedData)
fun sign(data: ByteArray): ByteArray =
signer.sign(data)
fun verify(signature: ByteArray, data: ByteArray): Boolean =
try {
verifier.verify(signature, data)
true
} catch (e: GeneralSecurityException) {
false
}
companion object {
private const val KEYSTORE_PATH_URI = "android-keystore://"
private const val KEYSET_PREF_NAME = "__crypto_helper_pref__"
private const val KEYSET_ALIAS = "__crypto_helper_keyset"
}
}
别忘了将 com.google.crypto.tink:tink-android
添加到实现依赖项中,因为Jetpack安全性不会将其公开作为api。
AccountManager
似乎并不特别流行。你为什么觉得需要使用它呢?“为什么它不支持字符串加密”这样的问题不适合在Stack Overflow上提问。“为什么开发者X做出了决策Y?”这种问题通常只有开发者X才能回答,并且开发者X甚至可能不会看到这个问题。 - CommonsWareAccountManager
:https://developer.android.com/reference/android/accounts/AccountManager#setPassword(android.accounts.Account,%20java.lang.String)。 - Roberto Leinardi