.net中有WebPush有效负载加密的方法吗? [英] Is there a way for WebPush payload encryption in .net?

查看:40
本文介绍了.net中有WebPush有效负载加密的方法吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I manage to send empty push notifications to chrome and firefox, through similar methods though I'm trying to make my notifications more detailed, however I can't find an example of detailed webpush notifications using .net as the backend.

我的firefox示例如下:

My firefox example is as follows:

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浏览器),在任何地方都找不到示例,所以我有点受阻.

Neither the userPublicKey or userAuth are actually in use right now and don't serve any purpose without the encryption of the payload so I've read, however using vb.net, there is no .net library for sending push notifications to web platforms (chrome and FF browsers) and there's no examples I can find anywhere, so I'm a little bit stuck.

如您所见,我将来自每个客户端的端点,p256dh和auth保存到了mysql数据库中,但从那时起,到目前为止,我一直无法继续工作.

As you can see, I have the endpoint, the p256dh and auth from each client saved into a mysql DB, but from that point I've been unable to progress thus far.

推荐答案

似乎有人找到了一种方法.使用 BouncyCastle 复制解决方案加密数据用于网络浏览器push.html"rel =" nofollow>此博客:

It seems that someone find a way to do that. Copying solution using BouncyCastle from this blog:

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

}

要使此代码与asp.net一起使用,请使用以下代码替换aspcore命名空间中的解码/编码方法:

To make this code work with asp.net replace decode/encode methods from aspcore namespace with:

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

这篇关于.net中有WebPush有效负载加密的方法吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