我正在尝试向 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);
}
}
谢谢你的时间