304 lines
10 KiB
C#
304 lines
10 KiB
C#
using Newtonsoft.Json;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using System.Web;
|
|
using DBHelper;
|
|
using Org.BouncyCastle.OpenSsl;
|
|
using Org.BouncyCastle.Crypto;
|
|
using Org.BouncyCastle.Security;
|
|
using Org.BouncyCastle.Crypto.Parameters;
|
|
using System.Configuration;
|
|
using System.Web.Script.Serialization;
|
|
|
|
namespace VECV_WebApi.Common
|
|
{
|
|
|
|
public class FirebaseNotificationSender
|
|
{
|
|
private static string serviceAccountPath = (ConfigurationManager.AppSettings["FCMJson"]);
|
|
private static string firebaseProjectId = (ConfigurationManager.AppSettings["ProjectId"]);
|
|
//private static string fcmUrl = $"https://fcm.googleapis.com/v1/projects/" + firebaseProjectId + "/messages:send";
|
|
private string fcmUrl;
|
|
public FirebaseNotificationSender()
|
|
{
|
|
fcmUrl = $"https://fcm.googleapis.com/v1/projects/{firebaseProjectId}/messages:send";
|
|
}
|
|
public string GetAccessTokenValue()
|
|
{
|
|
// var jwtGenerator = new GetAccessToken();
|
|
return GetAccessToken();
|
|
}
|
|
public void SendNotification(string accessToken, string deviceToken, string title, string body)
|
|
{
|
|
var payload = new
|
|
{
|
|
message = new
|
|
{
|
|
token = deviceToken,
|
|
notification = new
|
|
{
|
|
title = title,
|
|
body = body
|
|
}
|
|
}
|
|
};
|
|
string jsonPayload = JsonConvert.SerializeObject(payload);
|
|
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(fcmUrl);
|
|
request.Method = "POST";
|
|
request.ContentType = "application/json";
|
|
request.Headers.Add("Authorization", "Bearer " + accessToken);
|
|
using (StreamWriter writer = new StreamWriter(request.GetRequestStream()))
|
|
{
|
|
writer.Write(jsonPayload);
|
|
}
|
|
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
|
|
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
|
|
{
|
|
string responseContent = reader.ReadToEnd();
|
|
Console.WriteLine("FCM Response: " + responseContent);
|
|
}
|
|
}
|
|
|
|
private static string GetAccessToken()
|
|
{
|
|
string json = File.ReadAllText(serviceAccountPath);
|
|
var keyData = new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(json);
|
|
string clientEmail = keyData["client_email"].ToString();
|
|
string privateKey = keyData["private_key"].ToString();
|
|
string tokenUri = "https://oauth2.googleapis.com/token";
|
|
// Create JWT Header & Payload
|
|
var now = DateTime.UtcNow;
|
|
var payload = new Dictionary<string, object>
|
|
{
|
|
{ "iss", clientEmail },
|
|
{ "scope", "https://www.googleapis.com/auth/firebase.messaging" },
|
|
{ "aud", tokenUri },
|
|
{ "iat", (int)(now - new DateTime(1970, 1, 1)).TotalSeconds },
|
|
{ "exp", (int)(now.AddMinutes(60) - new DateTime(1970, 1, 1)).TotalSeconds }
|
|
};
|
|
string jwt = CreateJwt(privateKey, payload);
|
|
// Exchange JWT for OAuth Token
|
|
using (var webClient = new WebClient())
|
|
{
|
|
var requestParams = new Dictionary<string, string>
|
|
{
|
|
{ "grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer" },
|
|
{ "assertion", jwt }
|
|
};
|
|
string postData = new JavaScriptSerializer().Serialize(requestParams);
|
|
webClient.Headers[HttpRequestHeader.ContentType] = "application/json";
|
|
string response = webClient.UploadString(tokenUri, postData);
|
|
var responseObj = new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(response);
|
|
return responseObj["access_token"].ToString();
|
|
}
|
|
}
|
|
|
|
private static string CreateJwt(string privateKey, Dictionary<string, object> payload)
|
|
{
|
|
var rsaParams = GetRsaParameters(privateKey);
|
|
var rsa = new RSACryptoServiceProvider();
|
|
rsa.ImportParameters(rsaParams);
|
|
var header = new { alg = "RS256", typ = "JWT" };
|
|
string encodedHeader = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(header)));
|
|
string encodedPayload = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(payload)));
|
|
string unsignedToken = $"{encodedHeader}.{encodedPayload}";
|
|
byte[] signature = rsa.SignData(Encoding.UTF8.GetBytes(unsignedToken), CryptoConfig.MapNameToOID("SHA256"));
|
|
return $"{unsignedToken}.{Convert.ToBase64String(signature)}";
|
|
}
|
|
private static RSAParameters GetRsaParameters(string privateKey)
|
|
{
|
|
var pemReader = new PemReader(new StringReader(privateKey));
|
|
var keyObject = pemReader.ReadObject();
|
|
|
|
// Handle the case where the private key is directly a RsaPrivateCrtKeyParameters
|
|
if (keyObject is RsaPrivateCrtKeyParameters privateRsaParams)
|
|
{
|
|
return DotNetUtilities.ToRSAParameters(privateRsaParams);
|
|
}
|
|
|
|
// Handle AsymmetricCipherKeyPair (private and public key)
|
|
if (keyObject is AsymmetricCipherKeyPair keyPair)
|
|
{
|
|
privateRsaParams = (RsaPrivateCrtKeyParameters)keyPair.Private;
|
|
return DotNetUtilities.ToRSAParameters(privateRsaParams);
|
|
}
|
|
|
|
throw new InvalidOperationException("Unsupported private key format.");
|
|
}
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*static string jsonFilePath = @"path-to-your-service-account.json"; // Path to JSON file
|
|
string accessToken = GetAccessTokenFromJsonFile(jsonFilePath);
|
|
|
|
static string GetAccessTokenFromJsonFile(string jsonFilePath)
|
|
{
|
|
// Read JSON file
|
|
string json = File.ReadAllText(jsonFilePath);
|
|
var serviceAccountData = JsonConvert.DeserializeObject<ServiceAccount>(json);
|
|
|
|
if (serviceAccountData == null)
|
|
{
|
|
throw new Exception("Invalid service account JSON file.");
|
|
}
|
|
|
|
// Generate JWT
|
|
string jwtToken = GenerateJwt(serviceAccountData);
|
|
|
|
// Request access token
|
|
string token = RequestAccessToken(jwtToken);
|
|
return token;
|
|
}
|
|
static string RequestAccessToken(string jwtToken)
|
|
{
|
|
string tokenUrl = "https://oauth2.googleapis.com/token";
|
|
|
|
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(tokenUrl);
|
|
request.Method = "POST";
|
|
request.ContentType = "application/x-www-form-urlencoded";
|
|
|
|
string postData = $"grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion={jwtToken}";
|
|
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
|
|
request.ContentLength = byteArray.Length;
|
|
|
|
using (Stream dataStream = request.GetRequestStream())
|
|
{
|
|
dataStream.Write(byteArray, 0, byteArray.Length);
|
|
}
|
|
|
|
try
|
|
{
|
|
using (WebResponse response = request.GetResponse())
|
|
{
|
|
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
|
|
{
|
|
string responseString = reader.ReadToEnd();
|
|
var jsonResponse = JsonConvert.DeserializeObject<dynamic>(responseString);
|
|
return jsonResponse.access_token;
|
|
}
|
|
}
|
|
}
|
|
catch (WebException ex)
|
|
{
|
|
using (var errorResponse = ex.Response)
|
|
using (var reader = new StreamReader(errorResponse.GetResponseStream()))
|
|
{
|
|
string errorText = reader.ReadToEnd();
|
|
Console.WriteLine($"Error: {errorText}");
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
|
|
static string GenerateJwt(ServiceAccount serviceAccount)
|
|
{
|
|
long iat = GetUnixTimestamp();
|
|
long exp = iat + 3600;
|
|
|
|
var header = new { alg = "RS256", typ = "JWT" };
|
|
var payload = new Dictionary<string, object>
|
|
{
|
|
{ "iss", serviceAccount.client_email },
|
|
{ "sub", serviceAccount.client_email },
|
|
{ "aud", "https://oauth2.googleapis.com/token" },
|
|
{ "iat", iat },
|
|
{ "exp", exp },
|
|
{ "scope", "https://www.googleapis.com/auth/firebase.messaging" }
|
|
};
|
|
|
|
string headerBase64 = Base64UrlEncode(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(header)));
|
|
string payloadBase64 = Base64UrlEncode(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(payload)));
|
|
|
|
string unsignedToken = $"{headerBase64}.{payloadBase64}";
|
|
string signedJwt = SignJwt(unsignedToken, serviceAccount.private_key);
|
|
|
|
return $"{unsignedToken}.{signedJwt}";
|
|
}
|
|
public static long GetUnixTimestamp()
|
|
{
|
|
DateTimeOffset dateTimeOffset = DateTimeOffset.UtcNow;
|
|
DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
|
TimeSpan elapsedTime = dateTimeOffset.UtcDateTime - epoch;
|
|
return (long)elapsedTime.TotalSeconds;
|
|
}
|
|
|
|
static string SignJwt(string unsignedJwt, string privateKey)
|
|
{
|
|
byte[] privateKeyBytes = Convert.FromBase64String(
|
|
privateKey
|
|
.Replace("-----BEGIN PRIVATE KEY-----", "")
|
|
.Replace("-----END PRIVATE KEY-----", "")
|
|
.Replace("\n", "")
|
|
);
|
|
|
|
using (RSACryptoServiceProvider rsa = ImportPrivateKey(privateKey))
|
|
{
|
|
byte[] hash = HashData(unsignedJwt);
|
|
byte[] signature = rsa.SignHash(hash, CryptoConfig.MapNameToOID("SHA256"));
|
|
return Base64UrlEncode(signature);
|
|
}
|
|
}
|
|
|
|
//static RSACryptoServiceProvider DecodePrivateKey(byte[] privateKeyBytes)
|
|
//{
|
|
// var rsa = new RSACryptoServiceProvider();
|
|
// rsa.ImportPkcs8PrivateKey(privateKeyBytes, out _); // If fails, try importing with OpenSSL-converted private key.
|
|
// return rsa;
|
|
//}
|
|
|
|
public static RSACryptoServiceProvider ImportPrivateKey(string pem)
|
|
{
|
|
PemReader pr = new PemReader(new StringReader(pem));
|
|
AsymmetricCipherKeyPair KeyPair = (AsymmetricCipherKeyPair)pr.ReadObject();
|
|
RSAParameters rsaParams =
|
|
DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)KeyPair.Private);
|
|
|
|
RSACryptoServiceProvider csp = new RSACryptoServiceProvider();// cspParams);
|
|
csp.ImportParameters(rsaParams);
|
|
return csp;
|
|
}
|
|
static string Base64UrlEncode(byte[] input)
|
|
{
|
|
return Convert.ToBase64String(input)
|
|
.TrimEnd('=')
|
|
.Replace('+', '-')
|
|
.Replace('/', '_');
|
|
}
|
|
static byte[] HashData(string data)
|
|
{
|
|
using (SHA256 sha256 = SHA256.Create())
|
|
{
|
|
return sha256.ComputeHash(Encoding.UTF8.GetBytes(data));
|
|
}
|
|
}
|
|
class ServiceAccount
|
|
{
|
|
public string type { get; set; }
|
|
public string project_id { get; set; }
|
|
public string private_key_id { get; set; }
|
|
public string private_key { get; set; }
|
|
public string client_email { get; set; }
|
|
public string client_id { get; set; }
|
|
public string auth_uri { get; set; }
|
|
public string token_uri { get; set; }
|
|
public string auth_provider_x509_cert_url { get; set; }
|
|
public string client_x509_cert_url { get; set; }
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
*/ |