我试图将android Fingerprint实现到示例应用程序中。 使用过的密码不被认为有效 - 但我不知道为什么,因为基于Android文档,它应该得到支持。
密码构建于:
return Cipher.getInstance(KeyProperties.KEY_ALGORITHM_RSA + "/" +KeyProperties.BLOCK_MODE_ECB + "/" + KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1);
此密码在官方docs中列出。
稍后使用的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.
我还使用该密码初始化密钥生成器:
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();
我还将公钥添加到加密过程中,而公钥是以这种方式生成的:
private PublicKey getPublicKey() throws GeneralSecurityException {
PublicKey publicKey = keyStore.getCertificate(keyAlias).getPublicKey();
KeySpec spec = new X509EncodedKeySpec(publicKey.getEncoded());
return keyFactory.generatePublic(spec);
}
编辑:添加了私钥的一部分:
PrivateKey getPrivateKey() throws GeneralSecurityException {
return (PrivateKey) keyStore.getKey(keyAlias, null);
}
实际指纹处理如下:
Cipher cipher = createCipher();
cipher.init(Cipher.ENCRYPT_MODE, getPublicKey());
fingerprintManager.authenticate(new FingerprintManager.CryptoObject(cipher), cancellationSignal,
0, new FingerprintManager.AuthenticationCallback() {/* cutted */ }, null);
解密:
cipher = createCipher();
cipher.init(Cipher.DECRYPT_MODE, getPrivateKey());
fingerprintManager.authenticate(new FingerprintManager.CryptoObject(cipher), cancellationSignal, 0, new FingerprintManager.AuthenticationCallback() {}, null);
导致以下结果:
处理:com.example.android.fingerprintdialog,PID:16254 java.lang.IllegalArgumentException:没有AndroidKeyStore提供者支持的Crypto原语:javax.crypto.Cipher@2419dda,spi:com.android.org.conscrypt.OpenSSLCipherRSA$PKCS1@4a4d20b
完整的堆栈跟踪:
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)
答案 0 :(得分:1)
我现在也遇到了同样的问题,新的 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))
getPublicKey
方法和所有其他参数与作者列出的相同。
然后我发现我们做错了。
我在本主题中找到的所有示例都使用带有 AES 密钥的 Symetric Cryptographi。对于这种类型的密码学,密钥是唯一的,并且仅用于加密和解密,因此如果我们进行加密或解密,则需要使用生物特征认证来保护它。这就是为什么在所有示例中我们都会看到此代码用于加密提示:
biometricPrompt.authenticate(promptInfo, BiometricPrompt.CryptoObject(cipher))
但是对于 RSA(又名非对称)加密,事情是不同的。加密密钥和解密密钥是不同的。加密密钥是私钥,因此不需要以任何方式进行保护。只需要解密,私钥需要。
这就是我们在尝试打开生物识别身份验证提示以激活公钥时获得加密的原因,因为公钥不是秘密,所以公钥是随机的。
解决方法很简单。只需调用不带 authenticate
(CryptoObject
) 的 biometricPrompt.authenticate(promptInfo)
方法,稍后,当身份验证成功时,使用您的公钥进行加密。
希望这可以帮助其他人,因为我找不到与此主题相关的任何信息,并且经过数小时的思考才发现那里出了什么问题。
答案 1 :(得分:0)
我遇到了相同的例外,我在指定密码提供者和其他提供者时解决了该例外; 例如:
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);