Обеспечение безопасности ваших приложений для Android с помощью Fingerprint API –

Начиная

Это пример приложения, ограниченного в пользовательском опыте, и будет сосредоточено на «как».

Для начала создайте новый проект и назовите его «FingerprintApi». Установите уровень API 15 в качестве минимального SDK и добавьте активность входа . Нажмите « Готово» , и Android Studio начнет создавать необходимые файлы.

LoginActivity поможет начать работу быстрее, но изменения по-прежнему необходимы.

В файле activity_login.xml измените метку кнопки « Вход» или «Зарегистрироваться» на « Регистрация» и добавьте еще одну кнопку с надписью « Войти с помощью отпечатка пальца» . Ниже этого, добавьте TextView именем noteTextView . Отладочная информация будет напечатана здесь, вместо использования диалогов.

В LoginActivity.java замените существующие переменные на приведенные ниже, которые вы будете использовать позже.

Authentication callbacks

After a helper class extends from FingerprintManager.AuthenticationCallback is created, the following callbacks are called depending on result of authenticate(CryptoObject, CancellationSignal, int, AuthenticationCallback, Handler) method.

Class properties

In addition to all of the properties accepted via the constructor, there are some values that we need to create inside the class itself. We need:

  • A cancellation signal to alert us when authentication should be cancelled.
  • A boolean flag to know if cancellation occured by our controller or some other source.
  • A boolean flag to tell us if this device should support fingerprint authentication.
  • A runnable that resets the dialog to its initial state. We’ll run this when the class is instantiated.

Here is what our class looks like now:

Creating your mainactivity.java file

Now it’s time to implement the fingerprint authentication part of our app.

Dialog view

When adding fingerprint authentication to your app, you have full control over how that flow should be designed. However, there is a material design specification for the fingerprint authentication flow. This page dicusses certain text that should be included, what the behavior looks like, and the icon that you should use.

From the above specification, you’ll notice that Google has supplied the red lines for creating a dialog:

For the purposes of this tutorial, we won’t be including a “use password” fallback, and will focus solely on the fingerprint authentication. Using the above red lines, I was able to create an XML file for the dialog using a ConstraintLayout. The following code is inside my dialog_fingerprint.xml file, which you can see on GitHub.

Fingerprint manager

FingerprintManager is fundamental of using fingerprint scan. Most of the fingerprint related methods can be found under this class.

FingerprintManager fingerprintManager = (FingerprintManager)
.getSystemService(Context.FINGERPRINT_SERVICE);

After getting instance of FingerprintManager , you will be able to use some methods of this class such as isHardwareDetected(), hasEnrolledFingerprints() and authenticate(…) .

Checks whether fingerprint sensor does exist or is available.

Checks if there is at least one enrolled fingerprint on device.

Tries to authenticate cypto object. The fingerprint hardware starts listening for scan after that call. Before calling this method , appropriate parameters should be prepared.

Parameter 1: CryptoObject

Before creating new instance of CryptoObject , you need to initialize Cipher object as follows

Handle fingerprint authentication callbacks

As discussed in the first part of this section, our controller class extends FingerprintManagerCompat.AuthenticationCallback. There are four methods in this class that we will implement, and another utility method. These are:

Sample code for each one may look like this. Everything below is inside our FingerprintController.kt file:

Keyguard manager

This class provides access to your lock screen.

KeyguardManager keyguardManager = (KeyguardManager)
.getSystemService(Context.KEYGUARD_SERVICE);
keyguardManager.isKeyguardSecure();

Returns whether the keyguard is secured by a PIN, pattern, password or a SIM card is currently locked.

Summary

Congrats! If you’ve made it this far, you made it through the toughest chunk of fingerprint authentication code. The only remaining step is to setup your MainActivity to launch this dialog.

As usual, you can find a full sample application for this post on GitHub, where everything is documented. If you have questions or improvements, please leave them in the comments!

Testing your project

Whenever you’re working on an Android app, you should test that app across a wide range of Android Virtual Devices (AVDs) plus at least one physical Android smartphone or tablet.

Где сенсор?

Чтобы начать получать профит от нового API, первым делом нужно добавить permission в манифесте:

Само собой, использовать Fingerprint API можно только на устройствах, его поддерживающих: соответственно, это устройства Android 6 с сенсором. Совместимость можно легко проверить с помощью метода:

public static boolean checkFingerprintCompatibility(@NonNull Context context) {
    return FingerprintManagerCompat.from(context).isHardwareDetected();
}

FingerprintManagerCompat — это удобная обертка для обычного FingerprintManager’а, которая упрощает проверку устройства на совместимость, инкапсулируя в себе проверку версии API. В данном случае, isHardwareDetected() вернет false, если API ниже 23.

