Android 指纹 - 加密原语不受 AndroidKeyStore 提供程序支持

Chr*_*chi 5 android android-keystore android-security android-fingerprint-api

我正在尝试将 android 指纹实现到示例应用程序中。使用的密码不被认为是有效的 - 但我不知道为什么,因为基于 android 文档,它应该被支持。

密码建立在:

return Cipher.getInstance(KeyProperties.KEY_ALGORITHM_RSA + "/" +KeyProperties.BLOCK_MODE_ECB + "/" + KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1);
Run Code Online (Sandbox Code Playgroud)

官方文档中列出了此密码。

稍后使用的 keyGenerator 和 keyFactory 生成如下。

            keyStore = KeyStore.getInstance("AndroidKeyStore");
            keyStore.load(null); // Ensure the key store can be loaded before continuing.

            keyGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
            keyFactory = KeyFactory.getInstance("RSA");

            createCipher(); // If this doesn't throw, the cipher we need is available.
Run Code Online (Sandbox Code Playgroud)

我还使用该密码初始化密钥生成器:

 keyGenerator.initialize(new KeyGenParameterSpec.Builder(keyAlias,
                    KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) //
                    .setBlockModes(KeyProperties.BLOCK_MODE_ECB) //
                    .setUserAuthenticationRequired(true) //
                    .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1) //
                    .build());

            keyGenerator.generateKeyPair();
Run Code Online (Sandbox Code Playgroud)

我还将公钥添加到加密过程中,而公钥是这样生成的:

private PublicKey getPublicKey() throws GeneralSecurityException {
    PublicKey publicKey = keyStore.getCertificate(keyAlias).getPublicKey();
    KeySpec spec = new X509EncodedKeySpec(publicKey.getEncoded());
    return keyFactory.generatePublic(spec);
}
Run Code Online (Sandbox Code Playgroud)

编辑:添加了私钥的一部分:

PrivateKey getPrivateKey() throws GeneralSecurityException {
    return (PrivateKey) keyStore.getKey(keyAlias, null);
}
Run Code Online (Sandbox Code Playgroud)

实际的指纹处理如下:

        Cipher cipher = createCipher();
        cipher.init(Cipher.ENCRYPT_MODE, getPublicKey());
        fingerprintManager.authenticate(new FingerprintManager.CryptoObject(cipher), cancellationSignal,
                0, new FingerprintManager.AuthenticationCallback() {/* cutted */ }, null);
Run Code Online (Sandbox Code Playgroud)

解密:

 cipher = createCipher();
 cipher.init(Cipher.DECRYPT_MODE, getPrivateKey());
 fingerprintManager.authenticate(new FingerprintManager.CryptoObject(cipher), cancellationSignal, 0, new FingerprintManager.AuthenticationCallback() {},  null);
Run Code Online (Sandbox Code Playgroud)

结果如下:

进程:com.example.android.fingerprintdialog,PID:16254 java.lang.IllegalArgumentException:AndroidKeyStore 提供程序不支持加密原语:javax.crypto.Cipher@2419dda,spi:com.android.org.conscrypt.OpenSSLCipherRSA$PKCS1@4a4d20

完整的堆栈跟踪:

04-21 11:48:00.031 16254-16254/com.example.android.fingerprintdialog E/AndroidRuntime: FATAL EXCEPTION: main
                                                                                       Process: com.example.android.fingerprintdialog, PID: 16254
                                                                                       java.lang.IllegalArgumentException: Crypto primitive not backed by AndroidKeyStore provider: javax.crypto.Cipher@2419dda, spi: com.android.org.conscrypt.OpenSSLCipherRSA$PKCS1@4a4d20b
                                                                                           at android.security.keystore.AndroidKeyStoreProvider.getKeyStoreOperationHandle(AndroidKeyStoreProvider.java:160)
                                                                                           at android.hardware.fingerprint.FingerprintManager$CryptoObject.getOpId(FingerprintManager.java:248)
                                                                                           at android.hardware.fingerprint.FingerprintManager.authenticate(FingerprintManager.java:468)
                                                                                           at android.hardware.fingerprint.FingerprintManager.authenticate(FingerprintManager.java:429)
                                                                                           at com.example.android.fingerprintdialog.MainActivity.tryToEncrypt(MainActivity.java:212)
                                                                                           at com.example.android.fingerprintdialog.MainActivity.access$000(MainActivity.java:61)
Run Code Online (Sandbox Code Playgroud)

And*_*nik 6

我现在也遇到了同样的问题,使用新的androidx.biometric. 我在尝试执行加密生物识别身份验证时遇到了同样的错误,例如:

val cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_RSA + "/"
                + KeyProperties.BLOCK_MODE_ECB + "/"
                + KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
cipher.init(Cipher.ENCRYPT_MODE, getPublicKey(KeyFactory.getInstance(KeyProperties.KEY_ALGORITHM_RSA), keyStore))

biometricPrompt.authenticate(promptInfo, BiometricPrompt.CryptoObject(cipher))
Run Code Online (Sandbox Code Playgroud)

该getPublicKey方法和所有其他参数与作者列出的相同。

然后我意识到我们做错了。

我在本主题中找到的所有示例都使用带有 AES 密钥的对称加密。对于这种类型的密码学,密钥是唯一的且仅用于加密和解密,因此无论我们是否进行加密或解密,都需要通过生物识别身份验证来保护它。这就是为什么在所有示例中我们都会看到以下加密提示代码: biometricPrompt.authenticate(promptInfo, BiometricPrompt.CryptoObject(cipher))

但对于 RSA(又名非对称)加密,事情就不同了。加密密钥和解密密钥不同。加密密钥是私钥,因此不需要以任何方式保护。只有解密,需要私钥。

这就是为什么我们在尝试打开生物识别身份验证提示来激活公钥时获得加密,这是无稽之谈,因为公钥不是秘密。

解决方案非常简单。只需调用authenticate不带CryptoObject( biometricPrompt.authenticate(promptInfo)) 的方法,稍后,当身份验证成功时,使用您的公钥进行加密。

希望这可以对某人有所帮助,因为我找不到与该主题相关的任何信息,并且经过几个小时的思考后我才发现那里出了什么问题。


小智 1

我遇到了同样的异常,当我指定密码提供者和其他人时,我修复了它;例如:

String alg = "AES"; 
Cipher cipher = Cipher.getInstance(alg, "SunJCE");
KeyGenerator generator = KeyGenerator.getInstance(alg, "SunJCE");
SecretKey key = generator.generateKey();
cipher.init(Cipher.ENCRYPT_MODE, key);
Run Code Online (Sandbox Code Playgroud)