質問は長くなるかもしれませんが、詳しく説明します。
これは私のような問題があるデモです。
Android アプリがあり、ユーザーがパスワードを暗号化して SharedPreferences に保存し、SharedPreferences からパスワードを読み取って解読できる機能を追加したいと考えています。指紋が登録されている場合にのみ使用でき、これらのパスワードを取得するための検証方法として有効な指紋を使用できます。
保管時:
- ユーザー入力パスワード
- AndroidKeyStore によって生成された SecretKey によって暗号化モードの暗号を作成します
public Cipher getEncryptCipher() {
try {
Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_CBC + "/"
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);
mKeyStore.load(null);
SecretKey key = (SecretKey) mKeyStore.getKey(KEY_NAME, null);
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher;
} catch (Exception e) {
throw new RuntimeException("Failed to encrypt pin ", e);
}
}
- FingerprintManager は暗号 (Cipher A) を有効にし、実際の暗号化暗号 (Cipher B) を取得します。
//mCryptoObject is generated by cipher in step 2
mFingerprintManager.authenticate(mCryptoObject, mCancellationSignal, 0 /* flags */, FingerprintManager.AuthenticationCallback, null);
//in FingerprintManager.AuthenticationCallback
javax.crypto.Cipher cipher = result.getCryptoObject().getCipher();
- FingerprintManager からサポートされている暗号 (Cipher B) によるパスワードの暗号化
- 暗号化されたパスワードと暗号 iv を SharedPreferences に保存する
//In my app, we have many device, every could have one password. Pin is password.
public void encryptPin(String deviceId, String pin, javax.crypto.Cipher cipher) {
try {
if (cipher == null) return;
byte[] encryptedBytes = cipher.doFinal(pin.getBytes("utf-8"));
byte[] cipherIv = cipher.getIV();
String encodedPin = Base64.encodeToString(encryptedBytes, Base64.DEFAULT);
String encodeCipherIv = Base64.encodeToString(cipherIv, Base64.DEFAULT);
SharedPreferences.Editor editor = mSharedPreferences.edit();
editor.putString(createPinKey(deviceId), encodedPin);
editor.putString(createIvKey(deviceId), encodeCipherIv);
editor.apply();
} catch (IOException | IllegalBlockSizeException | BadPaddingException e) {
throw new RuntimeException("Failed to encrypt pin ", e);
}
}
読んだとき:
- 暗号 iv を読み取り、復号化モードの暗号 (Cipher C) を作成します。
public Cipher getDecryptCipher(String deviceId) {
try {
String encodedIv = mSharedPreferences.getString(createIvKey(deviceId), "");
byte[] cipherIv = Base64.decode(encodedIv, Base64.DEFAULT);
Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_CBC + "/"
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);
mKeyStore.load(null);
SecretKey key = (SecretKey) mKeyStore.getKey(KEY_NAME, null);
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(cipherIv));
return cipher;
} catch (Exception e) {
throw new RuntimeException("Failed to encrypt pin ", e);
}
}
- 有効なフィンガープリントを取得し、実際の復号化暗号 (Cipher D) を取得します
//mCryptoObject is generated by cipher in step 1
mFingerprintManager.authenticate(mCryptoObject, mCancellationSignal, 0 /* flags */, FingerprintManager.AuthenticationCallback, null);
//in FingerprintManager.AuthenticationCallback
javax.crypto.Cipher cipher = result.getCryptoObject().getCipher();
- 暗号化されたパスワードを読み取り、実際の復号化暗号 (Cipher D) で復号化します
public String decryptPin(String deviceId, javax.crypto.Cipher cipher) {
String encryptedPin = mSharedPreferences.getString(createPinKey(deviceId), "");
if (TextUtils.isEmpty(encryptedPin)) {
return "";
}
try {
if (cipher == null) return "";
byte[] decodedBytes = Base64.decode(encryptedPin, Base64.DEFAULT);
//BadPaddingException in this line
byte[] decryptBytes = cipher.doFinal(decodedBytes);
return new String(decryptBytes, Charset.forName("UTF8"));
} catch (Exception e) {
MyLog.d(TAG, "Failed to decrypt the data with the generated key." + e.getMessage());
e.printStackTrace();
return "";
}
}
私の初期化方法:
private void init() {
try {
mKeyStore = KeyStore.getInstance("AndroidKeyStore");
mKeyStore.load(null);
KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(KEY_NAME,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setUserAuthenticationRequired(true)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
builder.setInvalidatedByBiometricEnrollment(true);
}
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
keyGenerator.init(
builder.build());
keyGenerator.generateKey();
} catch (Exception e) {
throw new RuntimeException("Fail to init:" + e);
}
}
アプリを閉じなくてもOK!!!
ただし、プロセスを強制終了して再起動すると、cipher.doFinal() を実行すると、decryptPin() メソッドで BadPaddingException が発生しました。
System.err: javax.crypto.BadPaddingException
at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:482)
at javax.crypto.Cipher.doFinal(Cipher.java:1502)
at com.xiaomi.smarthome.framework.page.verify.DeviceVerifyConfigCache.decryptPin(DeviceVerifyConfigCache.java:156)
at com.xiaomi.smarthome.framework.page.verify.VerifyManager.decryptPin(VerifyManager.java:173)
at com.xiaomi.smarthome.framework.page.verify.FingerPrintVerifyActivity$1.onAuthenticated(FingerPrintVerifyActivity.java:62)
at com.xiaomi.smarthome.framework.page.verify.view.FingerPrintOpenVerifyDialog.onAuthenticationSucceeded(FingerPrintOpenVerifyDialog.java:136)
at android.hardware.fingerprint.FingerprintManager$MyHandler.sendAuthenticatedSucceeded(FingerprintManager.java:805)
at android.hardware.fingerprint.FingerprintManager$MyHandler.handleMessage(FingerprintManager.java:757)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5458)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:738)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:628)
Caused by: android.security.KeyStoreException: Invalid argument
at android.security.KeyStore.getKeyStoreException(KeyStore.java:632)
at android.security.keystore.KeyStoreCryptoOperationChunkedStreamer.doFinal(KeyStoreCryptoOperationChunkedStreamer.java:224)
at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:473)
... 13 more
この問題を解決するのを手伝ってくれる人はいますか? SecretKey が原因ですか?ありがとうございます!!!