- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有 CipherUtils 类,它负责创建取自 the google dialog sample 的密码
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。我缺少什么以及如何解决此问题以了解何时添加了新指纹?
最佳答案
我搜索了很多,找到了答案 in a comment in issues
This sample creates a new key in onCreate, so if you launch the sample app after you add a new fingerprint, KeyPermanentlyInvalidatedException is not thrown because at the time the key is created, a set of fingerprints include the new created one.
If you want to test that KeyPermanentlyInvalidatedException, please try to add a new fingerprint while keeping the app open.
但我希望此功能在应用程序关闭时起作用。所以在创建新 key 之前添加了几行代码,我检查之前是否有 key ,如果存在则不创建。
/**
* returns current saved key
*/
private fun getCurrentKey(keyName: String): Key? {
keyStore?.load(null)
return keyStore?.getKey(keyName, null)
}
并将其替换为代码的创建 key 部分
/**
* 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)
}
}
关于android - 添加新指纹时不会抛出 KeyPermanentlyInvalidatedException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57944220/
尝试使用集成到 QTCreator 的表单编辑器,但即使我将插件放入 QtCreator.app/Contents/MacOS/designer 也不会显示。不过,相同的 dylib 文件确实适用于独
在此代码示例中。 “this.method2();”之后会读到什么?在返回returnedValue之前会跳转到method2()吗? public int method1(int returnedV
我的项目有通过gradle配置的依赖项。我想添加以下依赖项: compile group: 'org.restlet.jse', name: 'org.restlet.ext.apispark', v
我将把我们基于 Windows 的客户管理软件移植到基于 Web 的软件。我发现 polymer 可能是一种选择。 但是,对于我们的使用,我们找不到 polymer 组件具有表格 View 、下拉菜单
我的项目文件夹 Project 中有一个文件夹,比如 ED 文件夹,当我在 Eclipse 中指定在哪里查找我写入的文件时 File file = new File("ED/text.txt"); e
这是奇怪的事情,这个有效: $('#box').css({"backgroundPosition": "0px 250px"}); 但这不起作用,它只是不改变位置: $('#box').animate
这个问题在这里已经有了答案: Why does OR 0 round numbers in Javascript? (3 个答案) 关闭 5 年前。 Mozilla JavaScript Guide
这个问题在这里已经有了答案: Is the function strcmpi in the C standard libary of ISO? (3 个答案) 关闭 8 年前。 我有一个问题,为什么
我目前使用的是共享主机方案,我不确定它使用的是哪个版本的 MySQL,但它似乎不支持 DATETIMEOFFSET 类型。 是否存在支持 DATETIMEOFFSET 的 MySQL 版本?或者有计划
研究 Seam 3,我发现 Seam Solder 允许将 @Named 注释应用于包 - 在这种情况下,该包中的所有 bean 都将自动命名,就好像它们符合条件一样@Named 他们自己。我没有看到
我知道 .append 偶尔会增加数组的容量并形成数组的新副本,但 .removeLast 会逆转这种情况并减少容量通过复制到一个新的更小的数组来改变数组? 最佳答案 否(或者至少如果是,则它是一个错
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
noexcept 函数说明符是否旨在 boost 性能,因为生成的对象中可能没有记录异常的代码,因此应尽可能将其添加到函数声明和定义中?我首先想到了可调用对象的包装器,其中 noexcept 可能会产
我正在使用 Angularjs 1.3.7,刚刚发现 Promise.all 在成功响应后不会更新 angularjs View ,而 $q.all 会。由于 Promises 包含在 native
我最近发现了这段JavaScript代码: Math.random() * 0x1000000 10.12345 10.12345 >> 0 10 > 10.12345 >>> 0 10 我使用
我正在编写一个玩具(物理)矢量库,并且遇到了 GHC 坚持认为函数应该具有 Integer 的问题。是他们的类型。我希望向量乘以向量以及标量(仅使用 * ),虽然这可以通过仅使用 Vector 来实现
PHP 的 mail() 函数发送邮件正常,但 Swiftmailer 的 Swift_MailTransport 不起作用! 这有效: mail('user@example.com', 'test
我尝试通过 php 脚本转储我的数据,但没有命令行。所以我用 this script 创建了我的 .sql 文件然后我尝试使用我的脚本: $link = mysql_connect($host, $u
使用 python 2.6.4 中的 sqlite3 标准库,以下查询在 sqlite3 命令行上运行良好: select segmentid, node_t, start, number,title
我最近发现了这段JavaScript代码: Math.random() * 0x1000000 10.12345 10.12345 >> 0 10 > 10.12345 >>> 0 10 我使用
我是一名优秀的程序员,十分优秀!