2016-07-02 38 views
4

雖然我試圖讓我的通知更加詳細,但我無法找到使用.net作爲後端的詳細webpush通知的示例,我設法通過類似的方法向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庫(chrome和FF瀏覽器),並且沒有任何示例可以在任何地方找到,所以我有點卡住了。你可以看到,我已經將每個客戶端的p256dh和auth保存到一個mysql數據庫中,但從那時起,我一直無法取得進展。

回答

4

似乎有人找到了一種方法來做到這一點。使用BouncyCastlethis 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命名空間:

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

這是太棒了,非常感謝。由於我從來沒有時間弄清楚,因此我一直在繼續使用indescript推送通知。也是很好的軍團士兵的照片,我以前玩HoN很多哈哈。 –

+0

當我遇到這個問題時,我發現許多語言的解決方案...除了C#。它困擾了我,直到找到解決方案才停下來。 – LbISS

+0

我正在使用asp.net,但我不明白最後一部分。替換這些方法意味着什麼?他們是否必須在特定的課程上實施?我應該將第一段代碼「WebEncoders.Base64UrlDecode」和「WebEncoders.Base64UrlEncode」的調用替換爲這些方法嗎?謝謝。 –