Дальше, нам нужно понять, готов ли сенсор к использованию. Для этого определим enum состояний:

Если заявления

Чтобы использовать Fingerprint API, вы должны запросить разрешение. Добавьте эту строку в AndroidManifest.xml в верхней части тега приложения.

Конечно, не у каждого устройства есть датчик отпечатка пальца или Android 6.0 и выше. Из-за этого вам следует проверить аппаратную / программную поддержку, прежде чем пытаться использовать методы API, которые могут вызвать исключение.

Защитите свои приложения

Хотя реализация Fingerprint API может потребовать усилий, для пользователей с совместимым устройством это значительно улучшит взаимодействие с пользователем.

Что вы думаете о считывателях отпечатков пальцев? Они улучшают приложение?

Кратчайший ликбез

Итак, что же представляет собой Fingerprint API?API позволяет пользователю аутентифицироваться посредством своего отпечатка, очевидно. Для работы с сенсором API предлагает нам FingerprintManager, достаточно простой в освоении.

Первая аутентификация

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

Внутри метода onCreate раскомментируйте вызов attemptFingerprintLogin и добавьте реализацию.

Подготовка

Итак, не зацикливаясь на проверке пин-кода на валидность, прикинем следующую упрощенную логику действий:

Схема была бы достаточно простой, если бы не одно но: Гугл настойчиво рекомендует не хранить приватные данные пользователя в открытом виде. Поэтому нам нужен механизм шифровки и расшифровки для, соответственно, сохранения и использования. Займемся этим.

Что нам нужно для шифровки и расшифровки:

  1. Защищенное хранилище для ключей.
  2. Криптографический ключ.
  3. Шифровальщик

Проверка подлинности

Перед сохранением учетных данных, есть еще пара шагов. Вернуться к LoginActivity , добавить FingerprintHelper.FingerprintHelperListener после implements LoaderCallbacks<Cursor> и реализовать два метода интерфейса.

@Override public void authenticationFailed(String error) { print(error); } @TargetApi(Build.VERSION_CODES.M) @Override public void authenticationSucceeded(FingerprintManager.AuthenticationResult result) { print("Authentication succeeded!"); }

TargetApi указывает минимальную версию Android для методов, которые будут вызываться внутри authenticationSucceeded . Раскомментируйте вызовы attemptRegister() внутри onCreate и добавьте эти строки в этот метод.

if (!testFingerPrintSettings()) return;

Объявите переменную типа FingerprintHelper в переменных класса.

private FingerprintHelper fingerprintHelper;

Переопределите метод onPause чтобы остановить задачи, если по какой-либо причине действие входит в это условие.

@Override protected void onPause() { super.onPause(); if (fingerprintHelper != null) fingerprintHelper.cancel(); if (mAuthTask != null) mAuthTask.cancel(true); }

Сохранение учетных данных

Я буду doInBackground метод doInBackground новые функции. Если какой-либо из этих вызовов завершается неудачно, процесс прерывается.

if (!getKeyStore()) return false; private boolean getKeyStore() { print("Getting keystore..."); try { keyStore = KeyStore.getInstance(KEYSTORE); keyStore.load(null); // Create empty keystore return true; } catch (KeyStoreException e) { print(e.getMessage()); } catch (CertificateException e) { print(e.getMessage()); } catch (NoSuchAlgorithmException e) { print(e.getMessage()); } catch (IOException e) { print(e.getMessage()); } return false; }

KeyStore – это хранилище ключей и сертификатов в виде базы данных. Каждая запись идентифицируется строкой под названием «псевдоним», но для доступа к ней сначала необходимо загрузить определенное хранилище ключей. Я назвал мой AndroidKeyStore.

Тестовые случаи

При использовании Fingerprint API многое может пойти не так. Я попытался составить список различных тестовых случаев, чтобы принять во внимание.

Хелпер класс

Затем вам понадобится вспомогательный класс для обработки методов Fingerprint API.Щелкните правой кнопкой мыши текущий пакет, затем выберите « Создать» -> «Класс Java» . Назовите новый класс FingerprintHelper и FingerprintHelper чтобы он расширял FingerprintManager.

@TargetApi(Build.VERSION_CODES.M) public class FingerprintHelper extends FingerprintManager.AuthenticationCallback { ... }

Объявите внутренний интерфейс с помощью следующих методов.

interface FingerprintHelperListener { public void authenticationFailed(String error); public void authenticationSucceeded(FingerprintManager.AuthenticationResult result); }

Все классы, которые будут использовать FingerprintHelper должны реализовывать этот интерфейс для уведомления о состоянии аутентификации. Чтобы иметь возможность вызывать эти методы, вам нужен экземпляр вызывающего класса, взятый в качестве параметра в конструкторе.

private FingerprintHelperListener listener; public FingerprintHelper(FingerprintHelperListener listener) { this.listener = listener; }

Чтобы сканер отпечатков пальцев начал прослушивать ввод, вызовите метод FingerprintManagerauthenticate . Вы используете CancellationSignal чтобы остановить прослушивание, когда пользователь отменяет аутентификацию, или когда приложение переходит в фоновый режим и т. Д.

Этот метод позволяет другим классам отменить вызов:

private CancellationSignal cancellationSignal; public void startAuth(FingerprintManager manager, FingerprintManager.CryptoObject cryptoObject) { cancellationSignal = new CancellationSignal(); try { manager.authenticate(cryptoObject, cancellationSignal, 0, this, null); } catch (SecurityException ex) { listener.authenticationFailed("An error occurred:n"   ex.getMessage()); } catch (Exception ex) { listener.authenticationFailed("An error occurredn"   ex.getMessage()); } } public void cancel() { if (cancellationSignal != null) cancellationSignal.cancel(); }

Метод authenticate использует объект CryptoObject , это класс-оболочка для объектов шифрования, поддерживаемых FingerprintManager .

Наконец, переопределите методы AuthenticationCallback чтобы уведомить FingerprintHelperListener .

@Override public void onAuthenticationError(int errMsgId, CharSequence errString) { listener.authenticationFailed("Authentication errorn"   errString); } @Override public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { listener.authenticationFailed("Authentication helpn"   helpString); } @Override public void onAuthenticationFailed() { listener.authenticationFailed("Authentication failed."); } @Override public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) { listener.authenticationSucceeded(result); }

Я упомяну эти методы, когда буду говорить о тестовых случаях, но пока вспомогательный класс готов к использованию.

Шифровальщик


Шифровкой и дешифровкой в Java занимается объект Cipher. Инициализируем его:

private static Cipher sCipher;
private static boolean getCipher() {
    try {
        sCipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
        return true;
    } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
        e.printStackTrace();
    }
    return false;
}

Адовая мешанина в аргументе — это строка трансформации, которая включает в себя алгоритм, режим смешивания и дополнение.

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

private static boolean initCipher(int mode) {
    try {
        sKeyStore.load(null);
        switch (mode) {
            case Cipher.ENCRYPT_MODE:
                initEncodeCipher(mode);
                break;
            case Cipher.DECRYPT_MODE:
                initDecodeCipher(mode);
                break;
            default:
                return false; //this cipher is only for encodedecode
            }
        return true;
    } catch (KeyPermanentlyInvalidatedException exception) {
        deleteInvalidKey();
    } catch (KeyStoreException | CertificateException | UnrecoverableKeyException | IOException |
                NoSuchAlgorithmException | InvalidKeyException | InvalidKeySpecException | InvalidAlgorithmParameterException e) {
        e.printStackTrace();
    }
    return false;
}

где initDecodeCipher() и initEncodeCiper() следующие:

private static void initDecodeCipher(int mode) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException, InvalidKeyException {
    PrivateKey key = (PrivateKey) sKeyStore.getKey(KEY_ALIAS, null);
    sCipher.init(mode, key);
}
private static void initEncodeCipher(int mode) throws KeyStoreException, InvalidKeySpecException, NoSuchAlgorithmException, InvalidKeyException, InvalidAlgorithmParameterException {
    PublicKey key = sKeyStore.getCertificate(KEY_ALIAS).getPublicKey();
    PublicKey unrestricted = KeyFactory.getInstance(key.getAlgorithm()).generatePublic(new X509EncodedKeySpec(key.getEncoded()));
    OAEPParameterSpec spec = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT);
    sCipher.init(mode, unrestricted, spec);
}

Нетрудно заметить, что зашифровывающий Cipher несколько сложнее инициализировать. Это косяк самого Гугла, суть которого в том, что публичный ключ требует подтверждения пользователя. Мы обходим это требование с помощью слепка ключа (костыль, ага).

Момент с KeyPermanentlyInvalidatedException — если по какой-то причине ключ нельзя использовать, выстреливает это исключение. Возможные причины — добавление нового отпечатка к существующему, смена или полное удаление блокировки. Тогда ключ более не имеет смысла хранить, и мы его удаляем.

public static void deleteInvalidKey() {
    if (getKeyStore()) {
        try {
            sKeyStore.deleteEntry(KEY_ALIAS);
        } catch (KeyStoreException e) {
            e.printStackTrace();
        }
    }
}

Метод, который собирает всю цепочку подготовки:

private static boolean prepare() {
    return getKeyStore() && getCipher() && isKeyReady();
}

Шифровка и расшифровка

Опишем метод, который зашифровывает строку аргумент:

Похожее:  Личный кабинет МТС - Марий Эл (Республика Марий Эл) — Йошкар-Ола

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *