Il s'agit d'une bibliothèque pour simplifier l'envoi de messages push cryptés de bout en bout (E2E) des serveurs d'applications basés sur Java aux clients Android. Veuillez vérifier les instructions ci-dessous et la démo pour plus de détails.
Pour ajouter une dépendance à l'aide de maven:
Pour un serveur basé sur Java:
< dependency >
< groupId >com.google.capillary</ groupId >
< artifactId >lib</ artifactId >
< version >1.0.0</ version >
</ dependency >
Pour Android:
< dependency >
< groupId >com.google.capillary</ groupId >
< artifactId >lib-android</ artifactId >
< version >1.0.0</ version >
</ dependency >
Pour ajouter une dépendance en utilisant Gradle:
dependencies {
compile 'com.google.capillary:lib:1.0.0'
}
dependencies {
compile 'com.google.capillary:lib-android:1.0.0'
}
Pour utiliser les services de messagerie Push pour envoyer des messages à des appareils connectés, les développeurs doivent les envoyer via un service de messagerie tiers, tel que Firebase Cloud Messaging (FCM). Il est simple de crypter les contenus des messages entre le développeur et le service de messagerie à l'aide de HTTPS. Les principaux services de messagerie, y compris FCM, cryptent également les messages entre leurs serveurs et les appareils clients.
Cependant, les messages entre le serveur de développeurs et les périphériques utilisateur ne sont pas cryptés de bout en bout (E2E):
Le chiffrement E2E peut être réalisé en générant une paire de clés de chiffrement asymétrique sur le client, en enregistrant la clé publique avec le service de messagerie de développeur, en cryptant des messages sortants avec la clé publique et en décryptant des messages sur le client à l'aide de la clé privée:
Capillary gère ces opérations pour les services de messagerie push utilisés par les applications Android. Il comprend:
Fonctionnalité de crypto et gestion des clés dans toutes les versions d'Android à KitKat (API Niveau 19).
Clé des workflows de génération et d'enregistrement.
Le cryptage des messages (sur le serveur) et le décryptage (sur le client).
Protection de l'intégrité pour éviter la modification des messages.
Cases de bord, telles que les utilisateurs, ajoutant / réinitialisation le verrouillage du périphérique après l'installation de l'application, le stockage des utilisateurs de réinitialisation de l'application, etc.
En prime, il permet également aux développeurs d'exiger que les appareils soient déverrouillés avant que les messages sélectionnés puissent être décryptés. Cela inclut les messages sur les périphériques à l'aide du chiffrement basé sur des fichiers (FBE): les messages chiffrés sont mis en cache dans l'appareil crypté (DE) Les clés de décryptage des messages sont stockées dans Android Keystore nécessitant une authentification utilisateur. Cela permet aux développeurs de spécifier des messages avec du contenu sensible pour rester cryptés sous forme en cache jusqu'à ce que l'utilisateur ait déverrouillé et déchiffré leur appareil.
Push
Pro: suit l'IETF RFC 8291, permet donc aux développeurs de partager le code et l'infrastructure de stockage clé avec des implémentations de push Web existantes. Le protocole de push Web est basé sur l'algorithme d'échange de clés de la courbe elliptique (ECDH), qui est très efficace pour les appareils limités aux performances. Notez que les applications (par opposition aux navigateurs) ne peuvent pas recevoir de messages de push Web bruts via FCM, mais les messages pushs Web peuvent facilement être enveloppés dans le FCM JSON approprié par une implémentation proxy, vous permettant d'utiliser la même infrastructure avec des modifications mineures.
CON: Android Keystore ne prend pas en charge les opérations clés de l'ECDH. Les clés sont cryptées hybrides avec une clé RSA stockée dans le stage de clés, ce qui signifie que EC Private Key Text est disponible dans la mémoire de l'utilisateur pendant les opérations de crypto.
RSA-ECDSA
PRO: Hybrid-crypte un message avec une clé publique RSA générée par le client (pour la confidentialité) et signe le texte chiffré avec une clé publique ECDSA générée par les développeurs (pour l'intégrité). Les opérations de crypto RSA (Encrypt, Decrypt) sont prises en charge par Android Keystore à partir des versions SDK 18 (Jelly Bean) et supérieures, ce qui signifie que le matériel clé n'est pas disponible en dehors de l'environnement d'exécution de confiance. Cela signifie que même un attaquant sophistiqué avec accès à la mémoire de l'appareil ne peut pas accéder au matériel de clé privé (par exemple, pour décrypter les futurs messages arrivant en mode de démarrage direct).
CON: moins efficace que l'ECDH et les clés ne sont pas compatibles avec la norme de messagerie Web Push.
Auth Bound Keys garantit que les messages ne peuvent pas être lus par les utilisateurs lorsque leur appareil est verrouillé, ce qui signifie que le contenu sensible ne sera pas lisible par les surfers ou si l'appareil est perdu ou volé.
Capillary fournit la fonctionnalité de crypto de base requise pour envoyer (à partir d'un serveur d'applications) et recevoir des messages push chiffrés dans les applications Android. Cela couvre:
Générer et stocker des clés sur le client.
Crypter et signer des messages sur le serveur.
Décriture et vérification des messages chiffrés sur le client.
Identification des messages chiffrés qui doivent être stockés pour plus tard s'ils sont reçus pendant que l'appareil est verrouillé.
Étant donné que les architectures côté serveur et les cas d'utilisation de la messagerie push sont nombreux et variés, il n'est pas pratique de fournir une API côté serveur pour gérer toutes les implémentations de messages push possibles. Par conséquent, nous avons découplé la fonctionnalité cryptographique ci-dessus à partir des fonctions de stockage / de récupération des touches côté serveur. Nous avons cependant fourni une implémentation complète qui utilise Capillary pour envoyer des messages push cryptés E2E d'un serveur basé sur Java aux clients Android dans l'application de démonstration. En résumé, vous devrez mettre en œuvre vous-même les aspects suivants de la solution (en utilisant l'application de démonstration et les instructions ci-dessous pour les conseils si nécessaire):
Veuillez suivre les étapes suivantes pour vous intégrer à la bibliothèque Capillary.
Avant que la bibliothèque capillaire ne puisse être utilisée, elle doit être initialisée au moment de l'exécution comme suit:
import com . google . capillary . Config ;
Config . initialize ();
Si vous utilisez l'algorithme RSA-ECDSA, vous devez générer une paire de clés publique / privée ECDSA et mettre la clé publique à la disposition de votre application Android (par exemple, en tant que ressource brute) et une clé privée disponible pour votre serveur d'applications. Utilisez le programme d'utilité que nous avons fourni pour générer ces paires de clés 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 bibliothèque capillaire utilise des méthodes de l'interface CapillaryHandler
pour fournir des réponses, telles que des clés publiques, des textes clairs décryptés, etc., de retour à l'application Android. Par conséquent, la première étape de l'intégration d'une application Android avec la bibliothèque Capillary consiste à implémenter l'interface CapillaryHandler
avec votre logique spécifique à l'application pour gérer les réponses mentionnées ci-dessus. Vous pouvez voir comment l'application Android de démonstration de la bibliothèque Capillary implémente l'interface CapillaryHandler
dans la classe DemoCapillaryHandler
.
Chaque paire de clés capillaires est identifiée par un ID de paire de clés (AKA Keychain ID), qui est une chaîne arbitraire qui vous appartient à décider. Pour générer une paire de clés:
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 );
Il existe également une méthode GenerateKeyPairs pour générer à la fois les touches Auth et NoAuth dans un seul appel de méthode.
Après avoir généré une paire de clés capillaires, vous pouvez récupérer la clé publique générée dans un tableau d'octets comme suit:
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.
Après avoir reçu un texte chiffré généré à l'aide d'une clé publique capillaire, vous pouvez le déchiffrer comme suit:
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.
Gardez à l'esprit que lors du décryptage, la bibliothèque capillaire peut générer automatiquement les paires de clés capillaires sous-jacentes si ces paires de clés sont irrégulières, ce qui peut se produire, par exemple, lorsque l'utilisateur ajoute / réinitialise le verrouillage de l'appareil, réinitialise le stockage de l'application, etc. CapillaryHandler
Si le texte chiffré a été généré à l'aide d'une touche Auth mais que le périphérique Android est dans un contexte non authentifié, la bibliothèque capillaire enregistre en interne le texte chiffré à décrypter plus tard et informe l'application Android via la méthode AuthCiPherTexTavedForlater. Cela permet à l'application Android de gérer des texrages en cache, par exemple en disant que les messages utilisateur sont disponibles lors du déverrouillage. Lors de l'utilisateur déverrouillant l'appareil, vous pouvez faire décrypter la bibliothèque capillaire tout chiffon enregistré comme suit:
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.
Il existe plusieurs façons de déclencher le gestionnaire de texte chiffré en cache lors du déverrouillage de l'appareil. L'approche utilisée par l'application Android de démonstration de la bibliothèque Capillary consiste à écouter l'intention de diffusion Action_User_Present. Voir DeviceUnlockedBroadcastReceiver
pour plus de détails.
Pour supprimer une paire de clés capillaires:
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 bibliothèque Capillary fournit la fonctionnalité pour chiffrer les messages sur les serveurs d'applications basés sur Java.
Pour crypter un message à l'aide d'une clé publique capillaire:
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 bibliothèque capillaire est maintenue par les Googlers suivants: