これは、JavaベースのアプリケーションサーバーからAndroidクライアントへのエンドツーエンド(E2E)暗号化されたプッシュメッセージの送信を簡素化するライブラリです。詳細については、以下の手順とデモを確認してください。
Mavenを使用して依存関係を追加するには:
Javaベースのサーバーの場合:
< dependency >
< groupId >com.google.capillary</ groupId >
< artifactId >lib</ artifactId >
< version >1.0.0</ version >
</ dependency >
Androidの場合:
< dependency >
< groupId >com.google.capillary</ groupId >
< artifactId >lib-android</ artifactId >
< version >1.0.0</ version >
</ dependency >
Gradleを使用して依存関係を追加するには:
dependencies {
compile 'com.google.capillary:lib:1.0.0'
}
dependencies {
compile 'com.google.capillary:lib-android:1.0.0'
}
プッシュメッセージングサービスを使用して接続されたデバイスにメッセージを送信するには、開発者はFireBase Cloudメッセージング(FCM)などのサードパーティメッセージングサービスを通じてそれらを送信する必要があります。 HTTPSを使用して、開発者とメッセージングサービスの間でメッセージの内容を暗号化するのは簡単です。 FCMを含む主要なメッセージングサービスも、サーバーとクライアントデバイス間のメッセージを暗号化します。
ただし、開発者サーバーとユーザーデバイスの間のメッセージは、エンドツーエンド(E2E)を暗号化されていません。
E2E暗号化は、クライアントに非対称暗号化キーペアを生成し、開発者メッセージングサービスに公開キーを登録し、公開メッセージを公開キーで暗号化し、秘密鍵を使用してクライアントのメッセージを復号化することで達成できます。
Capillaryは、Androidアプリが使用するプッシュメッセージングサービスのためにこれらの操作を処理します。それは以下を含みます:
AndroidのすべてのバージョンにわたるCrypto機能と主要な管理は、Kitkat(APIレベル19)に戻ります。
キー生成および登録ワークフロー。
メッセージ暗号化(サーバー上)と復号化(クライアント上)。
メッセージの変更を防ぐための整合性保護。
アプリのインストール後にデバイスロックを追加/リセットするユーザー、ユーザーがアプリストレージをリセットするなどのエッジケース。
ボーナスとして、選択したメッセージを解読する前に、開発者がデバイスをロック解除することを要求することもできます。これには、ファイルベースの暗号化(FBE)を使用したデバイス上のメッセージが含まれます。暗号化されたメッセージは、暗号化された(DE)ストレージ(DE)ストレージ(ユーザー認証を必要とするAndroidキーストア)に保存されます。これにより、開発者は、ユーザーがデバイスのロックを解除して復号化するまで、キャッシュされた形で暗号化されたままであるように、機密コンテンツのメッセージを指定することができます。
ウェブプッシュ
Pro: IETF RFC 8291に従って、開発者は既存のWebプッシュ実装とコードとキーストレージインフラストラクチャを共有できます。 Web Pushプロトコルは、パフォーマンスに制約のあるデバイスに非常に効率的なElliptic-Curve Diffie-Hellman(ECDH)キーエクスチェンジアルゴリズムに基づいています。アプリ(ブラウザとは対照的に)はFCMを介して生のWebプッシュメッセージを受信できませんが、プロキシ実装によりWebプッシュメッセージを適切なFCM JSONに簡単にラップでき、マイナーな変更で同じインフラストラクチャを使用できることに注意してください。
CON: Android KeystoreはECDHキー操作をサポートしていません。キーは、Keystoreに保存されたRSAキーでハイブリッド暗号化されています。つまり、Crypto操作中にEC秘密キープレーンテキストがユーザーメモリで利用可能です。
RSA-ECDSA
PRO:ハイブリッドは、クライアントが生成したRSA公開キー(機密性のため)でメッセージを結成し、開発者が生成したECDSA公開キー(整合性のため)で暗号文に署名します。 RSA Crypto Operations(暗号化、復号化)は、SDKバージョン18(Jelly Bean)以上のAndroid Keystoreによってサポートされています。つまり、キーマテリアルは信頼できる実行環境以外では利用できません。これは、デバイスメモリにアクセスできる洗練された攻撃者でさえ、秘密のキーマテリアルにアクセスできないことを意味します(たとえば、直接ブートモードに到着する将来のメッセージを復号化するため)。
CON: ECDHよりも効率が低く、キーはWebプッシュメッセージング標準と互換性がありません。
Auth Bound Keysは、デバイスがロックされている場合、ユーザーがメッセージをユーザーが読み取ることができないことを保証します。つまり、機密コンテンツは肩のサーファーによって読み取られません。
Capillaryは、(アプリケーションサーバーから)送信するために必要なコアクリプト機能を提供し、Androidアプリで暗号化されたプッシュメッセージを受信します。これはカバー:
クライアントにキーを生成および保存します。
サーバー上のメッセージの暗号化と署名。
暗号化されたメッセージをクライアントに復号化および検証します。
デバイスがロックされているときに受信した場合に後で保存する必要がある暗号化されたメッセージを識別します。
サーバー側のアーキテクチャとプッシュメッセージングのユースケースは多様であるため、可能なすべてのプッシュメッセージの実装を処理するためにサーバー側のAPIを提供することは実用的ではありません。したがって、メッセージの送信およびサーバー側のキーストレージ/検索機能から上記の暗号機能を分離しました。ただし、キャピラリーを使用して、DEMOアプリケーションでJavaベースのサーバーからAndroidクライアントにE2E暗号化されたプッシュメッセージを送信するフルスタックの実装を提供しました。要約すると、ソリューションの次の側面を自分で実装する必要があります(必要に応じてガイダンスのために以下のデモアプリケーションと手順を使用してください):
次の手順に従って、毛細管図書館と統合してください。
毛細管ライブラリを使用する前に、次のように実行時に初期化する必要があります。
import com . google . capillary . Config ;
Config . initialize ();
RSA-ECDSAアルゴリズムを使用している場合は、ECDSAパブリック/プライベートキーペアを生成し、Androidアプリ(例えば、生リソースとして)で公開キーを使用できるようにする必要があります。このような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 >
キャピラリーライブラリは、 CapillaryHandler
インターフェイスの方法を使用して、パブリックキー、復号化された平文などの応答をAndroidアプリに戻します。したがって、AndroidアプリをCapillary Libraryと統合する最初のステップは、上記の応答を処理するためにアプリ固有のロジックを使用してCapillaryHandler
インターフェイスを実装することです。 Capillary LibraryのデモAndroidアプリが、 DemoCapillaryHandler
クラスのCapillaryHandler
インターフェイスをどのように実装するかを確認できます。
各キャピラリーキーペアは、キーペアID(別名キーチェーンID)によって識別されます。これは、決定する任意の文字列です。キーペアを生成するには:
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 );
単一のメソッド呼び出しでAUTHキーとNOAUTHキーの両方を生成するGenerateKeypairsメソッドもあります。
毛細管キーペアを生成した後、次のように、バイト配列の生成された公開キーを取得できます。
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.
キャピラリー公開キーを使用して生成された暗号文を受け取った後、次のように復号化できます。
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.
復号化中に、毛細管図書館は、これらのキーペアが非回復的に破損している場合、ユーザーがデバイスロックを追加/リセットし、アプリストレージなどをリセットするときに発生する可能性がある場合、基礎となる毛細管キーペアを自動的に再生成する可能性があることに留意してください。 。キーの再生をトリガーした毛細血管の暗号バイトとともに、このような新たに生成された公開キーは、 CapillaryHandler
の適切な方法を介してAndroidアプリに渡されます。
ciphertextがAuthキーを使用して生成されたが、Androidデバイスが認証されていないコンテキストである場合、Capillary Libraryは後でdecryptedを復号化するようにciphertextを内部的に保存し、authciphertextsavedforlaterメソッドを介してAndroidアプリを通知します。これにより、Androidアプリは、ユーザーメッセージがロック解除時に使用可能であることを伝えることにより、キャッシュされた暗号文を処理できます。ユーザーがデバイスのロックを解除すると、キャピラリーライブラリに次のように保存された暗号文を復号化できます。
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.
デバイスのロック解除時に、キャッシュされた暗号文のハンドラーをトリガーする方法はいくつかあります。 Capillary LibraryのDemo Androidアプリで使用されるアプローチは、Action_User_Presentブロードキャストの意図を聞くことです。詳細については、 DeviceUnlockedBroadcastReceiver
を参照してください。
キャピラリーキーペアを削除するには:
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 );
キャピラリーライブラリは、Javaベースのアプリケーションサーバーでメッセージを暗号化する機能を提供します。
毛細管の公開キーを使用してメッセージを暗号化するには:
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 ();
毛細血管ライブラリは、次のGooglerによって維持されています。