这是一个库,可以简化从基于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 Messaging(FCM))发送它们。使用HTTPS在开发人员和消息服务之间加密消息内容非常简单。包括FCM在内的主要消息传递服务还加密了其服务器和客户端设备之间的消息。
但是,开发人员服务器和用户设备之间的消息未加密端到端(E2E):
可以通过在客户端上生成非对称加密密钥对,在开发人员消息传输服务上注册公共密钥,使用公共密钥加密消息,并使用私有密钥上的客户在客户端上解密消息来实现E2E加密。
毛细管处理Android应用程序使用的推送消息服务的这些操作。它包括:
加密功能和键管理的所有版本的Android返回到Kitkat(API级别19)。
关键的一代和注册工作流程。
消息加密(在服务器上)和解密(在客户端上)。
完整性保护以防止消息修改。
Edge-Case,例如在安装应用程序后添加/重置设备锁定的用户,用户重置应用程序存储等。
作为奖励,它还允许开发人员要求在解密选定的消息之前解锁设备。这包括使用基于文件的加密(FBE)上的设备上的消息:在设备加密(DE)存储(DE)中,将加密消息存储在需要用户身份验证的Android密钥库中。这使开发人员可以指定具有敏感内容的消息,以保持缓存的形式加密,直到用户解锁和解密其设备为止。
网络推动
PRO:遵循IETF RFC 8291,因此允许开发人员与现有的Web推动实现共享代码和密钥存储基础结构。 Web Push协议基于椭圆形曲线差异 - 赫尔曼(ECDH)密钥交换算法,该算法对于性能受限的设备高效。请注意,应用程序(与浏览器相反)无法通过FCM接收原始的Web推动消息,但是通过代理实现,可以轻松地将Web推动消息包裹在适当的FCM JSON中,从而使您可以使用少量修改的相同基础结构。
CON: Android密钥店不支持ECDH密钥操作。密钥与密钥库中存储的RSA密钥进行混合加密,这意味着在加密操作过程中,在用户内存中可以使用EC私有密钥授权。
RSA-ECDSA
专业: Hybrid-contrypt通过客户生成的RSA公共密钥(用于机密性)的消息,并通过开发人员生成的ECDSA公共密钥(用于诚信)签名密文。 RSA加密操作(加密,解密)由SDK版本18(Jelly Bean)及更高版本的Android密钥店支持,这意味着关键材料在受信任的执行环境之外不可用。这意味着即使是具有访问设备内存的复杂攻击者也无法访问私钥材料(例如,以直接启动模式解密未来消息)。
CON:不如ECDH和键效率与Web推送消息标准不兼容。
Auth Bound Bound Keys确保用户锁定设备时无法读取消息,这意味着肩膀弯曲器或设备丢失或窃取的敏感内容将无法读取。
毛细管提供了发送(从应用程序服务器)发送并在Android应用中接收加密推送消息所需的核心加密功能。这涵盖了:
在客户端生成和存储密钥。
在服务器上加密和签名消息。
解密和验证客户端上的加密消息。
识别设备被锁定时收到的加密消息,以便以后存储。
由于服务器端体系结构和推动消息传递用例且多样化,因此提供服务器端API以处理所有可能的推送消息实现是不切实际的。因此,我们已将上述加密功能与消息传输和服务器端密钥存储/检索功能解耦。但是,我们提供了一个全堆栈实现,该实现使用毛细管将E2E加密的推送消息从基于Java的服务器发送到Demo应用程序中的Android客户端。总而言之,您将需要自己实施解决方案的以下方面(在需要时使用以下演示应用程序和指导以进行指导):
请按照以下步骤与毛细管库集成。
在使用毛细管库之前,必须在运行时初始化它,如下所示:
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应用程序与毛细管库集成的第一步是与您的应用程序特定逻辑实现CapillaryHandler
接口,以处理上述响应。您可以看到毛细管库的演示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键。
生成毛细管密钥对后,您可以在字节数组中检索生成的公共密钥,如下所示:
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应用。
如果使用auth键生成了密文,但是Android设备处于未经验证的上下文中,则毛细管库内部可以保存稍后解密的密文,并通过authciphertextsavedforlater方法通知Android App。这允许Android应用程序处理缓存的密文,例如通过告诉Unlock时可用用户消息。用户解锁设备后,您可以让毛细管库解密任何保存的密文,如下所示:
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.
有几种方法可以在设备解锁时触发缓存密文的处理程序。毛细管库的演示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 ();
毛细管库由以下Googles维护: