Esta es una biblioteca para simplificar el envío de mensajes push encriptados de extremo a extremo (E2E) de servidores de aplicaciones basados en Java a clientes de Android. Consulte las instrucciones a continuación y la demostración para obtener más detalles.
Para agregar una dependencia usando Maven:
Para un servidor basado en Java:
< dependency >
< groupId >com.google.capillary</ groupId >
< artifactId >lib</ artifactId >
< version >1.0.0</ version >
</ dependency >
Para Android:
< dependency >
< groupId >com.google.capillary</ groupId >
< artifactId >lib-android</ artifactId >
< version >1.0.0</ version >
</ dependency >
Para agregar una dependencia usando Gradle:
dependencies {
compile 'com.google.capillary:lib:1.0.0'
}
dependencies {
compile 'com.google.capillary:lib-android:1.0.0'
}
Para usar los servicios de mensajería Push para enviar mensajes a dispositivos conectados, los desarrolladores deben enviarlos a través de un servicio de mensajería de terceros, como Firebase Cloud Messaging (FCM). Es simple cifrar el contenido de mensajes entre el desarrollador y el servicio de mensajería utilizando HTTPS. Los principales servicios de mensajería, incluido FCM, también cichan mensajes entre sus servidores y dispositivos de clientes.
Sin embargo, los mensajes entre el servidor de desarrollador y los dispositivos de usuario no están encriptados de extremo a extremo (E2E):
El cifrado de E2E se puede lograr generando un par de clave de cifrado asimétrico en el cliente, registrando la clave pública con el servicio de mensajería de desarrolladores, encriptando mensajes salientes con la clave pública y descifrando mensajes en el cliente utilizando la clave privada:
Capilar maneja estas operaciones para los servicios de mensajería de empuje utilizados por Android Apps. Incluye:
Funcionalidad criptográfica y gestión de claves en todas las versiones de Android Back to Kitkat (Nivel de API 19).
Flujos de trabajo de generación de claves y registro.
Cifrado de mensajes (en el servidor) y descifrado (en el cliente).
Protección de integridad para evitar la modificación del mensaje.
Casos de borde, como los usuarios que agregan/restablecen el bloqueo del dispositivo después de instalar la aplicación, los usuarios restablecen el almacenamiento de la aplicación, etc.
Como beneficio adicional, también permite a los desarrolladores requerir que los dispositivos se desbloqueen antes de que se puedan descifrar mensajes seleccionados. Esto incluye mensajes en dispositivos que utilizan el cifrado basado en archivos (FBE): los mensajes cifrados se almacenan en caché en el almacenamiento encriptado del dispositivo (DE) y las claves de descifrado de mensajes se almacenan en la tienda de claves Android que requiere la autenticación del usuario. Esto permite a los desarrolladores especificar mensajes con contenido confidencial para permanecer encriptados en forma de almacenamiento en caché hasta que el usuario haya desbloqueado y descifrado su dispositivo.
Impulso web
PRO: Sigue el IETF RFC 8291, por lo tanto, permite a los desarrolladores compartir la infraestructura de almacenamiento de código y clave con implementaciones de push web existentes. El protocolo de push web se basa en el algoritmo de intercambio de claves de la curva elíptica (ECDH), que es altamente eficiente para los dispositivos limitados por el rendimiento. Tenga en cuenta que las aplicaciones (a diferencia de los navegadores) no pueden recibir mensajes de impulso web en bruto a través de FCM, pero los mensajes de empuje web se pueden envolver fácilmente en el FCM JSON apropiado mediante una implementación proxy, lo que le permite usar la misma infraestructura con modificaciones menores.
Con: Android Key Store no admite operaciones de teclas ECDH. Las claves están cifradas híbridas con una clave RSA almacenada en el almacén de claves, lo que significa que el texto de la formación de materia privada EC está disponible en la memoria del usuario durante las operaciones criptográficas.
RSA-ECDSA
Pro: Hybrid-cifrenta un mensaje con una clave pública RSA generada por el cliente (para confidencialidad) y firma el texto cifrado con una clave pública ECDSA generada por el desarrollador (para integridad). Las operaciones RSA Crypto (CiCrypt, Decrypt) son compatibles con Android Key Store de las versiones SDK 18 (Jelly Bean), lo que significa que el material clave no está disponible fuera del entorno de ejecución confiable. Esto significa que incluso un atacante sofisticado con acceso a la memoria del dispositivo no puede acceder al material de clave privada (por ejemplo, para descifrar mensajes futuros que llegan en modo de arranque directo).
Con: menos eficiente que ECDH y las teclas no son compatibles con el estándar de mensajería de push web.
Las teclas de Auth Bounds aseguran que los usuarios no puedan leer los mensajes cuando su dispositivo esté bloqueado, lo que significa que el contenido confidencial no será legible por las superficies de los hombros o si el dispositivo se pierde o se roba.
Capillary proporciona la funcionalidad Core Crypto requerida para enviar (desde un servidor de aplicaciones) y recibir mensajes de empuje cifrados en las aplicaciones de Android. Esto cubre:
Generar y almacenar claves en el cliente.
Cifrar y firmar mensajes en el servidor.
Descifrar y verificar mensajes encriptados en el cliente.
Identificar mensajes cifrados que deben almacenarse para más adelante si se recibe mientras el dispositivo está bloqueado.
Debido a que las arquitecturas del lado del servidor y los casos de uso de mensajes de empuje son muchas y variadas, no es práctico proporcionar una API del lado del servidor para manejar todas las implementaciones posibles de mensajes. Por lo tanto, hemos desacoplado la funcionalidad de cifrado anterior a partir de la transmisión de mensajes y las funciones de almacenamiento/recuperación de clave del lado del servidor. Sin embargo, hemos proporcionado una implementación de pila completa que utiliza capilar para enviar mensajes push cifrados de E2E de un servidor basado en Java a clientes de Android en la aplicación de demostración. En resumen, deberá implementar los siguientes aspectos de la solución usted mismo (utilizando la aplicación de demostración e instrucciones a continuación para obtener orientación cuando sea necesario):
Siga los siguientes pasos para integrarse con la biblioteca capilar.
Antes de que se pueda usar la biblioteca capilar, debe inicializarse en tiempo de ejecución de la siguiente manera:
import com . google . capillary . Config ;
Config . initialize ();
Si está utilizando el algoritmo RSA-ECDSA, debe generar un par de claves públicas/privadas ECDSA y poner la clave pública a disposición de su aplicación Android (por ejemplo, como recurso sin procesar) y la clave privada a disposición de su servidor de aplicaciones. Use el programa de utilidad que hemos proporcionado para generar tales pares de claves ECDSA:
$ ./gradlew tools:installDist
$ ./tools/build/install/tools/bin/ecdsa-key-pair-generator
> --ecdsa_public_key_path= < path to new public key >
> --ecdsa_private_key_path= < path to new private key >
La biblioteca capilar utiliza métodos de la interfaz CapillaryHandler
para proporcionar respuestas, como claves públicas, textos sin formatos descifrados, etc., de regreso a la aplicación Android. Por lo tanto, el primer paso para integrar una aplicación Android con la biblioteca capilar es implementar la interfaz CapillaryHandler
con su lógica específica de la aplicación para manejar las respuestas mencionadas anteriormente. Puede ver cómo la aplicación Demo Android de la Biblioteca Capilar implementa la interfaz CapillaryHandler
en la clase DemoCapillaryHandler
.
Cada par de claves capilares se identifica mediante una ID de par de clave (también conocido como ID de llavero), que es una cadena arbitraria que depende de usted decidir. Para generar un par de claves:
import android . content . Context ;
import com . google . capillary . android . RsaEcdsaKeyManager ;
import com . google . capillary . android . WebPushKeyManager ;
import java . io . InputStream ;
Context context = ... // The current app context.
String keychainId = ... // Some identifier for the key pair.
boolean isAuth = ... // Whether the private key usage should be guarded by the device lock.
// To generate an RSA-ECDSA key pair.
InputStream senderVerificationKey = ... // The ECDSA public key of the server.
RsaEcdsaKeyManager . getInstance ( context , keychainId , senderVerificationKey ). generateKeyPair ( isAuth );
// To generate a Web Push key pair.
WebPushKeyManager . getInstance ( context , keychainId ). generateKeyPair ( isAuth );
También hay un método GenerateKeYPairs para generar teclas Auth y NoAuth en una sola llamada de método.
Después de generar un par de claves capilar, puede recuperar la clave pública generada en una matriz de bytes de la siguiente manera:
import android . content . Context ;
import com . google . capillary . android . CapillaryHandler ;
import com . google . capillary . android . RsaEcdsaKeyManager ;
import com . google . capillary . android . WebPushKeyManager ;
import java . io . InputStream ;
Context context = ... // The current app context.
String keychainId = ... // The identifier for the key pair.
boolean isAuth = ... // Whether the private key usage is guarded by the device lock.
CapillaryHandler handler = ... // An implementation of CapillaryHandler interface.
Object extra = ... // Any extra information to be passed back to the handler.
// To obtain an RSA-ECDSA public key.
InputStream senderVerificationKey = ... // The ECDSA public key of the server.
RsaEcdsaKeyManager . getInstance ( context , keychainId , senderVerificationKey )
. getPublicKey ( isAuth , handler , extra );
// To obtain a Web Push public key.
WebPushKeyManager . getInstance ( context , keychainId ). getPublicKey ( isAuth , handler , extra );
// The Capillary library returns a byte array representing the Capillary public key via the
// handlePublicKey method of the CapillaryHandler instance.
Después de recibir un texto cifrado generado utilizando una clave pública capilar, puede descifrarla de la siguiente manera:
import android . content . Context ;
import com . google . capillary . android . CapillaryHandler ;
import com . google . capillary . android . RsaEcdsaKeyManager ;
import com . google . capillary . android . WebPushKeyManager ;
import java . io . InputStream ;
byte [] ciphertext = ... // The ciphertext received through FCM.
Context context = ... // The current app context.
String keychainId = ... // The identifier for the key pair.
CapillaryHandler handler = ... // An implementation of CapillaryHandler interface.
Object extra = ... // Any extra information to be passed back to the handler.
// To decrypt a ciphertext and pass the plaintext to the CapillaryHandler instance,
// (e.g. for display to the user):
// For RSA-ECDSA:
InputStream senderVerificationKey = ... // The ECDSA public key of the server.
RsaEcdsaKeyManager . getInstance ( context , keychainId , senderVerificationKey )
. getDecrypterManager (). decrypt ( ciphertext , handler , extra );
// For Web Push:
WebPushKeyManager . getInstance ( context , keychainId )
. getDecrypterManager (). decrypt ( ciphertext , handler , extra );
// The Capillary library returns a byte array representing the plaintext via the handleData
// method of the CapillaryHandler instance.
Tenga en cuenta que durante el descifrado, la biblioteca capilar puede volver a generar automáticamente los pares de claves capilares subyacentes si esos pares de claves están irrecuperablemente corrompidos, lo que puede suceder, por ejemplo, cuando el usuario agrega/reinicia el bloqueo del dispositivo, reinicia el almacenamiento de aplicaciones, etc. CapillaryHandler
Si el texto cifrado se ha generado utilizando una tecla Auth, pero el dispositivo Android está en un contexto no autenticado, la biblioteca capilar guarda internamente el texto cifrado para descifrar más adelante e informa la aplicación de Android a través del método AuthCipherteTextExtSavedForLater. Esto permite que la aplicación de Android maneje los texto cifrado en caché, por ejemplo, diciendo que los mensajes del usuario están disponibles al desbloquear. Tras el usuario desbloqueando el dispositivo, puede hacer que la biblioteca capilar descifra cualquier texto cifrado guardado de la siguiente manera:
import android . content . Context ;
import com . google . capillary . android . CapillaryHandler ;
import com . google . capillary . android . RsaEcdsaKeyManager ;
import com . google . capillary . android . WebPushKeyManager ;
import java . io . InputStream ;
Context context = ... // The current app context.
String keychainId = ... // The identifier for the key pair.
CapillaryHandler handler = ... // An implementation of CapillaryHandler interface.
Object extra = ... // Any extra information to be passed back to the handler.
// To decrypt saved ciphertexts and pass the plaintexts to the CapillaryHandler instance,
// (e.g. for display to the user):
// For RSA-ECDSA:
InputStream senderVerificationKey = ... // The ECDSA public key of the server.
RsaEcdsaKeyManager . getInstance ( context , keychainId , senderVerificationKey )
. getDecrypterManager (). decryptSaved ( handler , extra );
// For Web Push:
WebPushKeyManager . getInstance ( context , keychainId )
. getDecrypterManager (). decryptSaved ( handler , extra );
// For each decrypted ciphertext, the Capillary library returns a byte array representing the
// plaintext via the handleData method of the CapillaryHandler instance.
Hay varias formas de activar el manejador para el texto de cifrado en caché sobre el desbloqueo del dispositivo. El enfoque utilizado por la aplicación Demo Android de la Biblioteca Capilar es escuchar la intención de transmisión Action_user_Present. Consulte DeviceUnlockedBroadcastReceiver
para obtener más detalles.
Para eliminar un par de claves capilares:
import android . content . Context ;
import com . google . capillary . android . RsaEcdsaKeyManager ;
import com . google . capillary . android . WebPushKeyManager ;
import java . io . InputStream ;
Context context = ... // The current app context.
String keychainId = ... // The identifier for the key pair.
boolean isAuth = ... // Whether the private key usage is guarded by the device lock.
// To delete an RSA-ECDSA key pair.
InputStream senderVerificationKey = ... // The ECDSA public key of the server.
RsaEcdsaKeyManager . getInstance ( context , keychainId , senderVerificationKey ). deleteKeyPair ( isAuth );
// To delete a Web Push key pair.
WebPushKeyManager . getInstance ( context , keychainId ). deleteKeyPair ( isAuth );
La biblioteca capilar proporciona la funcionalidad para cifrar mensajes en servidores de aplicaciones basados en Java.
Para cifrar un mensaje usando una clave pública capilar:
import com . google . capillary . EncrypterManager ;
import com . google . capillary . RsaEcdsaEncrypterManager ;
import com . google . capillary . WebPushEncrypterManager ;
import java . io . InputStream ;
byte [] recipientPublicKey = ... // The Capillary public key of the client.
byte [] message = ... // The message to be sent to the client.
// To create an RSA-ECDSA ciphertext.
InputStream senderSigningKey = ... // The ECDSA private key of the server.
EncrypterManager rsaEcdsaEncrypterManager = new RsaEcdsaEncrypterManager ( senderSigningKey );
rsaEcdsaEncrypterManager . loadPublicKey ( recipientPublicKey );
byte [] ciphertext = rsaEcdsaEncrypterManager . encrypt ( message );
// This step is not strictly necessary, but it ensures that the EncrypterManager releases the
// stored public key for garbage collection.
rsaEcdsaEncrypterManager . clearPublicKey ();
// To create a Web Push ciphertext.
EncrypterManager webPushEncrypterManager = new WebPushEncrypterManager ();
webPushEncrypterManager . loadPublicKey ( recipientPublicKey );
byte [] ciphertext = webPushEncrypterManager . encrypt ( message );
webPushEncrypterManager . clearPublicKey ();
La biblioteca capilar es mantenida por los siguientes Googlers: