Agrega Play Integrity a tu aplicación para Android

1. Introducción

Última actualización: 4 de enero de 2023

¿Qué es Play Integrity?

La API de Play Integrity ayuda a proteger tus apps y juegos de interacciones que podrían ser riesgosas y fraudulentas. Puedes usar esta API para obtener veredictos de integridad sobre tu app y dispositivo, lo que puede ayudarte a tomar las medidas adecuadas para reducir ataques y abusos, como fraudes, trampas y accesos no autorizados.

Soluciones anteriores

La API de Play Integrity reemplaza dos soluciones anteriores: la biblioteca de Licencias de apps y la API de SafetyNet Attestation. Se recomienda Play Integrity para aplicaciones nuevas. Las apps existentes que usan las soluciones anteriores deberían actualizarse para usar Play Integrity.

Elige una ruta

Play Integrity incluye opciones de biblioteca para diferentes tipos de app, como los siguientes:

  • Juegos y otras apps escritos en C/C++
  • Apps escritas en los lenguajes de programación Kotlin o Java
  • Juegos desarrollados con el motor Unity

En este codelab, se incluyen rutas para las tres opciones. Puedes elegir las que son relevantes para tus necesidades de desarrollo. El codelab implica la compilación y la implementación de un servidor de backend. Este servidor se comparte en los tres tipos de app.

Qué compilarás

En este codelab, integrarás Play Integrity en una app de ejemplo y usarás la API para verificar la integridad del dispositivo y la app. Implementarás una pequeña aplicación de servidor de backend que se usa para respaldar el proceso de verificación de integridad.

Qué aprenderás

  • Cómo integrar la biblioteca de Play Integrity en una app
  • Cómo usar la API de Play Integrity para realizar una verificación de integridad
  • Cómo procesar de forma segura un veredicto de integridad con un servidor
  • Cómo interpretar los resultados del veredicto de integridad

Este codelab se enfoca en Play Integrity. Los conceptos y los bloques de código no relevantes no se explican en detalle y se proporcionan para que simplemente los copies y pegues.

Requisitos

  • Una Cuenta de Google con un registro activo de desarrollador de Android, además de acceso a Play Console y a la consola de Google Cloud
  • En el caso de las rutas de acceso de C++ o Kotlin, Android Studio 2021.1.1 o una versión posterior
  • En el caso de la ruta de acceso de Unity, Unity 2020 LTS o una versión posterior
  • Un dispositivo con tecnología Android conectado a tu computadora que tenga habilitadas las Opciones para desarrolladores y la depuración por USB

En este codelab, se incluyen vínculos a recursos para firmar compilaciones y subirlas a Play Console, pero se da por sentado que ya conoces este proceso.

2. Obtén el código

Los proyectos del codelab están disponibles en un repositorio de Git. En el directorio principal del repositorio, hay cuatro directorios:

  • El directorio server contiene el código del servidor de ejemplo que implementarás.
  • El directorio cpp contiene un proyecto de Android Studio para agregar Play Integrity a una app o un juego de C++.
  • El directorio kotlin contiene un proyecto de Android Studio para agregar Play Integrity a una app para Android estándar.
  • El directorio unity contiene un proyecto creado con la versión 2020 LTS del motor de Unity para agregar Play Integrity a un proyecto de Unity.

En cada uno de los directorios de ejemplo de cliente, hay dos subdirectorios:

  • El directorio start, que tiene la versión del proyecto que modificaremos para este codelab
  • El directorio final, que tiene una versión del proyecto que coincide con el modo en que debería verse el proyecto cuando se complete el codelab

Cómo clonar el repositorio

Desde la línea de comandos, cambia al directorio donde deseas que esté el directorio raíz add-play-integrity-codelab y, luego, clona el proyecto desde GitHub con la siguiente sintaxis:

git clone https://github.com/android/add-play-integrity-codelab.git

Cómo agregar dependencias (solo C++)

Si deseas usar la ruta de acceso de C++, debes inicializar los submódulos del repositorio para configurar la biblioteca Dear ImGui, que se usa para la interfaz de usuario. Para ello, desde la línea de comandos, haz lo siguiente:

  1. Cambia el directorio de trabajo a add-play-integrity-codelab/cpp/start/third-party.
  2. git submodule update --init

3. Explicación de las funciones del cliente y del servidor

Un elemento clave del modelo de seguridad de Play Integrity es mover las operaciones de validación del dispositivo a un servidor seguro que tú controles. Si realizas estas operaciones en el servidor, te proteges contra situaciones como un dispositivo vulnerado que intenta implementar un ataque de repetición o manipular el contenido del mensaje.

El servidor de muestra del codelab se diseñó a modo de ejemplo y, por cuestiones de simplicidad, no contiene muchos atributos que serían convenientes en un entorno de producción. Su lista de valores generados se almacena solo en la memoria, y el almacenamiento persistente no crea una copia de seguridad. Los extremos del servidor no están configurados para requerir autenticación.

El extremo performCommand

El servidor proporciona un extremo /performCommand al que se accede a través de una solicitud POST HTTP. Se espera que el cuerpo de la solicitud sea una carga útil de JSON con los siguientes pares clave-valor:

{
   "commandString": "command-here",
   "tokenString": "token-here"
}

El extremo /performCommand devuelve una carga útil de JSON con los siguientes pares clave-valor:

{
   "commandSuccess": true/false,
   "diagnosticMessage": "summary text",
   "expressToken": "token-here"
}

El contenido real del parámetro commandString no importa. El servidor valida la integridad de commandString cuando se usa Play Integrity, pero no usa el valor del comando. Todas las versiones de cliente usan el valor "TRANSFER FROM alice TO bob CURRENCY gems QUANTITY 1000".

El valor del parámetro tokenString debe ser uno de los siguientes:

  • Un token que genera la API de Play Integrity
  • Un token exprés que devuelve una llamada exitosa a /performCommand anterior

El servidor desencripta y valida el token que proporciona la API de Play Integrity. El resultado de la desencriptación es una carga útil de JSON de los indicadores de integridad. Según el valor de los indicadores, el servidor puede elegir aprobar o rechazar el comando. Si el token se desencripta de manera correcta, se devuelve una descripción resumida de los indicadores en diagnosticMessage. Por lo general, no devolverías esta información al cliente en una aplicación de producción, pero los clientes del codelab la usan para mostrar el resultado de tu operación sin tener que mirar los registros del servidor. Si se produce una condición de error mientras se procesa el token, el error se devuelve en diagnosticMessage.

Generación de nonce

Para realizar una solicitud de Play Integrity, es necesario generar un nonce y asociarlo con la solicitud. El nonce se usa para garantizar que una solicitud de integridad sea única y se procese solo una vez. También se usa para verificar que no se haya manipulado el contenido del mensaje asociado con la solicitud de integridad. Para obtener más información sobre los nonces de Play Integrity, consulta la documentación.

En este codelab, el nonce se genera combinando dos valores:

  • Un número aleatorio de 128 bits creado por un generador de números al azar con seguridad criptográfica
  • Un hash SHA-256 del valor commandString

La API de Play Integrity espera que el nonce sea una cadena Base64 sin padding con codificación de URL. Para crear la cadena del nonce, este codelab convierte los arrays de bytes del número aleatorio y los valores hash en cadenas hexadecimales y los concatena. La cadena resultante es una Base64 válida, pero no está codificada ni decodificada como tal.

Los clientes del codelab recuperan el número aleatorio llamando a un extremo /getRandom en el servidor con una solicitud GET HTTP. Este es el método más seguro de generación aleatoria, ya que el servidor puede verificar que fue la fuente del número aleatorio usado en la solicitud de comando. Sin embargo, esto implica un recorrido adicional al servidor. Los clientes pueden eliminar este recorrido si generan el número aleatorio por su cuenta, aunque sacrificarían seguridad.

Token exprés

Dado que llamar a la API es costoso, el servidor también proporciona un token exprés, un método alternativo de autenticación que proporciona menos seguridad a un menor costo. Una llamada exitosa a /serverCommand devuelve un token exprés en el campo expressToken con una verificación de integridad aprobada. El llamado a la API de Play Integrity es costoso en términos de procesamiento y está diseñado para proteger operaciones de alto valor. Cuando se devuelve el token exprés, el servidor proporciona una autenticación de menor seguridad para realizar operaciones que pueden ser menos importantes o que ocurren con demasiada frecuencia para justificar la validación completa con la API de Play Integrity. Se puede usar un token exprés en lugar de un token de Play Integrity cuando se llama a /serverCommand. Cada token exprés es de un solo uso. Las llamadas que se realizan correctamente a /serverCommand con un token exprés válido devuelven un token nuevo.

En la implementación del codelab, como el token exprés se genera de forma única en el servidor, los comandos aún están protegidos contra ataques de repetición. Sin embargo, los tokens exprés son menos seguros, ya que omiten la protección de hash contra la modificación de comandos y no pueden detectar las modificaciones del dispositivo que ocurrieron después de la llamada inicial a la API de Play Integrity.

Arquitectura del servidor del codelab

En este codelab, puedes crear una instancia de un servidor si usas el programa de servidor de ejemplo incluido escrito en Kotlin con el framework de Ktor. El proyecto incluye archivos de configuración para implementarlo en App Engine de Google Cloud. Las instrucciones de este codelab abarcan la compilación y la implementación en App Engine. Si usas otro proveedor de servicios en la nube, Ktor tiene instrucciones de implementación para varios servicios en la nube. Puedes modificar el proyecto según corresponda y además implementarlo en el servicio que elijas. La implementación en tu propia instancia de servidor local es una opción adicional.

No es necesario usar el código de muestra para tu servidor. Si tienes un framework de aplicación web preferido, puedes implementar los extremos /getRandom y /performCommand en tu propio framework con el servidor de codelab de ejemplo como guía.

Diseño de cliente

Las tres versiones de cliente (C++, Kotlin y el motor de Unity) tienen una interfaz de usuario similar.

El botón Request random solicita el extremo /getRandom en el servidor. El resultado se muestra en un campo de texto. Se puede usar para verificar la conexión y la función del servidor antes de agregar la integración de Play Integrity.

El botón Call server with integrity check no hace nada al comienzo del codelab. Seguirás los pasos para agregar código correspondiente a las siguientes operaciones:

  • Llamar a /getRandom para obtener un número aleatorio
  • Cómo generar un nonce
  • Crear una solicitud de Play Integrity con el nonce
  • Llamar a /performCommand con el token que generó la solicitud de Play Integrity
  • Mostrar los resultados de /performCommand

Los resultados del comando se muestran en un campo de texto. Para un comando correcto, este es un resumen de la información del veredicto del dispositivo que devuelve la verificación de Play Integrity.

El botón Call server with express token aparece después de una operación exitosa de llamada al servidor con verificación de integridad. Llama a /performCommand con el token exprés del /performCommand anterior. Se usa un campo de texto para mostrar el éxito o la falla del comando. El valor de un token exprés devuelto se muestra en el campo de texto que se usa para números aleatorios.

Las funciones de la API de Play Integrity que informan errores lo hacen con la devolución de un código de error. Para obtener más detalles sobre estos códigos de error, consulta la documentación de Play Integrity. Algunos errores pueden deberse a condiciones del entorno, como una conexión a Internet inestable o un dispositivo sobrecargado. En el caso de estos errores, considera incluir una opción de reintento con retirada exponencial. En este codelab, los errores de Play Integrity se imprimen en Logcat.

4. Configura App Engine de Google Cloud

Para usar Google Cloud, sigue estos pasos:

  1. Regístrate en Google Cloud Platform. Usa la misma Cuenta de Google registrada en Play Console.
  2. Crea una cuenta de facturación.
  3. Instala y también inicializa el SDK de Google Cloud.

Usa el Google Cloud CLI recién instalado para ejecutar los siguientes comandos:

  1. Instalar la extensión de App Engine para Java: gcloud components install app-engine-java
  2. Crear un proyecto de Cloud nuevo y reemplazar $yourname en el siguiente comando por algún identificador único: gcloud projects create $yourprojectname --set-as-default
  3. Crear una aplicación de App Engine en el proyecto de Cloud: gcloud app create

5. Implementa el servidor

Configura el nombre del paquete de la aplicación

En este paso, agregarás el nombre del paquete de la aplicación al código del servidor. En un editor de texto, abre el archivo de origen ValidateCommand.kt. Se encuentra en el siguiente directorio:

add-play-integrity-codelab/server/src/main/kotlin/com/google/play/integrity/codelab/server/util

Busca la siguiente línea, reemplaza el texto del marcador de posición por un identificador de paquete único y, luego, guarda el archivo:

const val APPLICATION_PACKAGE_IDENTIFIER = "com.your.app.package"

Más adelante, configurarás este identificador en tu proyecto de cliente antes de subir la app a Play Console.

Compila el servidor y, luego, impleméntalo en App Engine

Usa Google Cloud CLI para ejecutar el siguiente comando desde el directorio add-play-integrity/server para compilar y, luego, implementar el servidor:

En Linux o macOS:

./gradlew appengineDeploy

En Microsoft Windows:

gradlew.bat appengineDeploy

Toma nota de la ubicación del servicio implementado en el resultado de una implementación exitosa. Necesitarás esta URL para configurar el cliente de modo que se comunique con el servidor.

Verifica la implementación

Puedes usar Google Cloud CLI para ejecutar el siguiente comando y verificar que el servidor funcione correctamente:

gcloud app browse

Este comando abrirá un navegador web y la URL raíz. El servidor de ejemplo debería mostrar el mensaje Hello World! cuando se acceda a él desde la URL raíz.

6. Configura la app en Play Console

Configura la integridad de la app en Play Console

Si ya tienes una entrada de app en Play Console, puedes usarla para este codelab. También puedes seguir los pasos para crear una app nueva en Play Console. Después de seleccionar o crear la app en Play Console, debes configurar la integridad de la app. En el menú del lado izquierdo de Play Console, ve a Integridad de la app en la sección Versión.

Haz clic en el botón Vincular proyecto de Cloud. Selecciona el proyecto de Google Cloud que usaste con el servidor y haz clic en el botón Vincular proyecto.

Acceso a Google Cloud para tu servidor

Tu servidor de backend debe desencriptar el token de integridad que genera la API de Play Integrity en el cliente. Play Integrity ofrece dos opciones de administración de claves: claves que genera y administra Google, o claves que proporciona el desarrollador. En este codelab, se usa el comportamiento predeterminado recomendado de las claves que administra Google.

Con las claves que administra Google, tu servidor de backend pasa el token de integridad encriptado a los servidores de Google Play para su desencriptación. El servidor del codelab usa la biblioteca cliente de las APIs de Google para comunicarse con los servidores de Google Play.

Ahora que el servidor está en funcionamiento y configuraste la app en Play Console, puedes customizar el o los clientes correspondientes a las plataformas que elegiste. Todos los pasos para una plataforma determinada se agrupan, por lo que puedes omitir las instrucciones de las plataformas que no utilizas.

7. Compila y ejecuta el cliente (C++)

Ejecuta Android Studio. En la ventana Welcome to Android Studio, haz clic en el botón Open y abre el proyecto de Android Studio ubicado en add-play-integrity-codelab/cpp/start.

Actualiza el ID de aplicación

Antes de subir una compilación a Google Play, debes cambiar el ID de aplicación del valor predeterminado a uno único. Completa los pasos siguientes:

  1. En el panel Project de Android Studio, busca el archivo build.gradle en start/app y ábrelo.
  2. Busca la sentencia applicationId.
  3. Cambia com.google.play.integrity.codelab.cpp por el nombre del paquete que elegiste cuando implementaste el servidor y, luego, guarda el archivo.
  4. En la parte superior del archivo, aparecerá un banner que te informará que los archivos de Gradle cambiaron. Haz clic en Sync Now para volver a cargar y sincronizar el archivo.
  5. En el panel Project de Android Studio, abre el archivo AndroidManifest.xml en start/app/src/main.
  6. Busca la sentencia package="com.example.google.codelab.playintegritycpp".
  7. Reemplaza com.example.google.codelab.playintegritycpp por el nombre único de tu paquete y, luego, guarda el archivo.
  8. En el panel Project de Android Studio, abre el archivo PlayIntegrityCodelabActivity en start/app/src/main/java/com.example.google.codelab.playintegritycpp.
  9. Busca la sentencia package com.example.google.codelab.playintegritycpp.
  10. Reemplaza com.example.google.codelab.playintegritycpp por el nombre único de tu paquete.
  11. Haz clic con el botón derecho en el nombre del paquete nuevo y elige Show Context Actions.
  12. Elige Move to (el nuevo nombre del paquete).
  13. Si aparece, selecciona el botón Sync Now en la parte superior del archivo.

Actualiza las URLs del servidor

El proyecto debe actualizarse para que apunte a las ubicaciones de las URLs en las que implementaste el servidor.

  1. En el panel Project de Android Studio, abre el archivo server_urls.hpp en start/app/src/main/cpp.
  2. Agrega la URL raíz que se mostró cuando implementaste el servidor en las definiciones GET_RANDOM_URL y PERFORM_COMMAND_URL, y guarda el archivo.

El resultado debería ser similar al siguiente:

constexpr char GET_RANDOM_URL[] = "https://your-play-integrity-server.uc.r.appspot.com/getRandom";
constexpr char PERFORM_COMMAND_URL[] = "https://your-play-integrity-server.uc.r.appspot.com/performCommand";

La URL específica variará según el nombre del proyecto y la región de Google Cloud que usaste para implementar el servidor.

Compilación y ejecución

Conecta un dispositivo Android configurado para desarrollo. En Android Studio, compila el proyecto y ejecútalo en el dispositivo conectado. La app debería aparecer de la siguiente manera:

429ccc112f78d454.png

Toca el botón Request Random para ejecutar un código que realice una solicitud HTTP a tu servidor para solicitar un número aleatorio. Después de una breve demora, deberías ver el número aleatorio en la pantalla:

62acee42ba1fa80.png

Si se muestra un mensaje de error, el resultado del panel de Logcat puede contener más detalles.

Después de que hayas recuperado de manera correcta un valor aleatorio para verificar que te comunicas con el servidor, estará todo listo para comenzar a integrar la API de Play Integrity.

8. Agrega Play Integrity al proyecto (C++)

Descarga el SDK

Deberás descargar y extraer el SDK de Play Core. Completa los pasos siguientes:

  1. Descarga el SDK de Play Core empaquetado en un archivo ZIP desde la página del SDK nativo de Play Core.
  2. Extrae el archivo ZIP.
  3. Asegúrate de que el directorio recién extraído tenga el nombre play-core-native-sdk y cópialo, o bien muévelo al directorio add-play-integrity-codelab/cpp/start.

Actualiza build.gradle

En Android Studio, desde el panel Project, abre el archivo build.gradle a nivel del módulo en el directorio start/app.

Agrega la siguiente línea debajo de la línea apply plugin: 'com.android.application':

def playcoreDir = file("../play-core-native-sdk")

Busca el bloque externalNativeBuild en el bloque defaultConfig y cambia la sentencia arguments dentro del bloque cmake para que coincida con lo siguiente:

                arguments "-DANDROID_STL=c++_shared",
                          "-DPLAYCORE_LOCATION=$playcoreDir"

Agrega lo siguiente dentro del bloque android al final:

    buildTypes {
        release {
            proguardFiles getDefaultProguardFile("proguard-android.txt"),
                          "proguard-rules.pro",
                          "$playcoreDir/proguard/common.pgcfg",
                          "$playcoreDir/proguard/integrity.pgcfg"
        }
    }

Agrega esta línea dentro del bloque dependencies al final:

    implementation files("$playcoreDir/playcore.aar")

Guarda los cambios. En la parte superior del archivo, aparecerá un banner que te informará que los archivos de Gradle cambiaron. Haz clic en Sync Now para volver a cargar y sincronizar el archivo.

Actualiza CMakeList.txt

En Android Studio, desde el panel Project, abre el archivo CMakeLists.txt en el directorio start/app/src/main/cpp.

Agrega las siguientes líneas debajo de los comandos find_package:

include("${PLAYCORE_LOCATION}/playcore.cmake")
add_playcore_static_library()

