Почему «KeyStoreException: неизвестная ошибка» возникает на некоторых устройствах - PullRequest
0 голосов
/ 20 ноября 2018

В своем приложении для 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

Кто-нибудь знает, почему произошла эта ошибка?Я прочитал все о подобной ошибке на этом сайте, но все решения уже используются в моем коде.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...