提问人:muetzenflo 提问时间:8/22/2016 最后编辑:muetzenflo 更新时间:6/18/2018 访问量:3376
FingerprintManager.authenticate() 期间的 UserNotAuthenticatedException
UserNotAuthenticatedException during FingerprintManager.authenticate()
问:
我有一个加密的密码存储在 Android KeyStore 中。
我想通过使用指纹 API 对用户进行身份验证来解密该密码。
据我了解,我必须调用该方法才能开始监听指纹结果。CryptoObject 参数的创建方式如下:FingerprintManager.authenticate(CryptoObject cryptoObject)
public static Cipher getDecryptionCipher(Context context) throws KeyStoreException {
try {
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
SecretKey secretKey = getKeyFromKeyStore();
final IvParameterSpec ivParameterSpec = getIvParameterSpec(context);
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
return cipher;
} catch (NoSuchAlgorithmException | NoSuchPaddingException | IOException | UnrecoverableKeyException | CertificateException | InvalidAlgorithmParameterException | InvalidKeyException e) {
e.printStackTrace();
}
return null;
}
Cipher cipher = FingerprintCryptoHelper.getDecryptionCipher(getContext());
FingerprintManager.CryptoObject cryptoObject = new FingerprintManager.CryptoObject(cipher);
fingerprintManager.authenticate(cryptoObject, ...);
该方法在调用之前正常工作。在此调用中,我得到一个 ,因为用户未对此 secretKey 进行身份验证。这在某种程度上是有道理的。但这难道不是一个无法实现的循环吗:getDecryptionCipher()
cipher.init()
UserNotAuthenticatedException
- 要对用户进行身份验证,我想使用他/她的指纹
- 为了监听他/她的指纹,我需要初始化密码,而密码又需要一个经过身份验证的用户
这里怎么了??
编辑:
我使用模拟器(Nexus 4,API 23)。
下面是我用来创建密钥的代码。
private SecretKey createKey() {
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE);
keyGenerator.init(new KeyGenParameterSpec.Builder(
KEY_NAME,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT
)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setUserAuthenticationRequired(true)
.setUserAuthenticationValidityDurationSeconds(AUTHENTICATION_DURATION_SECONDS)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.build());
return keyGenerator.generateKey();
} catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidAlgorithmParameterException e) {
throw new RuntimeException("Failed to create a symmetric key", e);
}
}
答:
事实证明,该问题与一个已知问题有关,该问题阻止了未经身份验证使用公钥(这正是公钥不需要的)。KeyGenParameterSpec
相关的问题/答案可以在这里找到: Android 指纹 API 加密和解密
解决方法是从最初创建的密钥创建一个,并使用此不受限制的 PublicKey 初始化密码。
因此,我的最终密码使用 AES/CBC/PKCS7Padding 并通过此方法初始化:PublicKey
public boolean initCipher(int opMode) {
try {
Key key = mKeyStore.getKey(KEY_NAME, null);
if (opMode == Cipher.ENCRYPT_MODE) {
final byte[] encoded = key.getEncoded();
final String algorithm = key.getAlgorithm();
final X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encoded);
PublicKey unrestricted = KeyFactory.getInstance(algorithm).generatePublic(keySpec);
mCipher.init(opMode, unrestricted);
} else {
final IvParameterSpec ivParameterSpec = getIvParameterSpec();
mCipher.init(opMode, key, ivParameterSpec);
}
return true;
} catch (KeyPermanentlyInvalidatedException exception) {
return false;
} catch ( NoSuchAlgorithmException | InvalidKeyException
| InvalidKeySpecException | InvalidAlgorithmParameterException | UnrecoverableKeyException | KeyStoreException exception) {
throw new RuntimeException("Failed to initialize Cipher or Key: ", exception);
}
}
@NonNull
public IvParameterSpec getIvParameterSpec() {
// the IV is stored in the Preferences after encoding.
String base64EncryptionIv = PreferenceHelper.getEncryptionIv(mContext);
byte[] encryptionIv = Base64.decode(base64EncryptionIv, Base64.DEFAULT);
return new IvParameterSpec(encryptionIv);
}
评论
getEncoded
null
SecretKey
PrivateKey
KeyFactory.getInstance
我找到了逃脱第22条军规的方法!
你这样做是这样的:
你像往常一样try{
.init
Cipher
如果没有(因为用户在密钥的有效期内进行了身份验证,即因为他在几秒钟前解锁了他的设备),则执行您的加密/解密例程。结束!
UserNotAuthenticatedException
你抓住了 - 运行工作流 (yes!) ,然后在回调中再次初始化你的密码 (yes!),但这次它不会抛出并使用这个初始化的实例来加密/解密(回调返回,就像我们用 CryptoObject 调用它一样,所以我们不能使用它)。结束!
UserNotAuthenticatedException
FingerprintManager.authenticate
null
CryptoObject
onAuthenticationSucceeded
UserNotAuthenticatedException
null
null
就这么简单......
但是我花了两天时间才通过跟踪和错误找到这种方法。更不用说 - 似乎网上存在的所有身份验证示例都是错误的!
评论