이것은 Java 기반 응용 프로그램 서버에서 Android 클라이언트로 암호화 된 푸시 메시지를 전송하는 것을 단순화하는 라이브러리입니다. 자세한 내용은 아래 지침과 데모를 확인하십시오.
Maven을 사용하여 종속성을 추가하려면 :
Java 기반 서버의 경우 :
< dependency >
< groupId >com.google.capillary</ groupId >
< artifactId >lib</ artifactId >
< version >1.0.0</ version >
</ dependency >
안드로이드 :
< 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 Back Back to KitKat (API Level 19)의 암호화 기능 및 키 관리.
주요 생성 및 등록 워크 플로우.
메시지 암호화 (서버) 및 암호 해독 (클라이언트).
메시지 수정을 방지하기위한 무결성 보호.
앱을 설치 한 후 장치 잠금 장치 추가/재설정, 앱 스토리지 재설정 등 사용자와 같은 에지 케이스.
보너스로서, 개발자는 선택한 메시지를 해독하기 전에 장치를 잠금 해제하도록 요구할 수 있습니다. 여기에는 파일 기반 암호화 (FBE)를 사용하는 장치의 메시지가 포함됩니다. 암호화 된 메시지는 장치 암호화 된 장치 (DE) 스토리지에 캐시되며 메시지 암호 해독 키는 사용자 인증이 필요한 Android Keystore에 저장됩니다. 이를 통해 개발자는 민감한 컨텐츠가있는 메시지를 사용자가 장치를 잠금 해제하고 해독 할 때까지 캐시 된 양식으로 암호화 된 상태로 유지하도록 지정할 수 있습니다.
웹 푸시
Pro : IETF RFC 8291을 따라 개발자는 기존 웹 푸시 구현과 코드 및 주요 스토리지 인프라를 공유 할 수 있습니다. 웹 푸시 프로토콜은 ECDH (Elliptic-Curve Diffie-Hellman) 키 교환 알고리즘을 기반으로하며, 이는 성능 제한 장치에 대해 매우 효율적입니다. 앱 (브라우저와 달리)은 FCM을 통해 원시 웹 푸시 메시지를받을 수 없지만 프록시 구현을 통해 적절한 FCM JSON에 웹 푸시 메시지를 쉽게 포장 할 수 있으므로 경미한 수정으로 동일한 인프라를 사용할 수 있습니다.
CON : Android Keystore는 ECDH 키 작업을 지원하지 않습니다. 키는 Keystore에 저장된 RSA 키로 하이브리드 암호화되어 있으며 EC 개인 키 일반 텍스트는 암호화 작업 중에 사용자 메모리에서 사용할 수 있음을 의미합니다.
RSA-ECDSA
프로 : 하이브리드 암호화 클라이언트 생성 RSA 공개 키 (기밀성)와 함께 메시지를 암호화하고 개발자가 생성 한 ECDSA 공개 키 (무결성)와 암호문에 서명합니다. RSA 암호화 작업 (암호화, 해독)은 SDK 버전 18 (Jelly Bean) 이상의 Android Keystore에서 지원됩니다. 이는 신뢰할 수있는 실행 환경 외부에서 키 자료를 사용할 수 없습니다. 즉, 장치 메모리에 액세스 할 수있는 정교한 공격자조차도 개인 키 자료에 액세스 할 수 없습니다 (예 : 직접 부팅 모드에 도착하는 미래 메시지를 해독하기 위해).
CON : ECDH보다 효율적이지 않고 키는 웹 푸시 메시징 표준과 호환되지 않습니다.
Auth Bound Keys는 장치가 잠겨있을 때 사용자가 메시지를 읽을 수 없도록합니다. 즉, 어깨 서퍼로 민감한 컨텐츠를 읽을 수 없거나 장치가 손실되거나 도난당한 경우에 민감한 콘텐츠를 읽을 수 없습니다.
Capillary는 애플리케이션 서버에서 보내고 Android 앱에서 암호화 된 푸시 메시지를 수신하는 데 필요한 핵심 암호화 기능을 제공합니다. 여기에는 다음과 같습니다.
클라이언트에서 키를 생성하고 저장합니다.
서버에서 메시지를 암호화하고 서명합니다.
클라이언트의 암호화 된 메시지를 암호화 및 확인합니다.
장치가 잠겨있는 동안 수신 된 경우 나중에 저장 해야하는 암호화 된 메시지를 식별합니다.
서버 측 아키텍처 및 푸시 메시징 사용 사례는 다양하고 다양하기 때문에 가능한 모든 푸시 메시지 구현을 처리 할 수있는 서버 측 API를 제공하는 것은 실용적이지 않습니다. 따라서 메시지 전송 및 서버 측 키 스토리지/검색 기능에서 위의 암호화 기능을 분리했습니다. 그러나 Capillary를 사용하여 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 >
Capillary Library는 CapillaryHandler
인터페이스의 방법을 사용하여 공개 키, 해독 된 일반 텍스트 등과 같은 응답을 Android 앱으로 돌아갑니다. 따라서 안드로이드 앱을 모세관 라이브러리와 통합하는 첫 번째 단계는 위에서 언급 한 응답을 처리하기 위해 앱 특정 논리와 함께 CapillaryHandler
인터페이스를 구현하는 것입니다. Capillary Library의 Demo Android 앱이 DemoCapillaryHandler
클래스에서 CapillaryHandler
인터페이스를 구현하는 방법을 알 수 있습니다.
각 모세관 키 쌍은 키 페어 ID (일명 Keychain 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 );
단일 메소드 호출로 인증 및 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
Ciphertext가 인증 키를 사용하여 생성되었지만 Android 장치가 무단 컨텍스트에있는 경우 Capillary Library는 내부적으로 암호 텍스트를 해독 할 수 있도록 내부적으로 저장하고 Authciphertextsavedforlater 메소드를 통해 Android 앱을 알려줍니다. 이를 통해 Android 앱은 캐시 된 Ciphertexts (예 : 잠금 해제시 사용자 메시지를 사용할 수 있음)를 처리 할 수 있습니다. 장치를 잠금 해제하면 모세관 라이브러리가 저장된 Ciphertexts를 다음과 같이 해독 할 수 있습니다.
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 Broadcast 의도를 듣는 것입니다. 자세한 내용은 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 ();
모세관 라이브러리는 다음 구글러에 의해 유지됩니다.