Je travaille sur une implémentation java pure pour WebPush avec VAPID et le chiffrement de charge utile (j'ai déjà fait des implémentations pour GCM et FCM). Cependant, la documentation est encore marginale et les exemples de code ne sont toujours pas importants. En ce moment j'essaye de l'obtenir pour fonctionner dans Chrome. Bien que je reçois des abonnements réussis en utilisant VAPID, quand j'envoie un Tickle ou un message Push Payload je reçois un 400 UnauthorizedRegistration. Je suppose que cela a quelque chose à voir avec l'en-tête d'autorisation ou l'en-tête Crypto-Key. C'est ce que j'envoie à ce jour pour une Tickle (Une notification push sans charge utile):[WebPush] La requête [VAPID] échoue avec 400 UnauthorizedRegistration
URL: https://fcm.googleapis.com/fcm/send/xxxxx:xxxxxxxxxxx...
Action: POST/PUT (Both give same result)
With headers:
Authorization: Bearer URLBase64(JWT_HEAD).URLBase64(JWT_Payload).SIGN
Crypto-Key: p265ecdsa=X9.62(PublicKey)
Content-Type: "text/plain;charset=utf8"
Content-Length: 0
TTL: 120
JWT_HEAD="{\"typ\":\"JWT\",\"alg\":\"ES256\"}"
JWT_Payload={
aud: "https://fcm.googleapis.com",
exp: (System.currentTimeMillis()/1000) + (60 * 60 * 12)),
sub: "mailto:[email protected]"
}
SIGN = the "SHA256withECDSA" signature algorithm over: "URLBase64(JWT_HEAD).URLBase64(JWT_Payload)"
J'ai dépouillé les espaces blancs des deux années JSON dans le JWT puisque la spécification est pas très clair sur l'utilisation des espaces que semblait la chose la plus sûre à faire. La signature valide après avoir décodé à nouveau le x9.62 vers ECPoint, de sorte que publicKey semble valablement codé. Cependant, je continue à obtenir la réponse:
<HTML><HEAD><TITLE>UnauthorizedRegistration</TITLE></HEAD><BODY BGCOLOR="#FFFFFF" TEXT="#000000"><H1>UnauthorizedRegistration</H1><H2>Error 400</H2></BODY></HTML>
Selon la documentation FCM cela ne happends lorsqu'une erreur JSON se produit, mais je me sens la spécification ne couvre pas WebPush du tout. Pour l'instant j'ai tous les deux essayé la construction dans Java Crypto fournisseurs et BC tous les deux produisent les mêmes résultats.
Quelques extraits de code pour la clarification:
KeyGeneration:
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC", "BC");
ECGenParameterSpec spec = new ECGenParameterSpec("secp256r1");
keyGen.initialize(spec, secureRandom);
KeyPair vapidPair = keyGen.generateKeyPair();
ecPublicKey à X9.62:
public byte[] toUncompressedPoint(ECPublicKey publicKey){
final ECPoint publicPoint = publicKey.getW();
final int keySizeBytes = (publicKey.getParams().getOrder().bitLength() + Byte.SIZE - 1)/Byte.SIZE;
final byte[] x = publicPoint.getAffineX().toByteArray();
final byte[] y = publicPoint.getAffineY().toByteArray();
final byte[] res = new byte[1 + 2 * keySizeBytes];
int offset = 0;
res[offset++] = 0x04; //Indicating no key compression is used
if(x.length <= keySizeBytes)
System.arraycopy(x, 0, res, offset + keySizeBytes - x.length, x.length);
else if(x.length == keySizeBytes + 1) System.arraycopy(x, 1, res, offset, keySizeBytes);
else throw new IllegalArgumentException("X value is too large!");
offset += keySizeBytes;
if(y.length <= keySizeBytes)
System.arraycopy(y, 0, res, offset + keySizeBytes - y.length, y.length);
else if(y.length == keySizeBytes + 1 && y[0] == 0) System.arraycopy(y, 1, res, offset, keySizeBytes);
else throw new IllegalArgumentException("Y value is too large!");
return res;
}
Signer la demande JWT:
ObjectNode claim = om.createObjectNode();
claim.put("aud", host);
claim.put("exp", (System.currentTimeMillis()/1000) + (60 * 60 * 12));
claim.put("sub", "mailto:[email protected]");
String claimString = claim.toString();
String encHeader = URLBase64.encodeString(VAPID_HEADER, false);
String encPayload = URLBase64.encodeString(claimString, false);
String vapid = null;
ECPublicKey pubKey = (ECPublicKey) vapidPair.getPublic();
byte[] point = toUncompressedPoint(pubKey);
String vapidKey = URLBase64.encodeToString(point, false);
try{
Signature dsa = Signature.getInstance("SHA256withECDSA", "BC");
dsa.initSign(vapidPair.getPrivate());
dsa.update((encHeader + "." + encPayload).getBytes(StandardCharsets.US_ASCII));
byte[] signature = dsa.sign();
vapid = encHeader + "." + encPayload + "." + URLBase64.encodeToString(signature, false);
Certains questions qui résident dans mon esprit:
quel est le champ auth pour dans la réponse d'enregistrement JSON? Puisque à ma connaissance pour le cryptage seul le p256dh est utilisé pour générer les clés de cryptage avec un serveur basé sur KeyPair.
Des recherches plus approfondies du projet ietf 03 m'a donné la réponse à la section: 2.3 Lien: https://tools.ietf.org/html/draft-ietf-webpush-encryption-03 également le lien dans la réponse de Vincent Cheung donne une bonne explication
La documentation parle de différents usages d'en-tête pour mièvres à l'aide Bearer/WebPush et en utilisant l'en-tête Crypto-Key ou l'en-tête Encryption-Key. Wat est la bonne chose à faire?
- Des idées pour lesquelles le serveur FCM continue à renvoyer un: 400 enregistrement non autorisé?
Quelqu'un peut-il ajouter le tag VAPID à cette question? Cela ne semble pas encore exister.
Pendant ce temps, j'ai fait quelques tests en utilisant le https://web-push-libs.github.io/vapid/js/ Une découverte intéressante est que même si le signataire, la signature et le ECPoint x et y les valeurs sont égales à ce que j'ai côté serveur pour signer le JWT. Même ainsi, la validation de la signature de la page de test insipide donne faux. Pourrait-il être que l'implémentation Java (à la fois soleil et BC) de "SHA256withECDSA" n'est pas compatible avec le Javascript: {name: "ECDSA", appelé Curve: "P-256", hash: {name: "SHA- 256 "}}; –
Juste un peu plus loin, maintenant besoin de refaire certains des anciens tests avec ordre et ainsi de suite.Il semble que l'encodage de la signature Java soit en ASN.1 DER et le format pour le JWT est concaténé R + S. Ceci explique l'échec de la validation de la signature dans mon dernier commentaire. Cependant, même avec cela changé, je reçois toujours 400 réponses UnauthorizedRegistration. –
@bruha merci d'ajouter l'étiquette insipide! –