私のアプリはAndroid 6.0 Fingerprint APIを使用してAndroid KeyStoreのAESキーを保護します。保存されたキーは、ユーザーが指紋センサーによって認証されている場合にのみ使用できます。 KeyGenParameterSpec
はsetUserAuthenticationRequired(true)
で初期化されます。
ユーザーがセンサーに触れると、コールバックonAuthenticationSucceeded(Cipher)
から初期化されたCipherを取得し、それを復号化に使用します。
これは、Android 6.のSamsung電話を除いて完全に機能します。返されたCipherを使用しようとすると、Samsung電話が_Android.security.KeyStoreException: Key user not authenticated
_をスローすることがあります。そのため、onAuthenticationSucceeded(Cipher)
Android KeyStoreは、ユーザーが指紋センサーによって認証されなかったと考えています。
アプリが長時間使用されなかった場合、むしろクラッシュが発生するようです。アプリがウォームアップされると、通常はすべて正しく機能します。
このエラーはランダムに発生し、Samsungの電話でのみ発生するため、Samsungの実装内のAndroid 6.0 KeyStoreおよびFingerPrint APIの内部タイミングの問題が原因であるようです。
編集:この問題は、OnePlusおよびAcer電話でも発生しました。
上記のメーカーがすぐにこの問題を修正するとは思わないので、Samsung、OnePlus、Asus、およびその他の一部のデバイスにKeyGenParameterSpec.setUserAuthenticationRequired(false)
を設定することで解決しました。
KeyGenParameterSpec.setUserAuthenticationRequired(false)を設定すると、セキュリティ上の問題が発生する可能性があります。上記のエラーはKeyPermanentlyInvalidatedExceptionと同様に処理する必要があります。 SecretKeyの作成後に新しいフィンガープリントが追加されると、KeyPermanentlyInvalidatedExceptionがスローされますCipherの初期化。ただし、新しい指紋が追加される前にCipherが初期化されている場合、暗号化または復号化しようとすると、認証されていないキーユーザーに対して上記のKeyStoreExceptionが発生しますその暗号で。
このエラーは簡単に再現できます。アプリの指紋認証画面がバックグラウンドにある間に、新しい指紋を追加してみてください。次に、アプリに切り替えて指紋を入力すると、暗号化または復号化のメソッドでこのエラーがスローされます。この問題は、例外をキャッチしてKeyPermanentlyInvalidatedExceptionと同じように扱うことで解決できます。
「setUserAuthenticationRequired(false)」をリッスンしないでください
これをサムスンで2回聞くことで再現できました。私が起こっていると思うのは、2回聞いて、1つのコールで認証するが、別のコールでそれを参照することです。
ログを追加し、指紋のリッスンを1回だけ開始することを確認します。
私もこの問題を経験しました。私の場合は、誤ってFingerprintManager.authenticate()
を2回呼び出してtwo同時指紋認証を開始していたことが原因でした。 2番目の通話を削除すると、エラーは消えました。
Android 8.でSamsung Galaxy S8を使用しているときにもこの問題が発生しました。この問題を解決するには、キーストアからキーを削除して新しいデータを生成し、その後、データの暗号化と復号化に新しいキーを使用します
try {
keyStore.deleteEntry(KEY_ALIAS);
} catch (KeyStoreException e) {
e.printStackTrace();
}
UPDATE:これはAndroid 8.0 https://issuetracker.google.com/issues/6557876
Samsungでこのエラーが発生しましたが、新しい指紋を追加するとエラーが発生するようです。このコードでは、signature.initSign()の実行中にKeyPermenantlyInvalidatedExceptionがスローされることを想定しています。これは発生せず、初期化された署名はCryptoObject内でFingerprintManagerに正常に渡されます。次に、フィンガープリントが正常に検証され、onAuthenticationSucceededが呼び出されます。このエラーは、signature.update(byte [] bytes)を呼び出そうとしたときに発生します。
KeyInvalidatedExceptionが実際にスローされると私が信じていると予想される動作ですが、これが解決されることを期待できるかどうかはわかりません。私の解決策は、それをonAuthenticationSucceeded側でキャッチすることです。
@Override
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
Log.d(LOG_TAG, "Device Authentication Succeeded");
try {
Signature signature = result.getCryptoObject().getSignature();
String authData = getAuthData();
signature.update(authData.getBytes());
// do something with signature
} catch (SignatureException e) {
Log.d(LOG_TAG, e.getMessage());
if(e.getMessage() != null && e.getMessage().contains("Key user not authenticated")) {
// handle as if were KeyPermanentlyInvalidatedException
} else {
Log.d(LOG_TAG, e.getMessage());
// handle as regular error
}
}
}
RSAを使用しているときにもこの問題があり、データを暗号化したい場合はすぐに公開鍵のコピーを作成することで解決できました。
// create a copy of the public key -> workaround for Android.security.KeyStoreException: Key user not authenticated
val publicKey = KeyFactory
.getInstance("RSA")
.generatePublic(X509EncodedKeySpec(keyPair.public.encoded))
// encrypt with the public key
val cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding")
cipher.init(Cipher.ENCRYPT_MODE, publicKey)
val encryptedData = cipher.doFinal(data)
認証されたキーの有効期間を明示的に設定することで、Samsung Galaxy S8で動作します。
setUserAuthenticationValidityDurationSeconds(10);
ただし、これにより、ユーザー認証をさらに必要とせずに、その期間内にキーを複数回使用することが技術的に可能になります。
個人的にはそんなに大きなリスクだとは思いません。
これらの保護手段を使用して完了するのに数秒かかる大きなストリームの暗号化はテストしていません。暗号化タスクに有効期間が許可するよりも長い時間がかかるとどうなるのでしょうか。
これは、更新後に発生するAndroidのバグのようです。
私はそれをOnePlusで食べました。設定からデバイスロックを外して再設定しました。その後、問題はなくなりました。
Samsungのバグが原因でランダムに発生するかどうかは関係ありません。新しい指紋の追加または別の理由により、キーが無効になるたび。次のステップは、KeyStore.delete(keyAlias)を使用して無効化されたキーをキーストアから削除することです。また、暗号化キーを削除すると、復号化する方法がないため、古いデータをクリアします。次に、ユーザーに新しい資格情報を入力し、新しいキーを使用して暗号化するように依頼します。新しいキーは、古いキーと同じエイリアスを持つことができます。ここでフローのサンプルを見ることができます: AndroidでFingerprintManagerフローをラップするクラスのサンプル