Нужен ли объект CryptoObject или значение NULL для следующего варианта использования во время FingerprintManager.authenticate - PullRequest
0 голосов
/ 19 декабря 2018

Когда мы звоним

mFingerprintManager
            .authenticate(cryptoObject, 0 /* flags */, mCancellationSignal, this, null);

, я замечаю, что вполне нормально передать null для cryptoObject.Согласно документации FingerprintManager

FingerprintManager.CryptoObject: объект, связанный с вызовом, или ноль, если ничего не требуется.


Согласно https://github.com/googlesamples/android-FingerprintDialog, это показывает длинный шаг для создания CryptoObject.


Так что я не уверен, должен ли я использовать CryptoObject или null для моего варианта использования.Я прочитал Зачем нужен криптообъект для аутентификации по отпечатку пальца Android? , но все еще не могу полностью понять и принять решение для моего случая.

Мой пример использования следующий:

У меня есть экран блокировки запуска приложения для заметок.Обычно, когда пользователь включает экран блокировки при запуске, ему необходимо настроить рисунок шаблона.В случае, если он забудет свой рисунок рисунка, он может использовать свой отпечаток пальца в качестве альтернативы.Приложение выглядит следующим образом

enter image description here


Это исходный код.В настоящее время я использую CryptoObject.Однако, согласно отзывам моих пользователей, меньшинство из них сталкивается с проблемой приложения для этой новой функции.Хотя мы не видим отчетов о сбоях в консоли Google Play, мы подозреваем, что что-то пошло не так во время генерации CryptoObject.

Итак, если CryptoObject можно заменить на ноль, мы с радостью сделаем это, чтобы упростить наш код.

Нужен ли мне объект CryptoObject или нуль для следующего использованиядело во время FingerprintManager.authenticate


/**
 * Small helper class to manage text/icon around fingerprint authentication UI.
 */
public class FingerprintUiHelper extends FingerprintManagerCompat.AuthenticationCallback {
    private static final String TAG = "FingerprintUiHelper";

    private static final String ANDROID_KEY_STORE = "AndroidKeyStore";
    private static final String DEFAULT_KEY_NAME = "hello world key name";

    private int configShortAnimTime;

    private final FingerprintManagerCompat mFingerprintManager;
    private final ImageView mIcon;
    private final Callback mCallback;
    private CancellationSignal mCancellationSignal;

    private boolean mSelfCancelled;

    private final ResetErrorRunnable resetErrorRunnable = new ResetErrorRunnable();

    private final int mSuccessColor;
    private final int mAlertColor;

    private class ResetErrorRunnable implements Runnable {

        @Override
        public void run() {
            resetError();
        }
    }

    public static FingerprintUiHelper newInstance(ImageView icon, Callback callback, int successColor, int alertColor) {
        FingerprintManagerCompat fingerprintManagerCompat = FingerprintManagerCompat.from(WeNoteApplication.instance());
        return new FingerprintUiHelper(fingerprintManagerCompat, icon, callback, successColor, alertColor);
    }

    private void initResource() {
        configShortAnimTime = WeNoteApplication.instance().getResources().getInteger(android.R.integer.config_shortAnimTime);
    }

    /**
     * Constructor for {@link FingerprintUiHelper}.
     */
    private FingerprintUiHelper(FingerprintManagerCompat fingerprintManager,
                        ImageView icon, Callback callback, int successColor, int alertColor) {
        initResource();

        mFingerprintManager = fingerprintManager;
        mIcon = icon;
        mCallback = callback;
        mSuccessColor = successColor;
        mAlertColor = alertColor;
    }

    public boolean isFingerprintAuthAvailable() {
        // The line below prevents the false positive inspection from Android Studio
        // noinspection ResourceType
        return mFingerprintManager.isHardwareDetected()
                && mFingerprintManager.hasEnrolledFingerprints();
    }

