Class VAPIDKeys

java.lang.Object
com.iizix.push.VAPIDKeys

public class VAPIDKeys extends Object
Helper class to generate VAPID keys for use with Push Notification.
Author:
Christopher Mindus
  • Field Details

    • PUBLIC_KEY_LENGTH

      public static final int PUBLIC_KEY_LENGTH
      The length in bytes of an uncompressed P-256 public point: the 0x04 tag plus the 32-byte X and Y coordinates.
      See Also:
    • PRIVATE_KEY_LENGTH

      public static final int PRIVATE_KEY_LENGTH
      The length in bytes of a P-256 private scalar.
      See Also:
    • FINGERPRINT_LENGTH

      public static final int FINGERPRINT_LENGTH
      The length in characters of a key fingerprint, fingerprint(String): 32 bytes of SHA-256 in base64url without padding.
      See Also:
  • Constructor Details

    • VAPIDKeys

      public VAPIDKeys()
  • Method Details

    • generateKeys

      public static String[] generateKeys() throws IllegalStateException
      Generates a public key and a private key.

      The keys are the RAW forms that Web Push requires, base64url encoded without padding: the public key is the uncompressed P-256 point, 0x04 || X || Y, 65 bytes; the private key is the unsigned scalar d, 32 bytes, left-padded so its length never varies with the value.

      These are the forms Utils.loadPublicKey(String) and Utils.loadPrivateKey(String) parse, and the public key is passed unchanged to the browser's pushManager.subscribe as applicationServerKey.

      Until r19738 this method encoded getEncoded() on both keys instead - X.509 SubjectPublicKeyInfo DER for the public key, 91 bytes, and PKCS#8 DER for the private. Neither is usable: loadPublicKey calls curve.decodePoint and threw on the DER, so the Designer's "Generate new VAPID Key Pair" produced keys that this class's own validators rejected and that no browser would accept. The private key was worse than a rejection - BigIntegers.fromUnsignedByteArray read the whole DER blob as the scalar and returned a wrong key without complaint.

      Returns:
      Array of 2 String's: first the public key, then the private key.
      Throws:
      IllegalStateException - NOTE: try-catch on it! In case of BouncyCastle errors.
    • validatePublicKey

      public static String validatePublicKey(String publicKey)
      Validates the public key Base64 encoded string.

      Accepted is the base64url form of an uncompressed P-256 point, 0x04 || X || Y, 65 bytes, whose coordinates lie in the field and satisfy the curve equation y^2 = x^3 + ax + b (mod p). The equation is evaluated here, because the JDK's KeyFactory builds a public key from any pair of coordinates without checking it - see the comment in the body. A key that passes here is one the push service will verify a VAPID signature against.

      Parameters:
      publicKey - The public key to validate.
      Returns:
      A validation message, or null for success.
    • validatePrivateKey

      public static String validatePrivateKey(String privateKey)
      Validates the private key Base64 encoded string.

      Accepted is the base64url form of the P-256 private scalar d, exactly 32 bytes, whose value lies in [1, n-1] for the order n of the curve. The range is checked here, because the JDK's KeyFactory builds a private key from any scalar without checking it - see the comment in the body. A key that passes here is one the transports can sign with.

      Parameters:
      privateKey - The private key to validate.
      Returns:
      A validation message, or null for success.
    • fingerprint

      public static String fingerprint(String publicKey)
      Gets the fingerprint of a VAPID public key: the base64url encoding, without padding, of the SHA-256 of the 65-byte uncompressed point, 43 characters (PUSH-CLUSTER-DESIGN.md section 1). A subscription row records it as the key the subscription was made under (IZS.SUBSCRIPTIONS.vapid_key, B-10a).

      A public key is not a secret, and neither is its fingerprint: it is used because it is fixed-length and cheap to compare, not to hide anything. Only the encoding is checked here, not the curve - validatePublicKey(String) does that: the same point gives the same fingerprint whether its base64url text carries padding or not, and text that is not the base64url form of a 65-byte uncompressed point gives none.

      Parameters:
      publicKey - The public key, base64url, may be null.
      Returns:
      The fingerprint, or null when publicKey is null or not the base64url form of a 65-byte uncompressed point.