5

我正在尝试向 GCM 发送推送通知,在 C# 中实现 VAPID,但我从服务器收到 400,并且在确定问题所在时遇到了一些麻烦。

我试图一步一步地遵循文档,但没有成功。

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;
        }
    }
}

我正在将保管库的公钥注册到服务工作者中,一个 base64String 的 _publicKeyBytes

 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 生成器

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 生成器和 VAPID 标头

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);
    }
}

这是结束代码,它尝试使用订阅端点发送通知

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");
        }
    }
}

这里是编码助手类

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);
    }
}

谢谢你的时间

4

0 回答 0