    /**
     * Initialize the {@link Cipher} instance with the created key in the
     * {@link #createKey(String, boolean)} method.
     *
     * @param keyName the key name to init the cipher
     * @return {@code true} if initialization is successful, {@code false} if the lock screen has
     * been disabled or reset after the key was generated, or if a fingerprint got enrolled after
     * the key was generated.
     */
    private boolean initCipher(Cipher cipher, String keyName) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            return false;
        }

        KeyStore keyStore;
        KeyGenerator keyGenerator;

        try {
            keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
        } catch (KeyStoreException e) {
            Log.e(TAG, "", e);
            return false;
        }

        try {
            keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE);
        } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
            Log.e(TAG, "", e);
            return false;
        }

        // The enrolling flow for fingerprint. This is where you ask the user to set up fingerprint
        // for your flow. Use of keys is necessary if you need to know if the set of
        // enrolled fingerprints has changed.
        try {
            keyStore.load(null);
            // Set the alias of the entry in Android KeyStore where the key will appear
            // and the constrains (purposes) in the constructor of the Builder

            KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keyName,
                    KeyProperties.PURPOSE_ENCRYPT |
                            KeyProperties.PURPOSE_DECRYPT)
                    .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
                    // Require the user to authenticate with a fingerprint to authorize every use
                    // of the key
                    .setUserAuthenticationRequired(true)
                    .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7);

            // This is a workaround to avoid crashes on devices whose API level is < 24
            // because KeyGenParameterSpec.Builder#setInvalidatedByBiometricEnrollment is only
            // visible on API level +24.
            // Ideally there should be a compat library for KeyGenParameterSpec.Builder but
            // which isn't available yet.
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                builder.setInvalidatedByBiometricEnrollment(true);
            }
            keyGenerator.init(builder.build());
            keyGenerator.generateKey();
        } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException
                | CertificateException | IOException e) {
            Log.e(TAG, "", e);
            return false;
        }

        try {
            keyStore.load(null);
            SecretKey key = (SecretKey) keyStore.getKey(keyName, null);
            cipher.init(Cipher.ENCRYPT_MODE, key);
            return true;
        } catch (Exception e) {
            Log.e(TAG, "", e);
            return false;
        }
    }

    public void startListening() {
        if (!isFingerprintAuthAvailable()) {
            return;
        }

        Cipher defaultCipher;
        try {
            defaultCipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
                    + KeyProperties.BLOCK_MODE_CBC + "/"
                    + KeyProperties.ENCRYPTION_PADDING_PKCS7);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            Log.e(TAG, "", e);
            return;
        }

        if (false == initCipher(defaultCipher, DEFAULT_KEY_NAME)) {
            return;
        }

        FingerprintManagerCompat.CryptoObject cryptoObject = new FingerprintManagerCompat.CryptoObject(defaultCipher);

        startListening(cryptoObject);

        showIcon();
    }

    private void startListening(FingerprintManagerCompat.CryptoObject cryptoObject) {
        if (!isFingerprintAuthAvailable()) {
            return;
        }
        mCancellationSignal = new CancellationSignal();
        mSelfCancelled = false;

        // The line below prevents the false positive inspection from Android Studio
        // noinspection ResourceType
        mFingerprintManager
                .authenticate(cryptoObject, 0 /* flags */, mCancellationSignal, this, null);
    }

    public void stopListening() {
        if (mCancellationSignal != null) {
            mSelfCancelled = true;
            mCancellationSignal.cancel();
            mCancellationSignal = null;
        }
    }

    @Override
    public void onAuthenticationError(int errMsgId, CharSequence errString) {
        if (!mSelfCancelled) {
            if (errMsgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT) {
                mIcon.removeCallbacks(resetErrorRunnable);
                showError();
                return;
            }

            if (errMsgId == FingerprintManager.FINGERPRINT_ACQUIRED_TOO_FAST) {
                return;
            }

            showError();
            mIcon.postDelayed(resetErrorRunnable, configShortAnimTime);
        }
    }

    @Override
    public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
        showError();
        mIcon.postDelayed(resetErrorRunnable, configShortAnimTime);
    }

    @Override
    public void onAuthenticationFailed() {
        showError();
        mIcon.postDelayed(resetErrorRunnable, configShortAnimTime);
    }

    @Override
    public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) {
        mIcon.setColorFilter(mSuccessColor);

        mIcon.postDelayed(() -> mCallback.onAuthenticated(), configShortAnimTime);
    }

    private void showIcon() {
        mIcon.setVisibility(View.VISIBLE);
    }

    private void showError() {
        mIcon.setColorFilter(mAlertColor);
    }

    private void resetError() {
        mIcon.clearColorFilter();
    }

    public interface Callback {

        void onAuthenticated();
    }
}

1 Ответ

0 голосов
/ 20 декабря 2018

Требуется ли вам CryptoObject, зависит от того, хотите ли вы выполнить криптографическую операцию, требующую от пользователя аутентификации по отпечатку пальца.Только вы знаете ответ на этот вопрос.


Например, предположим, что ваше приложение обменивается данными с сервером, и в какой-то момент вы хотите доказать серверу, что пользователь прошел проверку подлинности с помощью своего отпечатка пальца вваше приложение.

Способ, который вы можете сделать, заключается в том, что, когда пользователь впервые «регистрируется» в вашем приложении (хотя это уже сделано), вы создаете пару ключей RSA, которая требует аутентификации по отпечатку пальца, и вы делитесьоткрытый ключ с сервером.

Позже, когда вы захотите доказать серверу, что пользователь прошел аутентификацию, вы можете попросить сервер подписать некоторые данные.Затем вы создаете Signature с закрытым ключом RSA и переносите его в CryptoObject.После аутентификации пользователя вы можете подписать данные, полученные с сервера, и отправить подпись на сервер, который может проверить подпись с помощью открытого ключа.

Это добавляет дополнительный уровень безопасности по сравнению с простым высказыванием «Аутентификация по отпечатку пальца прошла успешно» , потому что - если только на устройстве нет какого-либо критического недостатка безопасности - закрытый ключ не может использоваться до тех пор, пока пользователь не аутентифицируется, даже на корневом устройстве.

...