В своем приложении для Android я сначала шифрую некоторый текст с помощью сгенерированного ключа, а затем пытаюсь расшифровать его.В большинстве случаев он работает правильно, но я получаю ошибку на одном устройстве (согласно Crashlytics) и не могу воспроизвести эту ошибку.
Это код моего класса для работы с хранилищем ключей и шифрованием / дешифрованиемdata:
public class KeysCryptoManager {
private static final String KEY_STORE = "AndroidKeyStore";
private static final String TRANSFORMATION = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";
private KeyStore androidKeyStore;
private KeyPairGenerator keyPairGenerator;
private Cipher cipher;
@Nullable
public String encode(String inputString, String keyAlias) {
try {
if (prepare(keyAlias) && initCipher(keyAlias, Cipher.ENCRYPT_MODE)) {
byte[] bytes = cipher.doFinal(inputString.getBytes());
return Converter.toBase64String(bytes);
}
} catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException e) {
Log.e("Error happened when try to encode", e);
}
return null;
}
@Nullable
public String decode(String encodedString, @NonNull Cipher cipher) {
try {
byte[] bytes = Converter.fromBase64(encodedString);
return new String(cipher.doFinal(bytes));
} catch (IllegalBlockSizeException | BadPaddingException e) {
Log.criticalError("Error happened when try to decode", e);
}
return null;
}
public void deleteKey(String keyAlias) {
if (initKeyStore()) {
try {
androidKeyStore.deleteEntry(keyAlias);
} catch (KeyStoreException e) {
Log.e("Error happened when try to delete key with alias = " + keyAlias, e);
}
}
}
@Nullable
public FingerprintManagerCompat.CryptoObject getCryptoObject(String keyAlias) throws InvalidKeyException {
if (prepare(keyAlias) && initCipher(keyAlias, Cipher.DECRYPT_MODE)) {
return new FingerprintManagerCompat.CryptoObject(cipher);
}
return null;
}
private boolean prepare(String keyAlias) {
return initKeyStore() && initCipher() && initKey(keyAlias);
}
private boolean initKeyStore() {
try {
androidKeyStore = KeyStore.getInstance(KEY_STORE);
androidKeyStore.load(null);
return true;
} catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException e) {
Log.e("Error happened when try to init KeyStore", e);
}
return false;
}
@TargetApi(Build.VERSION_CODES.M)
private boolean initKeyPairGenerator() {
try {
keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, KEY_STORE);
return true;
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
Log.e("Error happened when try to init key pair generator", e);
}
return false;
}
private boolean initCipher() {
try {
cipher = Cipher.getInstance(TRANSFORMATION);
return true;
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
Log.e("Error happened when try to init cipher", e);
}
return false;
}
private boolean initKey(String keyAlias) {
try {
return (androidKeyStore.containsAlias(keyAlias) && androidKeyStore.getCertificate(keyAlias) != null)
|| generateNewKey(keyAlias);
} catch (KeyStoreException e) {
Log.e("Error happened when try to init key for key alias = " + keyAlias, e);
}
return false;
}
@TargetApi(Build.VERSION_CODES.M)
private boolean generateNewKey(String keyAlias) {
if (initKeyPairGenerator()) {
try {
int keyPurposes = KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT;
KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keyAlias, keyPurposes)
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
.setUserAuthenticationValidityDurationSeconds(-1)
.setUserAuthenticationRequired(true);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
builder.setInvalidatedByBiometricEnrollment(true);
}
KeyGenParameterSpec keyGenParameterSpec = builder.build();
keyPairGenerator.initialize(keyGenParameterSpec);
keyPairGenerator.generateKeyPair();
return true;
} catch (InvalidAlgorithmParameterException e) {
Log.e("Error happened when try to generate new key for key alias = " + keyAlias, e);
}
}
return false;
}
@TargetApi(Build.VERSION_CODES.M)
private boolean initCipher(String keyAlias, int mode) throws InvalidKeyException {
try {
androidKeyStore.load(null);
switch (mode) {
case Cipher.ENCRYPT_MODE:
initEncodeCipher(keyAlias);
break;
case Cipher.DECRYPT_MODE:
initDecodeCipher(keyAlias);
break;
default:
return false; //this cipher is only for encode/decode
}
return true;
} catch (InvalidKeyException exception) {
deleteKey(keyAlias);
throw exception;
} catch (KeyStoreException | CertificateException | UnrecoverableKeyException | IOException |
NoSuchAlgorithmException | InvalidKeySpecException |
InvalidAlgorithmParameterException e) {
Log.e("Error happened when try to init cipher in mode = " + mode, e);
}
return false;
}
private void initDecodeCipher(String keyAlias) throws KeyStoreException, NoSuchAlgorithmException,
UnrecoverableKeyException, InvalidKeyException, InvalidAlgorithmParameterException {
PrivateKey key = (PrivateKey) androidKeyStore.getKey(keyAlias, null);
OAEPParameterSpec spec = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1,
PSource.PSpecified.DEFAULT);
cipher.init(Cipher.DECRYPT_MODE, key, spec);
}
private void initEncodeCipher(String keyAlias) throws KeyStoreException, InvalidKeySpecException,
NoSuchAlgorithmException, InvalidKeyException, InvalidAlgorithmParameterException {
PublicKey key = androidKeyStore.getCertificate(keyAlias).getPublicKey();
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(key.getEncoded());
PublicKey unrestricted = KeyFactory.getInstance(key.getAlgorithm()).generatePublic(keySpec);
OAEPParameterSpec spec = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1,
PSource.PSpecified.DEFAULT);
cipher.init(Cipher.ENCRYPT_MODE, unrestricted, spec);
}
}
Вот как я шифрую данные:
private Single<String> encryptPassword(String password) {
return Single.create(e -> {
String encryptedPassword = keysCryptoManager.encode(password, LOGIN_CREDENTIALS_CRYPTO_KEY_ALIAS);
e.onSuccess(encryptedPassword);
});
}
Так я дешифрую данные (шифр взят из криптообъекта, открытого с помощью отпечатка пальца):
private Single<String> decryptPassword(String encryptedPassword, Cipher cipher) {
return Single.create(e -> {
String password = keysCryptoManager.decode(encryptedPassword, cipher);
e.onSuccess(password);
});
}
Я знаю об ошибке только на устройстве LeMobile LEX720 с Android 6.0.1 (с root-доступом), но я предположил, что это может произойти на некоторых других устройствах
Это частьвойти с устройства с ошибкой:
javax.crypto.IllegalBlockSizeException
at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:486)
at javax.crypto.Cipher.doFinal(Cipher.java:1502)
at com.unitybars.bone.personal.domain.managers.app.KeysCryptoManager.a(SourceFile:68)
at com.unitybars.bone.personal.domain.managers.data.biometric.BiometricAuthDataManager.a(SourceFile:160)
at com.unitybars.bone.personal.domain.managers.data.biometric.BiometricAuthDataManager.lambda$SrCwT75dv8uuPLtpQEnSoc5Jy10(SourceFile)
at com.unitybars.bone.personal.domain.managers.data.biometric.-$$Lambda$BiometricAuthDataManager$SrCwT75dv8uuPLtpQEnSoc5Jy10.subscribe(lambda)
at io.reactivex.internal.operators.single.SingleCreate.b(SourceFile:39)
at io.reactivex.Single.a(SourceFile:3394)
at io.reactivex.internal.operators.single.SingleFlatMap$SingleFlatMapCallback.a(SourceFile:84)
at io.reactivex.internal.operators.single.SingleCreate$Emitter.a(SourceFile:68)
at com.unitybars.bone.personal.domain.managers.data.biometric.BiometricAuthDataManager.b(SourceFile:120)
at com.unitybars.bone.personal.domain.managers.data.biometric.BiometricAuthDataManager.lambda$qMoTqiW2v-wVm0W3RhYcjSakYTU(SourceFile)
at com.unitybars.bone.personal.domain.managers.data.biometric.-$$Lambda$BiometricAuthDataManager$qMoTqiW2v-wVm0W3RhYcjSakYTU.subscribe(lambda)
at io.reactivex.internal.operators.single.SingleCreate.b(SourceFile:39)
at io.reactivex.Single.a(SourceFile:3394)
at io.reactivex.internal.operators.single.SingleFlatMap.b(SourceFile:36)
at io.reactivex.Single.a(SourceFile:3394)
at io.reactivex.internal.operators.single.SingleFlatMap.b(SourceFile:36)
at io.reactivex.Single.a(SourceFile:3394)
at io.reactivex.internal.operators.single.SingleFlatMapCompletable.b(SourceFile:44)
at io.reactivex.Completable.a(SourceFile:2171)
at io.reactivex.internal.operators.completable.CompletableSubscribeOn$SubscribeOnObserver.run(SourceFile:64)
at io.reactivex.Scheduler$DisposeTask.run(SourceFile:579)
at io.reactivex.internal.schedulers.ScheduledRunnable.run(SourceFile:66)
at io.reactivex.internal.schedulers.ScheduledRunnable.call(SourceFile:57)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:154)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:269)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
Caused by: android.security.KeyStoreException: Unknown error
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)
... 30 more
Кто-нибудь знает, почему произошла эта ошибка?Я прочитал все о подобной ошибке на этом сайте, но все решения уже используются в моем коде.