我有CipherUtils类,它负责创建从on running提取的密码
import android.annotation.TargetApi
import android.app.Application
import android.app.KeyguardManager
import android.content.SharedPreferences
import android.hardware.fingerprint.FingerprintManager
import android.os.Build
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyPermanentlyInvalidatedException
import android.security.keystore.KeyProperties
import androidx.core.content.ContextCompat.getSystemService
import androidx.core.content.edit
import java.io.IOException
import java.security.*
import java.security.cert.CertificateException
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import javax.crypto.NoSuchPaddingException
import javax.crypto.SecretKey
import javax.inject.Inject
class CipherUtils @Inject constructor(
val application: Application,
val sharedPreferences: SharedPreferences
) {
internal val DEFAULT_KEY_NAME = "ubit_key"
private val KEY = "fingerInvalid"
private var mKeyStore: KeyStore? = null
private var mKeyGenerator: KeyGenerator? = null
private var defaultCipher: Cipher
init {
try {
mKeyStore = KeyStore.getInstance("AndroidKeyStore")
} catch (e: KeyStoreException) {
throw RuntimeException("Failed to get an instance of KeyStore", e)
}
try {
mKeyGenerator = KeyGenerator
.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
} catch (e: NoSuchAlgorithmException) {
throw RuntimeException("Failed to get an instance of KeyGenerator", e)
} catch (e: NoSuchProviderException) {
throw RuntimeException("Failed to get an instance of KeyGenerator", e)
}
try {
defaultCipher = Cipher.getInstance(
KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_CBC + "/"
+ KeyProperties.ENCRYPTION_PADDING_PKCS7
)
} catch (e: NoSuchAlgorithmException) {
throw RuntimeException("Failed to get an instance of Cipher", e)
} catch (e: NoSuchPaddingException) {
throw RuntimeException("Failed to get an instance of Cipher", e)
}
init()
}
@TargetApi(Build.VERSION_CODES.M)
private fun init() {
val keyguardManager = getSystemService(application,KeyguardManager::class.java)
val fingerprintManager = getSystemService(application,FingerprintManager::class.java)
if (!keyguardManager!!.isKeyguardSecure()) {
// Show a message that the user hasn't set up a fingerprint or lock screen.
return
}
// Now the protection level of USE_FINGERPRINT permission is normal instead of dangerous.
// See http://developer.android.com/reference/android/Manifest.permission.html#USE_FINGERPRINT
// The line below prevents the false positive inspection from Android Studio
// noinspection ResourceType
if (!fingerprintManager!!.hasEnrolledFingerprints()) {
// This happens when no fingerprints are registered.
return
}
createKey(DEFAULT_KEY_NAME, true)
}
/**
* Creates a symmetric key in the Android Key Store which can only be used after the user has
* authenticated with fingerprint.
*
* @param keyName the name of the key to be created
* @param invalidatedByBiometricEnrollment if `false` is passed, the created key will not
* be invalidated even if a new fingerprint is enrolled.
* The default value is `true`, so passing
* `true` doesn't change the behavior
* (the key will be invalidated if a new fingerprint is
* enrolled.). Note that this parameter is only valid if
* the app works on Android N developer preview.
*/
@TargetApi(Build.VERSION_CODES.M)
fun createKey(keyName: String, invalidatedByBiometricEnrollment: Boolean) {
// The enrolling flow for fingerprint. This is where you ask the user to set up fingerprint
// for your flow. Use of keys is necessary if you need to know if the set of
// enrolled fingerprints has changed.
try {
mKeyStore?.load(null)
// Set the alias of the entry in Android KeyStore where the key will appear
// and the constrains (purposes) in the constructor of the Builder
val builder = KeyGenParameterSpec.Builder(
keyName,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
// Require the user to authenticate with a fingerprint to authorize every use
// of the key
.setUserAuthenticationRequired(true)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
// This is a workaround to avoid crashes on devices whose API level is < 24
// because KeyGenParameterSpec.Builder#setInvalidatedByBiometricEnrollment is only
// visible on API level +24.
// Ideally there should be a compat library for KeyGenParameterSpec.Builder but
// which isn't available yet.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
builder.setInvalidatedByBiometricEnrollment(invalidatedByBiometricEnrollment)
}
mKeyGenerator?.init(builder.build())
mKeyGenerator?.generateKey()
} catch (e: NoSuchAlgorithmException) {
throw RuntimeException(e)
} catch (e: InvalidAlgorithmParameterException) {
throw RuntimeException(e)
} catch (e: CertificateException) {
throw RuntimeException(e)
} catch (e: IOException) {
throw RuntimeException(e)
}
}
/**
* Initialize the [Cipher] instance with the created key in the
* [.createKey] method.
*
* @param keyName the key name to init the cipher
* @return `true` if initialization is successful, `false` if the lock screen has
* been disabled or reset after the key was generated, or if a fingerprint got enrolled after
* the key was generated.
*/
@TargetApi(Build.VERSION_CODES.M)
private fun initCipher(cipher: Cipher, keyName: String): Boolean {
try {
mKeyStore?.load(null)
val key = mKeyStore?.getKey(keyName, null) as SecretKey
cipher.init(Cipher.ENCRYPT_MODE, key)
return true
} catch (e: KeyPermanentlyInvalidatedException) {
return false
} catch (e: KeyStoreException) {
throw RuntimeException("Failed to init Cipher", e)
} catch (e: CertificateException) {
throw RuntimeException("Failed to init Cipher", e)
} catch (e: UnrecoverableKeyException) {
throw RuntimeException("Failed to init Cipher", e)
} catch (e: IOException) {
throw RuntimeException("Failed to init Cipher", e)
} catch (e: NoSuchAlgorithmException) {
throw RuntimeException("Failed to init Cipher", e)
} catch (e: InvalidKeyException) {
throw RuntimeException("Failed to init Cipher", e)
}
}
}
当注册新指纹并运行initCipher
时,我希望得到KeyPermanentlyInvalidatedException
,但它返回true。我缺少什么以及如何解决此问题以了解何时添加新指纹?
答案 0 :(得分:2)
我进行了很多搜索,找到了答案in a comment in issues
此示例在onCreate中创建了一个新密钥,因此,如果在添加新指纹后启动示例应用程序,则不会引发KeyPermanentlyInvalidatedException,因为在创建密钥时,一组指纹包括新创建的指纹。 / p>
如果您想测试KeyPermanentlyInvalidatedException,请尝试在打开应用程序的同时添加新指纹。
但是我希望该功能在应用程序关闭时能够正常工作。因此,在创建新密钥之前添加了几行代码,我检查之前是否有密钥,如果存在则不创建。
/**
* returns current saved key
*/
private fun getCurrentKey(keyName: String): Key? {
keyStore?.load(null)
return keyStore?.getKey(keyName, null)
}
并将其替换为代码的创建关键部分
/**
* Only if the key is not created, Create a new key
*/
private fun createKeyIfNotExists() {
if (getCurrentKey(DEFAULT_KEY_NAME) == null) {
createKey(DEFAULT_KEY_NAME, true)
}
}