Icons/Others/Logistic
Tecnología
Tiempo de lectura: 6 minutos

Identificación Biométrica: Autenticación con huella dactilar en tu App

Un ejercicio práctico para añadir a nuestra App la autenticación a través de la huella dactilar, extendido hoy como un nivel más en seguridad.

Utilizamos el móvil 24h al día para el ocio, trabajo, vida personal,… lo que le hace portador de abundante información personal. ¿Nos hemos parado a pensar si esta información está debidamente protegida? Además de los conocidos sistemas de PIN y patrones, actualmente se ha extendido la autenticación por huella dactilar como un nivel más en seguridad.

Casi siempre cuantificamos el valor de nuestros dispositivos solamente utilizando como criterio el valor en dinero que tenga el aparato, pero debemos tener en cuenta también la información que se incluye. Algo que puede ser todavía más valioso. Es por esto que debemos tener todo debidamente bien protegido.

Desde hace unos cuantos años, además de los conocidos sistemas de PIN y patrones, se están extendiendo sistemas biométricos que se pueden utilizar o añaden un nivel más en el ámbito de la seguridad. Reconocimiento facial, de iris, patrones de voz,…y entre ellos el escáner de huellas dactilares.

Autenticación con Huella Dactilar en iOS

 

Este último es el más extendido ya que está disponible en la mayoría de dispositivos, sean de alta o baja gama. Su uso se ha extendido tan rápido gracias a las ventajas que ofrece:

  • La huella siempre va con nosotros. Nunca se pierde o se nos olvida.
  • Se utiliza rápidamente y tiene un nivel alto de confianza.
  • Su naturaleza única asegura que los desbloqueos se realicen solamente por el usuario.
  • Las operaciones que requieran verificaciones serán más rápidas y prácticas (como puede ser la banca online por ejemplo).

Existen diferentes tipos de escáneres de huellas, cada uno con sus características, que se utilizan (o que se han utilizado) dependiendo del dispositivo:

  • Óptico: Es el primer sistema utilizado. Utiliza tecnología parecida a la fotografía para reconocer las líneas de las huellas. No es muy seguro ya que se podría engañar al sistema con una fotografía de gran calidad.
  • Capacitivo: Este tipo de sensores genera una huella eléctrica, al contacto del dedo, en la que se analizan las características únicas de la misma. Se puede engañar al sistema, pero es mucho más difícil y hoy por hoy es el sistema más utilizado.
  • Ultrasónico: Es el sistema más reciente y utilizan los ultrasonidos para crear una huella tridimensional. Todavía no está extendido pero se prevé que su implantación será rápida, ya que permite “esconder” el detector debajo de la pantalla. De esta forma ganaríamos todavía más espacio de pantalla en una época en la que los bordes mínimos son tendencia.

Acceso con Huella Dactilar en Android

Dejando la pequeña introducción de lado, y como buenos programadores que somos, a continuación veremos cómo añadir a nuestros desarrollos la detección de la huella del usuario. Debemos de tener en cuenta que nosotros accederemos a la información de seguridad configurada por Android, no crearemos nuevas configuraciones para diferentes huellas o cosas por el estilo.

Vamos a ver paso a paso cómo añadir el reconocimiento de huellas en nuestra app. Muchos ejemplos de la web utilizan la estructura que veremos a continuación:

  1. Creamos un nuevo proyecto en Android Studio. El SDK mínimo lo fijaremos en el 6.0 (API 23). En versiones anteriores no funcionará.
  2. Ya que necesitaremos acceso a las huellas debemos solicitar el permiso correspondiente en el Manifest. Añadimos la siguiente línea:
    <uses-permission android:name="android.permission.USE_FINGERPRINT" />
  3. En el fichero colors.xml añadiremos unos colores personalizados.
    <resources>
        <color name="colorPrimary">#263237</color>
        <color name="colorPrimaryDark">#1e282d</color>
        <color name="colorAccent">#1e282d</color>
        <color name="textPrimary">#f5f5f5</color>
        <color name="textPrimaryDark">#95aab4</color>
        <color name="errorText">#ff7878</color>
        <color name="successText">#aaFF00</color>
    </resources>
  4. En los string añadiremos las siguientes cadenas. Son mensajes que apareceran en la pantalla.
    <resources>
        <string name="app_name">Huella</string>
        <string name="title_activity_main">MainActivity</string>
        <string name="title_fingerprint">Logueo con un toque</string>
        <string name="desc_fingerprint">Coloque la yema del dedo en el escáner para verificar su identidad</string>
        <string name="note">(El inicio de sesión con la huella dactilar hace que la aplicación inicie sesión mucho más rápido. Su dispositivo debe tener al menos una huella digital registrada en la configuración del dispositivo)</string>
        <string name="title_activity_home">Huella dactilar</string>
        <string name="activity_home_desc">Ha iniciado sesión con éxito con la autenticación de huellas dactilares</string>
        <string name="activity_home_note">Cierre y vuelva a abrir la aplicación para volver a ver la pantalla de autenticación de huellas dactilares</string>
    </resources>
  5. Si no disponemos de uno, crearemos un icono de Huella Dactilar. Para ello pulsamos el botón derecho en la carpeta drawable y Create New -> Image Asset y lo llamamos ic_action_fingerprint. Es importante que seleccionemos en el tipo Action Bar and Tab Icons.
  6. Creamos un layout nuevo llamado activity_fingerprint.xml y añadimos este contenido:
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/activity_fingerprint"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorPrimary"
        tools:context="ertzil.com.huella.FingerprintActivity">
    
        <LinearLayout
            android:layout_width="match_parent"
            android:id="@+id/headerLayout"
            android:orientation="vertical"
            android:gravity="center"
            android:layout_marginTop="100dp"
            android:layout_height="wrap_content">
    
            <ImageView
                android:layout_width="70dp"
                android:layout_height="70dp"
                android:src="@drawable/ic_action_fingerprint"
                android:id="@+id/icon"
                android:paddingTop="2dp"
                android:layout_marginBottom="30dp"/>
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="@color/textPrimary"
                android:textSize="24sp"
                android:text="@string/title_fingerprint"
                android:layout_marginLeft="5dp"
                android:layout_marginRight="5dp"
                android:layout_marginTop="20dp"
                android:layout_marginBottom="10dp"/>
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textColor="@color/textPrimary"
                android:textSize="16sp"
                android:textAlignment="center"
                android:gravity="center"
                android:id="@+id/desc"
                android:text="@string/desc_fingerprint"
                android:layout_margin="16dp"
                android:paddingEnd="30dp"
                android:paddingStart="30dp"/>
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textColor="@color/errorText"
                android:textSize="14sp"
                android:textAlignment="center"
                android:id="@+id/errorText"
                android:paddingEnd="30dp"
                android:paddingStart="30dp"
                android:layout_marginTop="30dp"
                android:gravity="center"/>
        </LinearLayout>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@color/textPrimaryDark"
            android:textSize="14sp"
            android:text="@string/note"
            android:layout_marginLeft="16dp"
            android:textAlignment="center"
            android:layout_marginRight="16dp"
            android:layout_marginBottom="26dp"
            android:layout_alignParentBottom="true"/>
    </RelativeLayout>
    
  7. A continuación tocará crear la clase de tipo Actividad que se llamará FingerprintActivity.java. Dentro haremos el inflate del layout creado en el paso 7.
    public class FingerprintActivity extends AppCompatActivity {
    
        private KeyStore keyStore;
        // Variable used for storing the key in the Android Keystore container
        private static final String KEY_NAME = "pruebaHuella";
        private Cipher cipher;
        private TextView textView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_fingerprint);
    
            // Initializing both Android Keyguard Manager and Fingerprint Manager
            KeyguardManager keyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
            FingerprintManager fingerprintManager = (FingerprintManager) getSystemService(FINGERPRINT_SERVICE);
    
            textView = (TextView) findViewById(R.id.errorText);
    
            // Check whether the device has a Fingerprint sensor.
            if(!fingerprintManager.isHardwareDetected()){
                /**
                 * An error message will be displayed if the device does not contain the fingerprint hardware.
                 * However if you plan to implement a default authentication method,
                 * you can redirect the user to a default authentication activity from here.
                 * Example:
                 * Intent intent = new Intent(this, DefaultAuthenticationActivity.class);
                 * startActivity(intent);
                 */
                textView.setText("Your Device does not have a Fingerprint Sensor");
            }else {
                // Checks whether fingerprint permission is set on manifest
                if (ActivityCompat.checkSelfPermission(this, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
                    textView.setText("Fingerprint authentication permission not enabled");
                }else{
                    // Check whether at least one fingerprint is registered
                    if (!fingerprintManager.hasEnrolledFingerprints()) {
                        textView.setText("Register at least one fingerprint in Settings");
                    }else{
                        // Checks whether lock screen security is enabled or not
                        if (!keyguardManager.isKeyguardSecure()) {
                            textView.setText("Lock screen security not enabled in Settings");
                        }else{
                            generateKey();
    
                            if (cipherInit()) {
                                FingerprintManager.CryptoObject cryptoObject = new FingerprintManager.CryptoObject(cipher);
                                FingerprintHandler helper = new FingerprintHandler(this);
                                helper.startAuth(fingerprintManager, cryptoObject);
                            }
                        }
                    }
                }
            }
        }
    
        @TargetApi(Build.VERSION_CODES.M)
        protected void generateKey() {
            try {
                keyStore = KeyStore.getInstance("AndroidKeyStore");
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            KeyGenerator keyGenerator;
            try {
                keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
            } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
                throw new RuntimeException("Failed to get KeyGenerator instance", e);
            }
    
            try {
                keyStore.load(null);
                keyGenerator.init(new
                        KeyGenParameterSpec.Builder(KEY_NAME,
                        KeyProperties.PURPOSE_ENCRYPT |
                                KeyProperties.PURPOSE_DECRYPT)
                        .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
                        .setUserAuthenticationRequired(true)
                        .setEncryptionPaddings(
                                KeyProperties.ENCRYPTION_PADDING_PKCS7)
                        .build());
                keyGenerator.generateKey();
            } catch (NoSuchAlgorithmException |
                    InvalidAlgorithmParameterException
                    | CertificateException | IOException e) {
                throw new RuntimeException(e);
            }
        }
    
        @TargetApi(Build.VERSION_CODES.M)
        public boolean cipherInit() {
            try {
                cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7);
            } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
                throw new RuntimeException("Failed to get Cipher", e);
            }
    
            try {
                keyStore.load(null);
                SecretKey key = (SecretKey) keyStore.getKey(KEY_NAME,
                        null);
                cipher.init(Cipher.ENCRYPT_MODE, key);
                return true;
            } catch (KeyPermanentlyInvalidatedException e) {
                return false;
            } catch (KeyStoreException | CertificateException | UnrecoverableKeyException | IOException | NoSuchAlgorithmException | InvalidKeyException e) {
                throw new RuntimeException("Failed to init Cipher", e);
            }
        }
    }
    

    generateKey(): función que genera una clave de cifrado que luego se almacena de forma segura en el dispositivo.
    cipherInit(): función que inicializa el cifrado que se utilizará para crear el FingerprintManager encriptado.
    CryptoObject: instancia y varias otras comprobaciones antes de iniciar el proceso de autenticación que se implementa dentro del método onCreate ().

  8. Crearemos la Actividad Home pulsando el botón derecho en el proyecto y seleccionando New -> Activity -> Basic Activity. Reemplazaremos su layout con el siguiente código:
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="ertzil.com.huella.HomeActivity">
        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/AppTheme.AppBarOverlay">
            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                app:popupTheme="@style/AppTheme.PopupOverlay" />
        </android.support.design.widget.AppBarLayout>
        <include layout="@layout/content_home" />
        <android.support.design.widget.FloatingActionButton
            android:id="@+id/fab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|end"
            android:layout_margin="@dimen/fab_margin"
            app:srcCompat="@android:drawable/ic_dialog_email" />
    </android.support.design.widget.CoordinatorLayout>
    
  9. El content_home.xml será como este:
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/content_home"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="10dp"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        android:paddingTop="10dp"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        tools:context="ertzil.com.huella.HomeActivity"
        tools:showIn="@layout/activity_home">
    
        <TextView
            android:layout_width="match_parent"
            android:text="@string/activity_home_desc"
            android:layout_centerVertical="true"
            android:textSize="24sp"
            android:textAlignment="center"
            android:textColor="@color/colorPrimary"
            android:layout_height="wrap_content" />
    
        <TextView
            android:layout_width="match_parent"
            android:text="@string/activity_home_note"
            android:layout_alignParentBottom="true"
            android:textSize="14sp"
            android:textAlignment="center"
            android:textColor="@color/colorPrimary"
            android:layout_height="wrap_content"
            android:layout_marginBottom="24dp"/>
    </RelativeLayout>
  10. Para terminar, la clase será como esta:
    public class HomeActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_home);
            Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
            setSupportActionBar(toolbar);
        }
    }
  11. Para ir terminando crearemos el Handler que se encargará de detectar la huella. Crearemos una nueva clase con el nombre FingerprintHandler.java. Esta clase extenderá de FingerprintManager.AuthenticationCallback y tendrá una serie de módulos adicionales. Reemplazad el contenido por este:
    import android.Manifest;
    import android.app.Activity;
    import android.content.Context;
    import android.content.pm.PackageManager;
    import android.hardware.fingerprint.FingerprintManager;
    import android.os.CancellationSignal;
    import android.support.v4.app.ActivityCompat;
    import android.support.v4.content.ContextCompat;
    import android.widget.TextView;
    public class FingerprintHandler extends FingerprintManager.AuthenticationCallback {
    
        private Context context;
    
        // Constructor
        public FingerprintHandler(Context mContext) {
            context = mContext;
        }
    
        public void startAuth(FingerprintManager manager, FingerprintManager.CryptoObject cryptoObject) {
            CancellationSignal cancellationSignal = new CancellationSignal();
            if (ActivityCompat.checkSelfPermission(context, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
                return;
            }
            manager.authenticate(cryptoObject, cancellationSignal, 0, this, null);
        }
    
        @Override
        public void onAuthenticationError(int errMsgId, CharSequence errString) {
            this.update("Error de Autenticación de huellas dactilares\n" + errString, false);
        }
    
        @Override
        public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
            this.update("Ayuda de Autenticación de huellas dactilares\n" + helpString, false);
        }
    
        @Override
        public void onAuthenticationFailed() {
            this.update("Fallo al autenticar con la huella dactilar.", false);
        }
    
        @Override
        public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
            this.update("Éxito al autenticar con la huella dactilar.", true);
        }
    
        public void update(String e, Boolean success){
            TextView textView = (TextView) ((Activity)context).findViewById(R.id.errorText);
            textView.setText(e);
            if(success){
                textView.setTextColor(ContextCompat.getColor(context,R.color.successText));
            }
        }
    }
  12. Con todo ya podemos probar la aplicación. Una vez lanzada, si pasamos el dedo por el sensor de huellas, la app nos avisará mediante un mensaje si la huella ha sido reconocida o no. Para realizar una nueva prueba deberemos de cerrar la app y volver a iniciarla.
    El código de ejemplo lo tenéis disponible aquí.

 

En los siguientas entradas seguiremos con este tema centrándonos en otros sensores disponibles hoy en día. ¡Estad atentos!

Escrito por
ErtzilProject Manager