Криптография - AzretMagometov/android-littlefinger GitHub Wiki

Строка кода

Шифрование производится с помощью метода

void encode(String textToEncode, String key, CryptoAlgorithm algorithm, Callback callback)

где textToEncode и key - это непосредственно текст, который нужно зашифровать и псевдоним ключа, под которым он будет храниться в KeyStore. Работа с KeyStore сокрыта. CryptoAlgorithm - это enum, с помощью которого определяется тип шифрования - асимметричный или симметричный - RSA и AES соответственно. С Callback мы уже работали. В случае, если все прошло хорошо (State.SUCCESS) , то в AuthResult.getData() можно будет получить зашифрованную строку.

Расшифровка производится с помощью метода

void decode(String encodedText, String key, CryptoAlgorithm algorithm, Callback callback)

В него мы передаем зашифрованную строку, псевдоним ключа, которым мы шифровали, алгоритм, который использовался при шифровании и Callback. И снова, если все прошло хорошо (State.SUCCESS), то в AuthResult.getData() можно будет получить расшифрованную исходную строку. Однако, не стоит забывать про вероятность ошибки с устареванием ключа, например, когда пользователь удалил все свои отпечатки и добавил новые. Тогда расшифровать зашифрованный старым ключом текст не получится, так как ключ завязан на отпечатки. Для проверки этого случая, нужно воспользоваться методом AuthResult.isKeyInvalidated(), пример будет ниже.

Асимметричное шифрование (то есть шифровка и расшифровка производятся разными ключами) имеет одно важное преимущество перед симметричным - при шифровке пользователю не нужно подтверждать себя отпечатком. Что приводит к тому, что при наличии технической возможности зашифровать данные, это можно сделать, не отвлекая пользователя.

Пример работы

Шифрование

   if (mShouldEncode && mLittleFinger.isReadyToUse()) {
       String key = mRepository.getKeyAlias();
       mLittleFinger.encode(pin, key, CryptoAlgorithm.RSA, this::handleResult);
   }

При асимметричном способе шифрования, пользователю не нужно прикладывать палец. Callback вызовется сразу же.

Обработка результата

    private void handleResult(AuthResult authResult) {
        switch (authResult.getState()) {
            case SUCCESS: {
                String encoded = authResult.getData();
                mRepository.saveEncodedPin(encoded);
                getViewState().showEncodedPin(encoded);
                break;
            }
            default:
                if (authResult.getThrowable() != null) {
                    getViewState().showEncodedPin(authResult.getThrowable().getMessage());
                }
                break;
        }
    }

Расшифровка происходит следующим образом

        if (mLittleFinger.isReadyToUse() && mRepository.isPinEncoded()) {
            getViewState().setSensorStateMessage(R.string.fp_or_use_sensor);
            String encoded = mRepository.getEncodedPin();
            String key = mRepository.getKeyAlias();
            mLittleFinger.decode(encoded, key, CryptoAlgorithm.RSA, this::handleCallback);
        }

Теперь пользователю нужно приложить палец, чтобы сработал Callback.

    private void handleCallback(AuthResult result) {
        switch (result.getState()) {
            case SUCCESS:
                checkIsPinCorrect(result.getData());
                break;
            case HELP:
                getViewState().showMessage(R.string.fp_touch_help);
                break;
            case ERROR:
                getViewState().showMessage(R.string.fp_touch_failed);
                break;
            case EXCEPTION:
                if (result.isKeyInvalidated()) {
                    getViewState().setSensorStateMessage(R.string.fp_added_or_removed_fp);
                    mRepository.saveEncodedPin(null);
                } else {
                    getViewState().showMessage(R.string.fp_auth_by_fp_unavailable);
                }
                Log.d(TAG, "exc", result.getThrowable());
                break;
        }
    }

Симметричное шифрование/расшифровка

    void onEncodeClick(String enteredText) {
        if (TextUtils.isEmpty(enteredText)) return;

        if (mLittleFinger.isReadyToUse()) {
            getViewState().showMessage("Прикоснитесь к сенсору");
            mLittleFinger.encode(enteredText, "AES_KEY", CryptoAlgorithm.AES, this::handleResult);
        }
    }

    void onDecodeClick(String enteredText) {
        if (TextUtils.isEmpty(enteredText)) return;

        if (mLittleFinger.isReadyToUse()) {
            getViewState().showMessage("Прикоснитесь к сенсору");
            mLittleFinger.decode(enteredText, "AES_KEY", CryptoAlgorithm.AES, this::handleResult);
        }
    }

    private void handleResult(AuthResult result) {
        if (result.getState().equals(State.SUCCESS)) {
            getViewState().setProcessedText(result.getData());
        }
        if (result.isKeyInvalidated()) {
            getViewState().showMessage("Был добавлен или удален отпечаток");
        }
        if (result.getThrowable() != null) {
            getViewState().showMessage(result.getThrowable().getMessage());
        }
    }

Тут все очевидно. Пользователю каждый раз придется прикладывать палец к сенсору