Send push notifications using VAPID and GCM authentication

I am trying to send a push notification to GCM, implementing VAPID in C #, but I get 400 from the server and have some problems to determine what the problem is.

I tried to follow the documents step by step, but did not have time.

ECVault

public class EcPrime256V1KeyPairVault { private readonly AsymmetricKeyParameter _privateKey; private readonly AsymmetricKeyParameter _publicKey; private readonly byte[] _privateKeyBytes; private readonly byte[] _publicKeyBytes; public EcPrime256V1KeyPairVault() { var gen = new ECKeyPairGenerator(); var secureRandom = new SecureRandom(); var curveName = X962NamedCurves.GetOid("prime256v1"); var genParam = new ECKeyGenerationParameters(curveName, secureRandom); gen.Init(genParam); var keyPair = gen.GenerateKeyPair(); _privateKey = keyPair.Private; _publicKey = keyPair.Public; _privateKeyBytes = PrivateKeyInfoFactory.CreatePrivateKeyInfo(_privateKey).ToAsn1Object().GetDerEncoded(); _publicKeyBytes = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(_publicKey).ToAsn1Object().GetEncoded(); } public AsymmetricKeyParameter PublicKey { get { return _publicKey; } } public AsymmetricKeyParameter PrivateKey { get { return _privateKey; } } public byte[] PublicKeyBytes { get { return _publicKeyBytes; } } public byte[] PrivateKeyBytes { get { return _privateKeyBytes; } } } 

I register the public key of the vault in the base64String _publicKeyBytes working service

  applicationServerKey: this.urlBase64ToUint8Array("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYcdBVrLXok3FFYu6cHwtiVHxhas9VcfHQcpYhcQtuKvWaLcrw428tsPkfJvWnXcYbGsY9ZPP2g/HeAqjeQiXzQ==") urlBase64ToUint8Array(base64String) { base64String = ''; const padding = '='.repeat((4 - base64String.length % 4) % 4); const base64 = (base64String + padding).replace(/\-/g, '+').replace(/_/g, '/'); const rawData = window.atob(base64); const outputArray = new Uint8Array(rawData.length); for (let i = 0; i < rawData.length; ++i) { outputArray[i] = rawData.charCodeAt(i); } return outputArray; } 

JsonWebToken Generator

 public static class JTWGenerator { public static string GenerateToken(object header, object payload, ICipherParameters privateKey, string algName = "SHA256withECDSA") { string jtwHeader = ToJsonBase64String(header); string jtwBody = ToJsonBase64String(payload); string jtwSignature = SignData(jtwHeader + "." + jtwBody, privateKey, algName); return jtwHeader + "." + jtwBody + "." + jtwSignature; } private static string ToJsonBase64String(object obj) { var json = new JavaScriptSerializer().Serialize(obj); byte[] bytes = Encoding.UTF8.GetBytes(json); return URLSafe.ConvertToBase64String(bytes); } private static string SignData(string value, ICipherParameters privateKey, string algName) { byte[] valueBytes = Encoding.UTF8.GetBytes(value); ISigner signer = SignerUtilities.GetSigner(algName); signer.Init(true, privateKey); signer.BlockUpdate(valueBytes, 0, valueBytes.Length); byte[] sigBytes = signer.GenerateSignature(); return URLSafe.ConvertToBase64String(sigBytes); } } 

VAPID Generator and VAPID Header

 public class VAPIDHeaders { private readonly string _authorization; private readonly string _cryptoKey; private VAPIDHeaders() { } public VAPIDHeaders(string jsonWebToken, string base64EcPublicKey) { _authorization = "Bearer " + jsonWebToken; _cryptoKey = "p256ecdsa=" + base64EcPublicKey; } public string Authorization { get { return _authorization; } } public string CryptoKey { get { return _cryptoKey; } } } public class VAPIDGenerator { private readonly string _subject; private readonly dynamic _jsonWebTokenVAPIDHeader; private readonly EcPrime256V1KeyPairVault _ecVault; private VAPIDGenerator() { } public VAPIDGenerator(string subject, EcPrime256V1KeyPairVault ecVault) { _ecVault = ecVault; _subject = subject; _jsonWebTokenVAPIDHeader = new { typ = "JWT", alg = "ES256" }; } public VAPIDHeaders GenerateHeaders(Dictionary<string, string> payload = null) { if (payload == null) { payload = new Dictionary<string, string>(); int expiration = int.MaxValue; payload.Add("sub", _subject); payload.Add("exp", expiration.ToString()); } string jsonWebToken = JTWGenerator.GenerateToken(_jsonWebTokenVAPIDHeader, payload, (ECPrivateKeyParameters) _ecVault.PrivateKey); string publicKey = URLSafe.ConvertToBase64String(_ecVault.PublicKeyBytes); return new VAPIDHeaders(jsonWebToken, publicKey); } } 

This is the end code that is trying to send a notification with the endpoint of the subscription.

 public class WebPush { private readonly EcPrime256V1KeyPairVault _ecVault; private readonly VAPIDGenerator _vapidGenerator; public WebPush() { _ecVault = new EcPrime256V1KeyPairVault(); _vapidGenerator = new VAPIDGenerator("mailto: admin@example.com ", _ecVault); } public void Notify(string endpoint) { var headers = _vapidGenerator.GenerateHeaders(); using (var client = new WebClient()) { client.Headers[HttpRequestHeader.Authorization] = headers.Authorization; client.Headers["Crypto-Key"] = headers.CryptoKey; client.Headers["TTL"] = "120"; string response = client.UploadString(endpoint, "POST"); } } } 

Here's a helper coding class

 public static class URLSafe { static readonly char[] padding = { '=' }; public static string ConvertToBase64String(byte[] bytes) { return Convert.ToBase64String(bytes).TrimEnd(padding).Replace('+', '-').Replace('/', '_'); } public static byte[] ConvertFromBase64String(string text) { string incoming = text.Replace('_', '/').Replace('-', '+'); switch (text.Length % 4) { case 2: incoming += "=="; break; case 3: incoming += "="; break; } return Convert.FromBase64String(incoming); } } 

thank you for your time

+5
source share

Source: https://habr.com/ru/post/1258072/


All Articles