Busca la línea target_include_directories(game PRIVATE y agrega la siguiente línea debajo:

        ${PLAYCORE_LOCATION}/include

Busca la línea target_link_libraries(game y agrega la siguiente línea debajo:

        playcore

Guarda el archivo. En la parte superior del archivo, aparecerá un banner que te informará que cambiaron los archivos de compilación externos. Haz clic en Sync Now para volver a cargar y sincronizar el archivo.

Selecciona Make Project en el menú Build y verifica que el proyecto se compile correctamente.

9. Realiza una solicitud de integridad (C++)

Tu app obtiene información de integridad con la API de Play Integrity para solicitar un token que, luego, enviarás a tu servidor para su desencriptación y verificación. Ahora, agregarás código al proyecto para inicializar la API de Play Integrity y usarla para realizar una solicitud de integridad.

Agrega un botón de comando

En una app o un juego reales, puedes realizar una verificación de integridad antes de actividades específicas, como hacer una compra en una tienda o unirse a una sesión de juego multijugador. En este codelab, agregaremos un botón a nuestra IU para activar manualmente una verificación de integridad y llamar al servidor pasando el token de Play Integrity generado.

El proyecto del codelab contiene una clase ClientManager, que se define en los archivos de origen client_manager.cpp y client_manager.hpp. Para mayor practicidad, este archivo ya se agregó al proyecto, pero carece del código de implementación que agregarás ahora.

Para agregar el botón de la IU, primero abre el archivo demo_scene.cpp desde el panel Project de Android Studio en el directorio start/app/src/main/cpp. Primero, ubica la función DemoScene::GenerateCommandIntegrity() vacía y agrega el siguiente código:

    const auto commandResult =
            NativeEngine::GetInstance()->GetClientManager()->GetOperationResult();
    if (commandResult != ClientManager::SERVER_OPERATION_PENDING) {
        if (ImGui::Button("Call server with integrity check")) {
            DoCommandIntegrity();
        }
    }

Luego, busca la función DemoScene::DoCommandIntegrity() vacía. Agrega el siguiente código:

    ClientManager *clientManager = NativeEngine::GetInstance()->GetClientManager();
    clientManager->StartCommandIntegrity();
    mServerRandom = clientManager->GetCurrentRandomString();

Guarda el archivo. Ahora, actualizarás la clase ClientManager del ejemplo para agregar la funcionalidad real de Play Integrity.

Actualiza el archivo de encabezado de administrador

Abre el archivo client_manager.hpp desde el panel Project de Android Studio en el directorio start/app/src/main/cpp.

Incluye el siguiente archivo de encabezado para la API de Play Integrity. Para ello, agrega la siguiente línea debajo de la línea #include "util.hpp":

#include "play/integrity.h"

La clase ClientManager deberá contener referencias a los objetos IntegrityTokenRequest y IntegrityTokenResponse. Agrega las siguientes líneas al final de la definición de la clase ClientManager:

    IntegrityTokenRequest *mTokenRequest;
    IntegrityTokenResponse *mTokenResponse;

Guarda el archivo.

Inicializa y cierra Play Integrity

Desde el panel Project de Android Studio, abre el archivo client_manager.cpp en el directorio start/app/src/main/cpp.

Busca el constructor ClientManager::ClientManager(). Reemplaza la sentencia mInitialized = false; por el siguiente código:

    mTokenRequest = nullptr;
    mTokenResponse = nullptr;

    const android_app *app = NativeEngine::GetInstance()->GetAndroidApp();
    const IntegrityErrorCode errorCode = IntegrityManager_init(app->activity->vm,
                                                               app->activity->javaGameActivity);
    if (errorCode == INTEGRITY_NO_ERROR) {
        mInitialized = true;
    } else {
        mInitialized = false;
        ALOGE("Play Integrity initialization failed with error: %d", errorCode);
        ALOGE("Fatal Error: Play Integrity is unavailable and cannot be used.");
    }

Agrega el siguiente código al destructor ClientManager::~ClientManager():

    if (mInitialized) {
        IntegrityManager_destroy();
        mInitialized = false;
    }

Solicita un token de integridad

La solicitud de un token de integridad de la API de Play Integrity es una operación asíncrona. Deberás crear un objeto de solicitud de token, asignarle un valor nonce y realizar la solicitud de token. Para ello, agrega el siguiente código a la función ClientManager::StartCommandIntegrity() vacía:

    // Only one request can be in-flight at a time
    if (mStatus != CLIENT_MANAGER_REQUEST_TOKEN) {
        mResult = SERVER_OPERATION_PENDING;
        // Request a fresh random
        RequestRandom();
        if (mValidRandom) {
            GenerateNonce();
            IntegrityTokenRequest_create(&mTokenRequest);
            IntegrityTokenRequest_setNonce(mTokenRequest, mCurrentNonce.c_str());

            const IntegrityErrorCode errorCode =
                    IntegrityManager_requestIntegrityToken(mTokenRequest, &mTokenResponse);
            if (errorCode != INTEGRITY_NO_ERROR) {
                // Log the error, in a real application, for potentially
                // transient errors such as network connectivity, you should
                // add retry with an exponential backoff
                ALOGE("Play Integrity returned error: %d", errorCode);
                CleanupRequest();
                mStatus = CLIENT_MANAGER_IDLE;
            } else {
                mStatus = CLIENT_MANAGER_REQUEST_TOKEN;
            }
        }
    }

Dado que la solicitud de token opera de forma asíncrona, deberás verificar que se haya completado. La clase ClientManager tiene una función Update(), a la que se llama como parte del bucle de actualización de la app. Agrega el siguiente código a la función ClientManager::Update() para verificar el estado de la solicitud del token y procesa el resultado una vez que se complete:

    if (mStatus == CLIENT_MANAGER_REQUEST_TOKEN) {
        IntegrityResponseStatus responseStatus = INTEGRITY_RESPONSE_UNKNOWN;
        const IntegrityErrorCode errorCode =
                IntegrityTokenResponse_getStatus(mTokenResponse, &responseStatus);
        if (errorCode != INTEGRITY_NO_ERROR) {
            // Log the error, in a real application, for potentially
            // transient errors such as network connectivity, you should
            // add retry with an exponential backoff
            ALOGE("Play Integrity returned error: %d", errorCode);
            CleanupRequest();
            mStatus = CLIENT_MANAGER_IDLE;
        } else if (responseStatus == INTEGRITY_RESPONSE_COMPLETED) {
            std::string tokenString = IntegrityTokenResponse_getToken(mTokenResponse);
            SendCommandToServer(tokenString);
            CleanupRequest();
            mStatus = CLIENT_MANAGER_RESPONSE_AVAILABLE;
        }
    }

Limpia los objetos de la solicitud

Cuando hayas terminado con la solicitud de token y los objetos de respuesta, deberás indicárselo a la API de Play Integrity para que pueda destruirlos y reclamar sus recursos. Agrega el siguiente código a la función ClientManager::CleanupRequest():

    if (mTokenResponse != nullptr) {
        IntegrityTokenResponse_destroy(mTokenResponse);
        mTokenResponse = nullptr;
    }
    if (mTokenRequest != nullptr) {
        IntegrityTokenRequest_destroy(mTokenRequest);
        mTokenRequest = nullptr;
    }

Selecciona Make Project en el menú Build y verifica que el proyecto se compile correctamente.

10. Envía el token al servidor (C++)

Ahora, agregarás código para enviar a tu servidor un comando que incluya el token de integridad. También agregarás código para procesar el resultado.

Agrega el siguiente código a la función ClientManager::SendCommandToServer():

// Note that for simplicity, we are doing HTTP operations as
// synchronous blocking instead of managing them from a
// separate network thread
HTTPClient client;
std::string errorString;

// Manually construct the json payload for ServerCommand
std::string payloadString = COMMAND_JSON_PREFIX;
payloadString += TEST_COMMAND;
payloadString += COMMAND_JSON_TOKEN;
payloadString += token;
payloadString += COMMAND_JSON_SUFFIX;

auto result = client.Post(PERFORM_COMMAND_URL, payloadString, &errorString);
if (!result) {
   ALOGE("SendCommandToServer Curl reported error: %s", errorString.c_str());
   mResult = SERVER_OPERATION_NETWORK_ERROR;
} else {
   ALOGI("SendCommandToServer result: %s", (*result).c_str())
   // Preset to success, ParseResult will set a failure result if the parsing
   // errors.
   mResult = SERVER_OPERATION_SUCCESS;
   ParseResult(*result);
}

Agrega el siguiente código a la función ClientManager::ParseResult():

    bool validJson = false;
    JsonLookup jsonLookup;
    if (jsonLookup.ParseJson(resultJson)) {
        // Look for all of our needed fields in the returned json
        auto commandSuccess = jsonLookup.GetBoolValueForKey(COMMANDSUCCESS_KEY);
        if (commandSuccess) {
            auto diagnosticString = jsonLookup.GetStringValueForKey(DIAGNOSTICMESSAGE_KEY);
            if (diagnosticString) {
                auto expressString = jsonLookup.GetStringValueForKey(EXPRESSTOKEN_KEY);
                if (expressString) {
                    if (*commandSuccess) {
                        // Express token only valid if the server reports the command succeeded
                        mValidExpressToken = true;
                    } else {
                        mValidExpressToken = false;
                        mResult = SERVER_OPERATION_REJECTED_VERDICT;
                    }
                    mCurrentSummary = *diagnosticString;
                    mCurrentExpressToken = *expressString;
                    validJson = true;
                }
            }
        }
    }
    if (!validJson) {
        mResult = SERVER_OPERATION_INVALID_RESULT;
    }

Ahora, generarás un paquete de aplicación firmado y lo subirás a Play Console para probar la app.

11. Compilación y carga (C++)

Crea y configura un almacén de claves para la app

Android requiere que todas las apps tengan una firma digital con un certificado antes de que se instalen o actualicen en un dispositivo.

En este codelab, crearemos un almacén de claves para la app. Si publicas una actualización de un juego existente, reutiliza el mismo almacén de claves como lo hiciste para lanzar versiones anteriores de la app.

Crea un almacén de claves y compila un paquete de aplicación de lanzamiento

Sigue los pasos que se indican en Almacén de claves con Android Studio para crear un almacén de claves y usarlo para generar una compilación de lanzamiento del juego firmada. En Android Studio, elige Generate Signed Bundle / APK en el menú Build para iniciar el proceso de compilación. Elige la opción App Bundle cuando se te solicite que selecciones un Android App Bundle o un APK. Al final del proceso, tendrás un archivo .aab adecuado para subir a Google Play Console.

Sube el contenido a Play Console

Después de crear un archivo de paquete de aplicación, súbelo a Play Console. Se recomienda usar el segmento de pruebas internas para facilitar el acceso rápido a tu compilación.

Ejecuta la compilación de prueba

Ahora debes descargar la compilación de prueba desde Play Store y ejecutarla. Por ahora, hay un código correcto de marcador de posición en la función que enviará el token de integridad a tu servidor. Por lo tanto, iniciar una verificación de integridad debería tener éxito y mostrar la siguiente pantalla:

ef5f55d73f808791.png

¡Felicitaciones! Integraste Play Integrity en una aplicación de C++. Continúa con cualquier otro ejemplo de cliente o ve al final de este codelab.

12. Compila y ejecuta el cliente (Unity)

El proyecto de Unity de codelab se creó con Unity 2020 LTS (2020.3.31f1), pero debería ser compatible con versiones posteriores de Unity. El complemento de Play Integrity para Unity es compatible con Unity 2018 LTS y versiones posteriores.

Configuración del proyecto

Completa los pasos siguientes:

  1. Desde Unity Hub o Unity Editor, abre el proyecto de Unity ubicado en add-play-integrity-codelab/unity/start.
  2. Cuando se cargue el proyecto, selecciona Build Settings… en el menú File de Unity.
  3. En la ventana Build Settings, cambia la plataforma a Android.
  4. En la ventana Build Settings, haz clic en el Player Settings….
  5. En la ventana Project Settings, con la categoría Player seleccionada, busca la sección Settings for Android. Expande la lista Other Settings.

b994587b808c7be4.png

  1. Busca la entrada Package Name en Identification.

d036e5be73096083.png

  1. Cambia el nombre del paquete al identificador que elegiste cuando implementaste el servidor.
  2. Cierra la ventana Project Settings.
  3. Selecciona Save Project en el menú File de Unity.

Actualiza las URLs del servidor

El proyecto debe actualizarse para que apunte a las ubicaciones de las URLs en las que implementaste el servidor. Para hacerlo, sigue estos pasos:

  1. Abre el archivo PlayIntegrityController.cs de la carpeta Scripts en un IDE o editor de texto.
  2. Cambia los valores de las variables URL_GETRANDOM y URL_PERFORMCOMMAND para que apunten a tu servidor.
  3. Guarda el archivo.

El resultado debería ser similar al siguiente:

    private readonly string URL_GETRANDOM = "https://your-play-integrity-server.uc.r.appspot.com/getRandom";
    private readonly string URL_PERFORMCOMMAND = "https://your-play-integrity-server.uc.r.appspot.com/performCommand";

La URL específica variará según el nombre del proyecto y la región de Google Cloud que usaste para implementar el servidor.

Prueba la funcionalidad del servidor

Para probar la funcionalidad del servidor, ejecuta el proyecto en el editor de Unity. Completa los pasos siguientes:

  1. Abre el archivo de escena SampleScene ubicado en la carpeta Scenes.
  2. Haz clic en el botón Play del editor.
  3. Haz clic en el botón Request Random en la pantalla Game.

Después de una breve demora, el valor aleatorio debe mostrarse en la pantalla, con un aspecto similar al siguiente:

f22c56cdd2e56050.png

13. Agrega el complemento de Play Integrity al proyecto (Unity)

Descarga el complemento

En un navegador web, abre la página de versiones del repositorio de GitHub de complementos de Play Unity. Usa la versión más reciente de los complementos. Descarga com.google.play.integrity-<version>.Archivo unitypackage para Play Integrity en la lista Assets.

Instala el complemento

En la barra de menú del editor de Unity, selecciona Assets -> Import Package -> Custom Package… y abre el archivo .unitypackage que descargaste. Haz clic en el botón Import una vez que aparezca la ventana Import Unity Package.

El complemento incluye External Dependency Manager para Unity (EDM4U). EDM4U implementa una resolución de dependencia automática para los componentes de Java necesarios para usar Play Integrity. Cuando se te solicite que habilites la resolución de dependencia automática, haz clic en el botón Enable.

5bf0be9139fab036.png

Te recomendamos que uses la resolución automática. Los problemas de dependencias pueden hacer que el proyecto no se compile o no se ejecute.

14. Realiza una solicitud de integridad (Unity)

Crea una solicitud de integridad

Para crear una solicitud de integridad, sigue los pasos a continuación.

  1. Abre el archivo PlayIntegrityController.cs de la carpeta Scripts en un IDE o editor de texto.
  2. Agrega la siguiente línea al bloque de sentencias using en la parte superior del archivo:
using Google.Play.Integrity;
  1. Busca la función RunIntegrityCommand() y reemplaza la sentencia yield return null; por el siguiente código:
        // Call our server to retrieve a random number.
        yield return GetRandomRequest();
        if (!string.IsNullOrEmpty(_randomString))
        {
            // Create an instance of an integrity manager.
            var integrityManager = new IntegrityManager();

            // Request the integrity token by providing a nonce.
            var tokenRequest = new IntegrityTokenRequest(GenerateNonceString(_randomString,
                TEST_COMMAND));
            var requestIntegrityTokenOperation =
                integrityManager.RequestIntegrityToken(tokenRequest);

            // Wait for PlayAsyncOperation to complete.
            yield return requestIntegrityTokenOperation;

            // Check the resulting error code.
            if (requestIntegrityTokenOperation.Error != IntegrityErrorCode.NoError)
            {
                // Log the error, in a real application, for potentially
                // transient errors such as network connectivity, you should
                // add retry with an exponential backoff
                Debug.Log($@"IntegrityAsyncOperation failed with error: 
                    {requestIntegrityTokenOperation.Error.ToString()}");
                yield break;
            }

            // Get the response.
            var tokenResponse = requestIntegrityTokenOperation.GetResult();

            // Send the command to our server with a POST request, including the
            // token, which will be decrypted and verified on the server.
            yield return PostServerCommand(tokenResponse.Token);
        }

Envía el comando al servidor

Para seguir editando el archivo PlayIntegrityController.cs, busca la función PostServerCommand() y reemplaza la sentencia yield return null; por el siguiente código:

        // Start a HTTP POST request to the performCommand URL, sending it the
        // command and integrity token data provided by Play Integrity.
        var serverCommand = new ServerCommand(TEST_COMMAND, tokenResponse);
        var commandRequest = new UnityWebRequest(URL_PERFORMCOMMAND, "POST");
        string commandJson = JsonUtility.ToJson(serverCommand);
        byte[] jsonBuffer = Encoding.UTF8.GetBytes(commandJson);
        commandRequest.uploadHandler = new UploadHandlerRaw(jsonBuffer);
        commandRequest.downloadHandler = new DownloadHandlerBuffer();
        commandRequest.SetRequestHeader(CONTENT_TYPE, JSON_CONTENT);
        yield return commandRequest.SendWebRequest();

        if (commandRequest.result == UnityWebRequest.Result.Success)
        {
            // Parse the command result Json
            var commandResult = JsonUtility.FromJson<CommandResult>(
                commandRequest.downloadHandler.text);
            if (commandResult != null)
            {
                resultLabel.text = commandResult.diagnosticMessage;
                _expressToken = commandResult.expressToken;
                if (commandResult.commandSuccess)
                {
                    resultLabel.color = Color.green;
                    expressButton.SetActive(true);
                }
                else
                {
                    resultLabel.color = Color.black;
                    expressButton.SetActive(false);
                }
            }
            else
            {
                Debug.Log("Invalid CommandResult json");
            }
        }
        else
        {
            Debug.Log($"Web request error on processToken: {commandRequest.error}");
        }

Guarda el archivo.

15. Compilación y carga (Unity)

Usa el editor de Unity Android Keystore Manager para configurar que se firme tu compilación para subirla a Play Console.

Después de configurar la información de firma, sigue estos pasos:

  1. Selecciona Build -> Build Settings… en el menú File de Unity.
  2. Asegúrate de que se incluya SampleScene en la lista Scenes in Build.
  3. Asegúrate de que la casilla Build App Bundle (Google Play) esté marcada.
  4. Haz clic en el botón Build y asigna un nombre al archivo de exportación.

Después de crear un archivo de paquete de aplicación, súbelo a Play Console. Se recomienda usar el segmento de pruebas internas para facilitar el acceso rápido a tu compilación.

Ahora puedes descargar y también instalar tu compilación para ejecutar una verificación de integridad. Los resultados deberían parecerse a los siguientes:

fa83cdb1a700ca0b.png

¡Felicitaciones! Integraste Play Integrity en un proyecto de motor de Unity. Continúa con cualquier otro ejemplo de cliente o ve al final de este codelab.

16. Compila y ejecuta el proyecto (Kotlin)

Ejecuta Android Studio. En la ventana Welcome to Android Studio, haz clic en el botón Open y abre el proyecto de Android Studio ubicado en add-play-integrity-codelab/kotlin/start.

Actualiza el ID de aplicación

Antes de subir una compilación a Google Play, debes cambiar el ID de aplicación del valor predeterminado a uno único. Completa los pasos siguientes:

  1. En el panel Project de Android Studio, abre el archivo build.gradle del módulo PlayIntegrityCodelab.app.
  2. Busca la sentencia applicationId.
  3. Cambia com.example.google.codelab.playintegritykotlin al identificador que elegiste cuando implementaste el servidor y guarda el archivo.
  4. En la parte superior del archivo, aparecerá un banner que te informará que los archivos de Gradle cambiaron. Haz clic en Sync Now para volver a cargar y sincronizar el archivo.

Actualiza las URLs del servidor

El proyecto debe actualizarse para que apunte a la ubicación de URL en la que implementaste el servidor. Para hacerlo, sigue estos pasos:

  1. En el panel Project de Android Studio, abre el archivo IntegrityServer en start/app/src/main/java/com.example.google.codelab.playintegritykotlin/integrity.
  2. Cambia la URL de ‘https://your.play-integrity.server.com' a la URL base de tu servidor y guarda el archivo.

El resultado debería ser similar al siguiente:

    private val SERVER_URL: String = "https://your-play-integrity-server.uc.r.appspot.com"

La URL específica variará según el nombre del proyecto y la región de Google Cloud que usaste para implementar el servidor.

Compilación y ejecución

Conecta un dispositivo Android configurado para desarrollo. En Android Studio, compila el proyecto y ejecútalo en el dispositivo conectado. La app debería aparecer de la siguiente manera:

d77ca71dc209452f.png

Durante el inicio, la app llama al extremo getRandom de tu servidor y muestra el resultado. Si se produce un error, por ejemplo, hay una URL incorrecta o no funciona el servidor, se mostrará un diálogo de error. Puedes seleccionar el botón Request Random para recuperar un nuevo número aleatorio del servidor. El botón Call server with integrity check aún no realiza ninguna acción. Agregarás esa funcionalidad en las próximas secciones.

17. Agrega Play Integrity al proyecto (Kotlin)

Para agregar la biblioteca de Play Integrity y las dependencias compatibles al proyecto, sigue estos pasos:

  1. En el panel Project de Android Studio, abre el archivo build.gradle en start/app.
  2. Busca el bloque dependencies en la parte inferior del archivo.
  3. Agrega las siguientes líneas al final del bloque dependencies:
    implementation "com.google.android.play:integrity:1.0.1"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.6.1"
  1. Guarda el archivo.
  2. En la parte superior del archivo, aparecerá un banner que te informará que los archivos de Gradle cambiaron. Haz clic en Sync Now para volver a cargar y sincronizar el archivo.

El ejemplo de Kotlin usa corrutinas. La biblioteca kotlinx-coroutines-play-services agrega extensiones que facilitan el trabajo con objetos Task asíncronos de Play Integrity desde dentro de las corrutinas de Kotlin.

18. Realiza una solicitud de integridad (Kotlin)

En el panel Project de Android Studio, abre el archivo IntegrityServer en start/app/src/main/java/com.example.google.codelab.playintegritykotlin/integrity. En la parte inferior del archivo, hay una función integrityCommand vacía. La IU llama a esta función cuando se presiona el botón Call server with integrity check.

Agregarás código a la función integrityCommand para realizar las siguientes operaciones:

  1. Recuperar un número aleatorio nuevo del servidor que se usará cuando se construya un nonce para asociarlo con la verificación de integridad
  2. Llamar a la API de Play Integrity para realizar una solicitud de integridad y recibir un token de integridad que contenga los resultados
  3. Enviar el comando y el token de integridad a tu servidor a través de una solicitud POST HTTP.
  4. Procesar y mostrar los resultados

Agrega el siguiente código a la función integrityCommand vacía:

        // Set our state to working to trigger a switch to the waiting UI
        _serverState.emit(ServerState(
            ServerStatus.SERVER_STATUS_WORKING))
        // Request a fresh random from the server as part
        // of the nonce we will generate for the request
        var integrityRandom = IntegrityRandom("", 0U)
        try {
            val returnedRandom = httpClient.get<IntegrityRandom>(
                SERVER_URL + "/getRandom")
            integrityRandom = returnedRandom
        } catch (t: Throwable) {
            Log.d(TAG, "getRandom exception " + t.message)
            _serverState.emit(ServerState(ServerStatus.SERVER_STATUS_UNREACHABLE,
                IntegrityRandom("", 0U)))
        }

        // If we have a random, we are ready to request an integrity token
        if (!integrityRandom.random.isNullOrEmpty()) {
            val nonceString = GenerateNonce.GenerateNonceString(TEST_COMMAND,
                integrityRandom.random)
            // Create an instance of an IntegrityManager
            val integrityManager = IntegrityManagerFactory.create(context)

            // Use the nonce to configure a request for an integrity token
            try {
                val integrityTokenResponse: Task<IntegrityTokenResponse> =
                    integrityManager.requestIntegrityToken(
                        IntegrityTokenRequest.builder()
                            .setNonce(nonceString)
                            .build()
                    )
                // Wait for the integrity token to be generated
                integrityTokenResponse.await()
                if (integrityTokenResponse.isSuccessful && integrityTokenResponse.result != null) {
                    // Post the received token to our server
                    postCommand(integrityTokenResponse.result!!.token(), integrityRandom)
                } else {
                    Log.d(TAG, "requestIntegrityToken failed: " +
                            integrityTokenResponse.result.toString())
                    _serverState.emit(ServerState(ServerStatus.SERVER_STATUS_FAILED_TO_GET_TOKEN))
                }
            } catch (t: Throwable) {
                Log.d(TAG, "requestIntegrityToken exception " + t.message)
                _serverState.emit(ServerState(ServerStatus.SERVER_STATUS_FAILED_TO_GET_TOKEN))
            }
        }

El código para realizar la acción POST en el comando en tu servidor se dividió en una función postCommand independiente. Agrega el siguiente código a la función postCommand vacía:

        try {
            val commandResult = httpClient.post<CommandResult>(
                SERVER_URL + "/performCommand") {
                contentType(ContentType.Application.Json)
                body = ServerCommand(TEST_COMMAND, tokenString)
            }
            _serverState.emit(ServerState(ServerStatus.SERVER_STATUS_REACHABLE,
                integrityRandom,
                commandResult.diagnosticMessage,
                commandResult.commandSuccess,
                commandResult.expressToken))
        } catch (t: Throwable) {
            Log.d(TAG, "performCommand exception " + t.message)
            _serverState.emit(ServerState(ServerStatus.SERVER_STATUS_UNREACHABLE))
        }

Resuelve las importaciones faltantes y guarda el archivo.

19. Muestra los resultados (Kotlin)

Actualiza la IU con el resumen del veredicto

Actualmente, la IU muestra el texto del marcador de posición para el resumen del veredicto de integridad. Para reemplazar el marcador de posición por el resumen real, sigue estos pasos:

  1. En el panel Project de Android Studio, abre el archivo MainView.kt en start/app/src/main/java/com.example.google.codelab.playintegritykotlin/ui/main.
  2. Ve al final de la función MainUI, busca la sentencia text = "None", y reemplázala por este código:
                        text = state.serverState.serverVerdict,
  1. Resuelve las importaciones faltantes y guarda el archivo.

20. Compilación y carga (Kotlin)

Crea y configura un almacén de claves para la app

Android requiere que todas las apps tengan una firma digital con un certificado antes de que se instalen o actualicen en un dispositivo.

En este codelab, crearemos un almacén de claves para la app. Si publicas una actualización de un juego existente, reutiliza el mismo almacén de claves como lo hiciste para lanzar versiones anteriores de la app.

Crea un almacén de claves y compila un paquete de aplicación de lanzamiento

Sigue los pasos que se indican en Almacén de claves con Android Studio para crear un almacén de claves y usarlo para generar una compilación de lanzamiento del juego firmada. En Android Studio, elige Generate Signed Bundle / APK en el menú Build para iniciar el proceso de compilación. Elige la opción App Bundle cuando se te solicite que selecciones un Android App Bundle o un APK. Al final del proceso, tendrás un archivo .aab adecuado para subir a Google Play Console.

Sube el contenido a Play Console

Después de crear un archivo de paquete de aplicación, súbelo a Play Console. Se recomienda usar el segmento de pruebas internas para facilitar el acceso rápido a tu compilación.

Ejecuta la compilación de prueba

Ahora debes descargar la compilación de prueba desde Play Store y ejecutarla. La selección del botón Call server with integrity check debería tener éxito, y debería aparecer la siguiente pantalla:

3291795e192396c9.png

21. Felicitaciones

¡Felicitaciones! Agregaste correctamente Play Integrity a una aplicación para Android.

Lecturas adicionales