Class WebPushEncryptor
java.lang.Object
com.iizix.server.push.engine.transport.WebPushEncryptor
Message encryption for Web Push, RFC 8291 with the
aes128gcm content coding of RFC 8188: ECDH on P-256 against the subscription's p256dh, HKDF-SHA-256 with the subscription's auth secret, one AES-128-GCM record.Rebuilt, not inherited. The severed HttpEce/AbstractPushService pair carried recorded defects; this class fixes them rather than copying them:
- IIZI-PUSH-001: a payload-less send dereferenced a null payload. Here an empty plaintext is a valid input and encrypts to a header plus one padded record.
- IIZI-PUSH-007: the salt was accepted unvalidated. Here every input length is checked before any key is derived: the receiver's public key is the 65-byte uncompressed point, the authentication secret 16 bytes, the salt 16 bytes; and the public entry point never accepts a salt or a key pair at all - it draws a fresh 16-byte salt and a fresh ephemeral key pair from
SecureRandomper message. - IIZI-PUSH-008: the padding delimiter and the record size were not related. Here the record size is fixed at 4096 and the plaintext is limited so that plaintext, the 0x02 delimiter and the 16-byte tag fit one record; a larger payload is refused before encryption with the limit in the message, because the push services reject bodies over 4096 bytes anyway.
aes128gcm is produced. The legacy aesgcm coding, which the old code kept for backward compatibility, has not been needed by any browser since Chrome 60 and Firefox 55 and is not emitted.HKDF is the RFC 5869 extract-and-expand over HmacSHA256 from the JDK; AES-GCM is the JDK cipher; only the curve arithmetic and key loading use BouncyCastle, the same routines VAPIDKeys and PushProps.verify already rely on.
Known-answer coverage: the test fragment reproduces RFC 8291 Appendix A byte for byte through the package-private encrypt(byte[],byte[],byte[],KeyPair,byte[]) entry.
- Author:
- Christopher Mindus
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final intThe authentication secret length in bytes.static final intThe length of the content coding header: salt (16) + rs (4) + idlen (1) + keyid (65).static final intThe largest plaintext that fits one record beside the delimiter and the tag, and keeps the whole body within the 4096 bytes the push services accept:4096 - HEADER_LENGTH - 1 - 16.static final intThe record size written to the content coding header: 4096 bytes.static final intThe salt length in bytes.Method Summary
Modifier and TypeMethodDescriptionstatic byte[]encrypt(byte[] plaintext, byte[] receiverPublicKey, byte[] authSecret) Encrypts a message for a subscription with a fresh salt and a fresh ephemeral key pair.static voidMakes sure the BouncyCastle provider is registered: the key loaders incom.iizix.push.vapid.Utilsname it, and the server registers it during its own start-up but a test fork does not.
Field Details
RECORD_SIZE
public static final int RECORD_SIZEThe record size written to the content coding header: 4096 bytes.- See Also:
HEADER_LENGTH
public static final int HEADER_LENGTHThe length of the content coding header: salt (16) + rs (4) + idlen (1) + keyid (65).- See Also:
MAX_PLAINTEXT
public static final int MAX_PLAINTEXTThe largest plaintext that fits one record beside the delimiter and the tag, and keeps the whole body within the 4096 bytes the push services accept:4096 - HEADER_LENGTH - 1 - 16.- See Also:
SALT_LENGTH
public static final int SALT_LENGTHThe salt length in bytes.- See Also:
AUTH_LENGTH
public static final int AUTH_LENGTHThe authentication secret length in bytes.- See Also:
Method Details
ensureProvider
public static void ensureProvider()Makes sure the BouncyCastle provider is registered: the key loaders incom.iizix.push.vapid.Utilsname it, and the server registers it during its own start-up but a test fork does not.encrypt
public static byte[] encrypt(byte[] plaintext, byte[] receiverPublicKey, byte[] authSecret) throws GeneralSecurityException Encrypts a message for a subscription with a fresh salt and a fresh ephemeral key pair.- Parameters:
plaintext- The message, at mostMAX_PLAINTEXTbytes; may be empty.receiverPublicKey- The subscription'sp256dh: the 65-byte uncompressed point.authSecret- The subscription'sauth: 16 bytes.- Returns:
- The body to POST: the content coding header followed by the single record.
- Throws:
IllegalArgumentException- If an input has the wrong length.GeneralSecurityException- For a cryptographic failure.