C# 在.net中是否有WebPush有效负载加密的方法?

C# 在.net中是否有WebPush有效负载加密的方法?,c#,.net,vb.net,push-notification,web-push,C#,.net,Vb.net,Push Notification,Web Push,我设法通过类似的方法向chrome和firefox发送空推送通知,尽管我试图使我的通知更详细,但是我找不到使用.net作为后端的详细webpush通知示例 我的firefox示例如下: Shared Function sendPushFox(ByVal value As String) As String Dim toret As String = "" Dim query As String = "SELECT subscribeid FROM custom_user_data

我设法通过类似的方法向chrome和firefox发送空推送通知,尽管我试图使我的通知更详细,但是我找不到使用.net作为后端的详细webpush通知示例

我的firefox示例如下:

Shared Function sendPushFox(ByVal value As String) As String
    Dim toret As String = ""
    Dim query As String = "SELECT subscribeid FROM custom_user_data WHERE NOT subscribeid = ' ';"
    Dim connection As New MySqlConnection(Utils.connectionString) : connection.Open()
    Dim command As MySqlCommand = New MySqlCommand(query, connection)
    Dim reader As MySqlDataReader = command.ExecuteReader()
    Dim regList As New List(Of String)
    Do While reader.Read
        regList.Add(reader.GetString(0))
    Loop
    Dim query2 As String = "SELECT p256dh FROM custom_user_data WHERE NOT p256dh = ' ';"
    Dim connection2 As New MySqlConnection(Utils.connectionString) : connection2.Open()
    Dim command2 As MySqlCommand = New MySqlCommand(query2, connection2)
    Dim reader2 As MySqlDataReader = command2.ExecuteReader()
    Dim regList2 As New List(Of String)
    Do While reader.Read
        regList2.Add(reader.GetString(0))
    Loop
    Dim query3 As String = "SELECT authsecret FROM custom_user_data WHERE NOT authsecret = ' ';"
    Dim connection3 As New MySqlConnection(Utils.connectionString) : connection3.Open()
    Dim command3 As MySqlCommand = New MySqlCommand(query3, connection3)
    Dim reader3 As MySqlDataReader = command3.ExecuteReader()
    Dim regList3 As New List(Of String)
    Do While reader.Read
        regList3.Add(reader.GetString(0))
    Loop
    Dim reg1 = regList.ToArray
    Dim reg2 = regList2.ToArray
    Dim reg3 = regList3.ToArray
    For Each Element As String In reg1
        Try
            Dim tRequest As WebRequest
            tRequest = WebRequest.Create("https://updates.push.services.mozilla.com/push/v1/" & Element)
            tRequest.Method = "post"
            tRequest.ContentType = " application/json"
            tRequest.Headers.Add("TTL: 1800")
            tRequest.Headers.Add("payload: " + value)
            For Each p25key As String In reg2
                tRequest.Headers.Add("userPublicKey: " + p25key)
            Next
            For Each authkey As String In reg3
                tRequest.Headers.Add("userAuth: " + authkey)
            Next
            Dim dataStream As Stream = tRequest.GetRequestStream()
            Dim tResponse As WebResponse = tRequest.GetResponse()
            dataStream = tResponse.GetResponseStream()
            Dim tReader As New StreamReader(dataStream)
            Dim sResponseFromServer As [String] = tReader.ReadToEnd()
            toret = sResponseFromServer
            tReader.Close()
            dataStream.Close()
            tResponse.Close()
        Catch ex As Exception
            Console.WriteLine(ex.Message)
            Continue For
        End Try
    Next
    Return toret
End Function
userPublicKey或userAuth现在都没有实际使用,如果没有有效负载的加密,就没有任何用途,所以我已经阅读了,但是使用vb.net,没有.net库可以向web平台(chrome和FF浏览器)发送推送通知,而且我在任何地方都找不到示例,所以我有点卡住了


如您所见,我已将每个客户端的端点、p256dh和auth保存到mysql数据库中,但从那时起,我一直无法取得进展。

似乎有人找到了这样做的方法。从以下位置复制解决方案:

/*
*为Windows 10上的.NET Core 1.0构建,带有Portable.BouncyCastle v1.8.1.1
* 
*在Chrome v53.0.2785.113 m(64位)和Firefox 48.0.2上测试
* 
*非常感谢Peter Beverloo所做的以下工作:
* https://docs.google.com/document/d/1_kWRLJHRYN0KH73WipFyfIXI1UzZ5IyOYSs-y_mLxEE/
* https://tests.peter.sh/push-encryption-verifier/
* 
*一些更有用的链接:
* https://developers.google.com/web/updates/2016/03/web-push-encryption?hl=en
* https://github.com/web-push-libs/web-push/blob/master/src/index.js
* 
*版权所有(C)2016 BravoTango86
*
*根据Apache许可证2.0版(以下简称“许可证”)获得许可;
*除非遵守许可证,否则不得使用此文件。
*您可以通过以下方式获得许可证副本:
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
*除非适用法律要求或书面同意,软件
*根据许可证进行的分发是按“原样”进行分发的,
*无任何明示或暗示的保证或条件。
*请参阅许可证以了解管理权限和权限的特定语言
*许可证下的限制。
*/
使用Microsoft.AspNetCore.WebUtilities;
使用Org.BouncyCastle.Asn1.X9;
使用Org.BouncyCastle.Crypto;
使用Org.BouncyCastle.Crypto.Agreement;
使用Org.BouncyCastle.Crypto.Generators;
使用Org.BouncyCastle.Crypto.Parameters;
使用Org.BouncyCastle.Math;
使用Org.BouncyCastle.Security;
使用制度;
使用System.Collections.Generic;
Net系统;
使用System.Net.Http;
使用System.Net.Http.Header;
使用系统文本;
公共类WebPushHelper{
私有常量字符串FirebaseServerKey=“”;
公共静态bool SendNotification(JsonSubscription子,字节[]数据,int ttl=0,ushort padding=0,
bool randomisepading=false){
返回SendNotification(端点:sub.endpoint,
数据:数据,
userKey:WebEncoders.Base64UrlDecode(sub.keys[“p256dh”]),
userSecret:WebEncoders.Base64UrlDecode(sub.keys[“auth”]),
ttl:ttl,
填充:填充,
randomisePadding:randomisePadding);
}
公共静态bool SendNotification(字符串端点、字符串数据、字符串userKey、字符串userSecret、,
int ttl=0,ushort padding=0,bool randomisepdding=false){
返回SendNotification(端点:端点,
数据:Encoding.UTF8.GetBytes(数据),
userKey:WebEncoders.Base64UrlDecode(userKey),
userSecret:WebEncoders.Base64UrlDecode(userSecret),
ttl:ttl,
填充:填充,
randomisePadding:randomisePadding);
}
公共静态bool SendNotification(字符串端点,字节[]userKey,字节[]userSecret,字节[]data=null,
int ttl=0,ushort padding=0,bool randomisepdding=false){
HttpRequestMessage请求=新的HttpRequestMessage(HttpMethod.Post,端点);
if(endpoint.StartsWith(“https://android.googleapis.com/gcm/send/"))
Request.Headers.tryadd未经验证(“授权”、“密钥=“+FirebaseServerKey”);
Add(“TTL”,TTL.ToString());
如果(数据!=null&&userKey!=null&&userSecret!=null){
EncryptionResult Package=EncryptMessage(用户密钥、用户机密、数据、填充、随机填充);
Request.Content=newbytearraycontent(Package.Payload);
Request.Content.Headers.ContentType=新的MediaTypeHeaderValue(“应用程序/八位字节流”);
Request.Content.Headers.ContentLength=Package.Payload.Length;
Request.Content.Headers.ContentEncoding.Add(“aesgcm”);
Add(“加密密钥”,“keyid=p256dh;dh=“+WebEncoders.Base64UrlEncode(Package.PublicKey));
Add(“Encryption”,“keyid=p256dh;salt=“+WebEncoders.Base64UrlEncode(Package.salt));
}
使用(HttpClient HC=new HttpClient()){
返回HC.sendsync(Request.Result.StatusCode==HttpStatusCode.Created;
}
}
公共静态加密结果加密消息(字节[]用户密钥,字节[]用户密钥,字节[]数据,
ushort padding=0,bool randomisepdding=false){
SecureRandom=新的SecureRandom();
字节[]Salt=新字节[16];
随机。下一个字节(盐);
X9ECParameters Curve=ECNamedCurveTable.GetByName(“prime256v1”);
ECDomainParameters Spec=新的ECDomainParameters(Curve.Curve、Curve.G、Curve.N、Curve.H、Curve.GetSeed());
ECKeyPairGenerator生成器=新的ECKeyPairGenerator();
Init(新的ECKeyGenerationParameters(Spec,newSecureRandom());
AsymmetrichipherKeyPair密钥对=Generator.GenerateKey
/* 
 * Built for .NET Core 1.0 on Windows 10 with Portable.BouncyCastle v1.8.1.1
 * 
 * Tested on Chrome v53.0.2785.113 m (64-bit) and Firefox 48.0.2
 * 
 * Massive thanks to Peter Beverloo for the following:
 * https://docs.google.com/document/d/1_kWRLJHRYN0KH73WipFyfIXI1UzZ5IyOYSs-y_mLxEE/
 * https://tests.peter.sh/push-encryption-verifier/
 * 
 * Some more useful links:
 * https://developers.google.com/web/updates/2016/03/web-push-encryption?hl=en
 * https://github.com/web-push-libs/web-push/blob/master/src/index.js
 * 
 * Copyright (C) 2016 BravoTango86
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using Microsoft.AspNetCore.WebUtilities;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Agreement;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;

public class WebPushHelper {

    private const string FirebaseServerKey = "";

    public static bool SendNotification(JsonSubscription sub, byte[] data, int ttl = 0, ushort padding = 0,
                                        bool randomisePadding = false) {
        return SendNotification(endpoint: sub.endpoint,
                                data: data,
                                userKey: WebEncoders.Base64UrlDecode(sub.keys["p256dh"]),
                                userSecret: WebEncoders.Base64UrlDecode(sub.keys["auth"]),
                                ttl: ttl,
                                padding: padding,
                                randomisePadding: randomisePadding);
    }

    public static bool SendNotification(string endpoint, string data, string userKey, string userSecret,
                                        int ttl = 0, ushort padding = 0, bool randomisePadding = false) {
        return SendNotification(endpoint: endpoint,
                                data: Encoding.UTF8.GetBytes(data),
                                userKey: WebEncoders.Base64UrlDecode(userKey),
                                userSecret: WebEncoders.Base64UrlDecode(userSecret),
                                ttl: ttl,
                                padding: padding,
                                randomisePadding: randomisePadding);
    }

    public static bool SendNotification(string endpoint, byte[] userKey, byte[] userSecret, byte[] data = null,
                                    int ttl = 0, ushort padding = 0, bool randomisePadding = false) {
        HttpRequestMessage Request = new HttpRequestMessage(HttpMethod.Post, endpoint);
        if (endpoint.StartsWith("https://android.googleapis.com/gcm/send/"))
            Request.Headers.TryAddWithoutValidation("Authorization", "key=" + FirebaseServerKey);
        Request.Headers.Add("TTL", ttl.ToString());
        if (data != null && userKey != null && userSecret != null) {
            EncryptionResult Package = EncryptMessage(userKey, userSecret, data, padding, randomisePadding);
            Request.Content = new ByteArrayContent(Package.Payload);
            Request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
            Request.Content.Headers.ContentLength = Package.Payload.Length;
            Request.Content.Headers.ContentEncoding.Add("aesgcm");
            Request.Headers.Add("Crypto-Key", "keyid=p256dh;dh=" + WebEncoders.Base64UrlEncode(Package.PublicKey));
            Request.Headers.Add("Encryption", "keyid=p256dh;salt=" + WebEncoders.Base64UrlEncode(Package.Salt));
        }
        using (HttpClient HC = new HttpClient()) {
            return HC.SendAsync(Request).Result.StatusCode == HttpStatusCode.Created;
        }
    }

    public static EncryptionResult EncryptMessage(byte[] userKey, byte[] userSecret, byte[] data,
                                                  ushort padding = 0, bool randomisePadding = false) {
        SecureRandom Random = new SecureRandom();
        byte[] Salt = new byte[16];
        Random.NextBytes(Salt);
        X9ECParameters Curve = ECNamedCurveTable.GetByName("prime256v1");
        ECDomainParameters Spec = new ECDomainParameters(Curve.Curve, Curve.G, Curve.N, Curve.H, Curve.GetSeed());
        ECKeyPairGenerator Generator = new ECKeyPairGenerator();
        Generator.Init(new ECKeyGenerationParameters(Spec, new SecureRandom()));
        AsymmetricCipherKeyPair KeyPair = Generator.GenerateKeyPair();
        ECDHBasicAgreement AgreementGenerator = new ECDHBasicAgreement();
        AgreementGenerator.Init(KeyPair.Private);
        BigInteger IKM = AgreementGenerator.CalculateAgreement(new ECPublicKeyParameters(Spec.Curve.DecodePoint(userKey), Spec));
        byte[] PRK = GenerateHKDF(userSecret, IKM.ToByteArrayUnsigned(), Encoding.UTF8.GetBytes("Content-Encoding: auth\0"), 32);
        byte[] PublicKey = ((ECPublicKeyParameters)KeyPair.Public).Q.GetEncoded(false);
        byte[] CEK = GenerateHKDF(Salt, PRK, CreateInfoChunk("aesgcm", userKey, PublicKey), 16);
        byte[] Nonce = GenerateHKDF(Salt, PRK, CreateInfoChunk("nonce", userKey, PublicKey), 12);
        if (randomisePadding && padding > 0) padding = Convert.ToUInt16(Math.Abs(Random.NextInt()) % (padding + 1));
        byte[] Input = new byte[padding + 2 + data.Length];
        Buffer.BlockCopy(ConvertInt(padding), 0, Input, 0, 2);
        Buffer.BlockCopy(data, 0, Input, padding + 2, data.Length);
        IBufferedCipher Cipher = CipherUtilities.GetCipher("AES/GCM/NoPadding");
        Cipher.Init(true, new AeadParameters(new KeyParameter(CEK), 128, Nonce));
        byte[] Message = new byte[Cipher.GetOutputSize(Input.Length)];
        Cipher.DoFinal(Input, 0, Input.Length, Message, 0);
        return new EncryptionResult() { Salt = Salt, Payload = Message, PublicKey = PublicKey };
    }

    public class EncryptionResult {
        public byte[] PublicKey { get; set; }
        public byte[] Payload { get; set; }
        public byte[] Salt { get; set; }
    }

    public class JsonSubscription {
        public string endpoint { get; set; }
        public Dictionary<string, string> keys { get; set; }
    }

    public static byte[] ConvertInt(int number) {
        byte[] Output = BitConverter.GetBytes(Convert.ToUInt16(number));
        if (BitConverter.IsLittleEndian) Array.Reverse(Output);
        return Output;
    }

    public static byte[] CreateInfoChunk(string type, byte[] recipientPublicKey, byte[] senderPublicKey) {
        List<byte> Output = new List<byte>();
        Output.AddRange(Encoding.UTF8.GetBytes($"Content-Encoding: {type}\0P-256\0"));
        Output.AddRange(ConvertInt(recipientPublicKey.Length));
        Output.AddRange(recipientPublicKey);
        Output.AddRange(ConvertInt(senderPublicKey.Length));
        Output.AddRange(senderPublicKey);
        return Output.ToArray();
    }

    public static byte[] GenerateHKDF(byte[] salt, byte[] ikm, byte[] info, int len) {
        IMac PRKGen = MacUtilities.GetMac("HmacSHA256");
        PRKGen.Init(new KeyParameter(MacUtilities.CalculateMac("HmacSHA256", new KeyParameter(salt), ikm)));
        PRKGen.BlockUpdate(info, 0, info.Length);
        PRKGen.Update((byte)1);
        byte[] Result = MacUtilities.DoFinal(PRKGen);
        if (Result.Length > len) Array.Resize(ref Result, len);
        return Result;
    }

}
///<summary>
/// Base 64 Encoding with URL and Filename Safe Alphabet using UTF-8 character set.
///</summary>
///<param name="str">The origianl string</param>
///<returns>The Base64 encoded string</returns>
public static string Base64ForUrlEncode(string str)
{
    byte[] encbuff = Encoding.UTF8.GetBytes(str);
    return HttpServerUtility.UrlTokenEncode(encbuff);
}
///<summary>
/// Decode Base64 encoded string with URL and Filename Safe Alphabet using UTF-8.
///</summary>
///<param name="str">Base64 code</param>
///<returns>The decoded string.</returns>
public static string Base64ForUrlDecode(string str)
{
    byte[] decbuff = HttpServerUtility.UrlTokenDecode(str);
    return Encoding.UTF8.GetString(decbuff);
}