Android Keystore?.getKey returns 在某些设备上为空
Android Keystore?.getKey returns null on some devices
我正在我的应用程序中实施指纹验证。我遇到了在某些设备上无法正常工作并且我的应用程序崩溃的问题。尽管如此,它也适用于某些设备。问题出在这一行
val key = keyStore?.getKey(Constants.FINGERPRINT_KEY_NAME, null) as SecretKey
LogCat 说:
11-02 14:27:42.825 E: FATAL EXCEPTION: main
Process: kyivenergo.ua.kyivenegro, PID: 2298
java.lang.RuntimeException: Unable to start activity ComponentInfo{kyivenergo.ua.kyivenegro/ua.dtec.appOk.ui.screens.splash.SplashScreenActivity}: kotlin.TypeCastException: null cannot be cast to non-null type javax.crypto.SecretKey
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2646)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2707)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1460)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
Caused by: kotlin.TypeCastException: null cannot be cast to non-null type javax.crypto.SecretKey
at ua.dtec.appOk.ui.dialog.FingerprintDialog.initCipher(FingerprintDialog.kt:222)
at ua.dtec.appOk.ui.dialog.FingerprintDialog.doCheck(FingerprintDialog.kt:147)
at ua.dtec.appOk.ui.dialog.FingerprintDialog.show(FingerprintDialog.kt:65)
at ua.dtec.appOk.ui.screens.splash.SplashScreenActivity.showFingerprintDialog(SplashScreenActivity.kt:55)
at ua.dtec.appOk.ui.screens.splash.SplashScreenActivity.onStart(SplashScreenActivity.kt:33)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1248)
at android.app.Activity.performStart(Activity.java:6679)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2609)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2707)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1460)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
这是我的完整代码:
private fun doCheck() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
keyguardManager =
c.getSystemService(KEYGUARD_SERVICE) as KeyguardManager
fingerprintManager =
c.getSystemService(FINGERPRINT_SERVICE) as FingerprintManager
if (!fingerprintManager!!.isHardwareDetected()) {
Toast.makeText(c, R.string.dialog_fingerprint_no_hardware_toast, Toast.LENGTH_SHORT).show()
}
if (ActivityCompat.checkSelfPermission(c, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(c, R.string.dialog_fingerprint_enable_fingerprint_permission, Toast.LENGTH_SHORT).show()
}
if (!fingerprintManager!!.hasEnrolledFingerprints()) {
Toast.makeText(c, R.string.dialog_fingerprint_no_fingerprints, Toast.LENGTH_SHORT).show()
}
if (!keyguardManager?.isKeyguardSecure()!!) {
Toast.makeText(c, R.string.dialog_fingerprint_no_lock_screen, Toast.LENGTH_SHORT).show()
} else {
generateKey()
}
if (initCipher()) {
cryptoObject = FingerprintManager.CryptoObject(cipher)
helper = FingerPrintHelper(dialog!!)
helper?.FingerprintHandler(c)
helper?.startAuth(fingerprintManager!!, cryptoObject!!)
}
}
}
private fun generateKey() {
try {
keyStore = KeyStore.getInstance("AndroidKeyStore")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
keyStore?.load(null)
keyGenerator!!.init(
KeyGenParameterSpec.Builder(Constants.FINGERPRINT_KEY_NAME,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setUserAuthenticationRequired(true)
.setEncryptionPaddings(
KeyProperties.ENCRYPTION_PADDING_PKCS7)
.build())
}
keyGenerator?.generateKey()
} catch (exc: KeyStoreException) {
exc.printStackTrace()
throw FingerprintException(exc)
} catch (exc: NoSuchAlgorithmException) {
exc.printStackTrace()
throw FingerprintException(exc)
} catch (exc: NoSuchProviderException) {
exc.printStackTrace()
throw FingerprintException(exc)
} catch (exc: InvalidAlgorithmParameterException) {
exc.printStackTrace()
throw FingerprintException(exc)
} catch (exc: CertificateException) {
exc.printStackTrace()
throw FingerprintException(exc)
} catch (exc: IOException) {
exc.printStackTrace()
throw FingerprintException(exc)
}
}
@RequiresApi(Build.VERSION_CODES.M)
fun initCipher(): Boolean {
try {
cipher = Cipher.getInstance(
KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_CBC + "/"
+ KeyProperties.ENCRYPTION_PADDING_PKCS7)
} catch (e: NoSuchAlgorithmException) {
throw RuntimeException("Failed to get Cipher", e)
} catch (e: NoSuchPaddingException) {
throw RuntimeException("Failed to get Cipher", e)
}
try {
keyStore?.load(
null)
val key = keyStore?.getKey(Constants.FINGERPRINT_KEY_NAME, 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)
}
}
构建版本应大于或等于 marshmallow。
如果您尝试少于 M 那么它可能会引发错误..
您将 null 转换为不可为 null 的 SecretKey
val key = keyStore?.getKey(Constants.FINGERPRINT_KEY_NAME, null) as SecretKey
当 keyStore
为空或 getKey()
returns 为空时(如果给定的别名不存在或未标识与键相关的条目),您可以获得空值。
所以在那里得到 null 是完全没问题的。因为你在向上转型,你甚至可能 运行 变成 ClassCastException
。为防止这种情况,只需使用类型安全的转换运算符 as?
即可。
val key: SecretKey? = keyStore?.getKey(Constants.FINGERPRINT_KEY_NAME, null) as? SecretKey
我正在我的应用程序中实施指纹验证。我遇到了在某些设备上无法正常工作并且我的应用程序崩溃的问题。尽管如此,它也适用于某些设备。问题出在这一行
val key = keyStore?.getKey(Constants.FINGERPRINT_KEY_NAME, null) as SecretKey
LogCat 说:
11-02 14:27:42.825 E: FATAL EXCEPTION: main
Process: kyivenergo.ua.kyivenegro, PID: 2298
java.lang.RuntimeException: Unable to start activity ComponentInfo{kyivenergo.ua.kyivenegro/ua.dtec.appOk.ui.screens.splash.SplashScreenActivity}: kotlin.TypeCastException: null cannot be cast to non-null type javax.crypto.SecretKey
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2646)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2707)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1460)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
Caused by: kotlin.TypeCastException: null cannot be cast to non-null type javax.crypto.SecretKey
at ua.dtec.appOk.ui.dialog.FingerprintDialog.initCipher(FingerprintDialog.kt:222)
at ua.dtec.appOk.ui.dialog.FingerprintDialog.doCheck(FingerprintDialog.kt:147)
at ua.dtec.appOk.ui.dialog.FingerprintDialog.show(FingerprintDialog.kt:65)
at ua.dtec.appOk.ui.screens.splash.SplashScreenActivity.showFingerprintDialog(SplashScreenActivity.kt:55)
at ua.dtec.appOk.ui.screens.splash.SplashScreenActivity.onStart(SplashScreenActivity.kt:33)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1248)
at android.app.Activity.performStart(Activity.java:6679)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2609)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2707)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1460)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
这是我的完整代码:
private fun doCheck() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
keyguardManager =
c.getSystemService(KEYGUARD_SERVICE) as KeyguardManager
fingerprintManager =
c.getSystemService(FINGERPRINT_SERVICE) as FingerprintManager
if (!fingerprintManager!!.isHardwareDetected()) {
Toast.makeText(c, R.string.dialog_fingerprint_no_hardware_toast, Toast.LENGTH_SHORT).show()
}
if (ActivityCompat.checkSelfPermission(c, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(c, R.string.dialog_fingerprint_enable_fingerprint_permission, Toast.LENGTH_SHORT).show()
}
if (!fingerprintManager!!.hasEnrolledFingerprints()) {
Toast.makeText(c, R.string.dialog_fingerprint_no_fingerprints, Toast.LENGTH_SHORT).show()
}
if (!keyguardManager?.isKeyguardSecure()!!) {
Toast.makeText(c, R.string.dialog_fingerprint_no_lock_screen, Toast.LENGTH_SHORT).show()
} else {
generateKey()
}
if (initCipher()) {
cryptoObject = FingerprintManager.CryptoObject(cipher)
helper = FingerPrintHelper(dialog!!)
helper?.FingerprintHandler(c)
helper?.startAuth(fingerprintManager!!, cryptoObject!!)
}
}
}
private fun generateKey() {
try {
keyStore = KeyStore.getInstance("AndroidKeyStore")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
keyStore?.load(null)
keyGenerator!!.init(
KeyGenParameterSpec.Builder(Constants.FINGERPRINT_KEY_NAME,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setUserAuthenticationRequired(true)
.setEncryptionPaddings(
KeyProperties.ENCRYPTION_PADDING_PKCS7)
.build())
}
keyGenerator?.generateKey()
} catch (exc: KeyStoreException) {
exc.printStackTrace()
throw FingerprintException(exc)
} catch (exc: NoSuchAlgorithmException) {
exc.printStackTrace()
throw FingerprintException(exc)
} catch (exc: NoSuchProviderException) {
exc.printStackTrace()
throw FingerprintException(exc)
} catch (exc: InvalidAlgorithmParameterException) {
exc.printStackTrace()
throw FingerprintException(exc)
} catch (exc: CertificateException) {
exc.printStackTrace()
throw FingerprintException(exc)
} catch (exc: IOException) {
exc.printStackTrace()
throw FingerprintException(exc)
}
}
@RequiresApi(Build.VERSION_CODES.M)
fun initCipher(): Boolean {
try {
cipher = Cipher.getInstance(
KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_CBC + "/"
+ KeyProperties.ENCRYPTION_PADDING_PKCS7)
} catch (e: NoSuchAlgorithmException) {
throw RuntimeException("Failed to get Cipher", e)
} catch (e: NoSuchPaddingException) {
throw RuntimeException("Failed to get Cipher", e)
}
try {
keyStore?.load(
null)
val key = keyStore?.getKey(Constants.FINGERPRINT_KEY_NAME, 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)
}
}
构建版本应大于或等于 marshmallow。 如果您尝试少于 M 那么它可能会引发错误..
您将 null 转换为不可为 null 的 SecretKey
val key = keyStore?.getKey(Constants.FINGERPRINT_KEY_NAME, null) as SecretKey
当 keyStore
为空或 getKey()
returns 为空时(如果给定的别名不存在或未标识与键相关的条目),您可以获得空值。
所以在那里得到 null 是完全没问题的。因为你在向上转型,你甚至可能 运行 变成 ClassCastException
。为防止这种情况,只需使用类型安全的转换运算符 as?
即可。
val key: SecretKey? = keyStore?.getKey(Constants.FINGERPRINT_KEY_NAME, null) as? SecretKey