A lightweight webpush encryption/decryption library for kotlin/android
This library supports both the 'aes128gcm' content encoding as specified in the WebPush encryption RFC as well as the legacy 'aesgcm' encoding as defined in the draft RFC.
It is built with okio and exposes types as ByteString
and Source
. These can easily
be converted to and from other forms using the built-in functions on those types.
implemenation("me.tatarka.webpush:webpush-encryption:0.2.0")
To encrypt, construct a WebPush object by calling WebPush.encrypt()
.
val webPush = WebPush.encrypt(
authSecret = authSecret,
keys = keys,
body = body,
encoding = ContentEncoding.aes128gcm // or ContentEncoding.aesgcm
)
where:
authSecret
is the 16-byte shared auth secret.keys
is the server public/private key pair, which must be a p-256 elliptic curve (See later section on how to generate).body
is the plaintext payload to encrypt.
You can then pass along the returned WebPush with the server of your choice. It includes headers
and
the encryptedBody
.
To decrypt, construct a WebPush object using its constructor then call webPush.decrypt()
.
val webPush = WebPush(headers = headers, encryptedBody = encrypedBody)
val bodyPlainText = webPush.decrypt(
authSecret = authSecret,
keys = keys
)
where:
authSecret
is the 16-byte shared auth secret.keys
is the client public/private key pair, which must be a p-256 elliptic curve (See later section on how to generate).
The auth secret is a random set of 16 bytes. You may use the convenience method WebPush.generateAuthSecret()
or
generate them yourself. Note: It's recommended to use a secure random number generator.
Both the server and the client need to generate a P-256 elliptic curve key pair. You can do this on the jvm with:
val keyPair = KeyPairGenerator.getInstance("EC").apply {
initialize(ECGenParameterSpec("secp256r1"))
}.generateKeyPair()
or on Android storing in the AndroidKeyStore (min api 31) with:
val keyPair = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore")
.apply {
initialize(
KeyGenParameterSpec.Builder(KeyAlias, KeyProperties.PURPOSE_AGREE_KEY)
.setAlgorithmParameterSpec(ECGenParameterSpec("secp256r1"))
.build()
)
}.generateKeyPair()
You will likely want share the auth secret and public key from the client to the server. To accomplish this you may want to base64-url-encode them. You can do this with:
val authSecretBase64 = authSecret.base64Url()
val publicKeyBase64 = WebPush.encodePublicKey(publicKey).base64Url()
Only 1 record of the default size of 4096 bytes is currently supported. A plaintext payload of more than 3993 bytes using 'aes128gcm' (or 4077 bytes using 'aesgcm') will be rejected.