430 lines
16 KiB
C#
430 lines
16 KiB
C#
using LoggingHelper;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Configuration;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Net.Http;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Web;
|
|
using System.Web.Script.Serialization;
|
|
|
|
namespace VECV_WebApi.Common
|
|
{
|
|
public class FCMNotification
|
|
{
|
|
#region Global Variables
|
|
|
|
/// <summary>
|
|
/// making object of LoggingUtility class available to this class
|
|
/// </summary>
|
|
LoggingUtility objLog = new LoggingUtility();
|
|
|
|
/// <summary>
|
|
/// making the data-log file path available to this class
|
|
/// </summary>
|
|
string path = HttpContext.Current.Server.MapPath(ConfigurationManager.AppSettings["PathLog"]);
|
|
|
|
/// <summary>
|
|
/// making error log file path available to this class
|
|
/// </summary>
|
|
string errorlogtf = (ConfigurationManager.AppSettings["ErrorLog"]);
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Generates a Firebase Access Token manually using RSA256
|
|
/// </summary>
|
|
public string GetFirebaseAccessToken()
|
|
{
|
|
try
|
|
{
|
|
string serviceAccountPath = (ConfigurationManager.AppSettings["FCMJson"]);
|
|
var json = File.ReadAllText(serviceAccountPath);
|
|
var serviceAccountData = new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(json);
|
|
|
|
string privateKey = serviceAccountData["private_key"].ToString().Replace("\\n", "\n");
|
|
string clientEmail = serviceAccountData["client_email"].ToString();
|
|
string tokenUri = serviceAccountData["token_uri"].ToString();
|
|
|
|
var now = DateTime.UtcNow;
|
|
long iat = (int)(now - new DateTime(1970, 1, 1)).TotalSeconds;
|
|
long exp = iat + 3600; // 1 hour expiration
|
|
|
|
var header = new { alg = "RS256", typ = "JWT" };
|
|
var payload = new
|
|
{
|
|
iss = clientEmail,
|
|
scope = "https://www.googleapis.com/auth/cloud-platform",
|
|
aud = tokenUri,
|
|
iat = iat,
|
|
exp = exp
|
|
};
|
|
|
|
string encodedHeader = Base64UrlEncode(new JavaScriptSerializer().Serialize(header));
|
|
string encodedPayload = Base64UrlEncode(new JavaScriptSerializer().Serialize(payload));
|
|
string unsignedToken = $"{encodedHeader}.{encodedPayload}";
|
|
|
|
string signedToken = SignWithRSA(unsignedToken, privateKey);
|
|
return signedToken;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine("Error generating Firebase Access Token: " + ex.Message);
|
|
return string.Empty;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sends an FCM notification with error handling (For .NET 4.5)
|
|
/// </summary>
|
|
public bool SendFCMNotification(string fcmToken, string projectId, string accessToken,string ticketid)
|
|
{
|
|
string url = $"https://fcm.googleapis.com/v1/projects/{projectId}/messages:send";
|
|
objLog.ErrorLogFile("GlobalRepository GcmSendNotificationtest1", url, path, errorlogtf);
|
|
var request = (HttpWebRequest)WebRequest.Create(url);
|
|
request.Method = "POST";
|
|
request.ContentType = "application/json";
|
|
request.Headers["Authorization"] = "Bearer " + accessToken;
|
|
objLog.ErrorLogFile("GlobalRepository GcmSendNotificationtest2", accessToken, path, errorlogtf);
|
|
|
|
var payload1 = new
|
|
{
|
|
message = new
|
|
{
|
|
token = fcmToken,
|
|
notification = new
|
|
{
|
|
title = "EOS Notification",
|
|
body = "EOS FCM!"
|
|
},
|
|
data = new
|
|
{
|
|
TicketId = ticketid,
|
|
NotificationCode = "null", // Change null to "null"
|
|
NotificationMessage = "2"
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
|
|
string jsonPayload = new JavaScriptSerializer().Serialize(payload1);
|
|
objLog.ErrorLogFile("GlobalRepository GcmSendNotification14", jsonPayload, path, errorlogtf);
|
|
try
|
|
{
|
|
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
|
|
{
|
|
streamWriter.Write(jsonPayload);
|
|
streamWriter.Flush();
|
|
streamWriter.Close();
|
|
}
|
|
|
|
var response = (HttpWebResponse)request.GetResponse();
|
|
using (var streamReader = new StreamReader(response.GetResponseStream()))
|
|
{
|
|
string result = streamReader.ReadToEnd();
|
|
Console.WriteLine("FCM Response: " + result);
|
|
objLog.ErrorLogFile("GlobalRepository GcmSendNotification12", result, path, errorlogtf);
|
|
}
|
|
return true;
|
|
}
|
|
catch (WebException webEx)
|
|
{
|
|
using (var streamReader = new StreamReader(webEx.Response.GetResponseStream()))
|
|
{
|
|
string errorResponse = streamReader.ReadToEnd();
|
|
if (errorResponse.Contains("UNREGISTERED"))
|
|
{
|
|
objLog.ErrorLogFile("GlobalRepository GcmSendNotification9", "FCM token is unregistered. Removing from database...", path, errorlogtf);
|
|
// objModel.TicketId = model.TicketId;
|
|
// Console.WriteLine("FCM token is unregistered. Removing from database...");
|
|
// Remove token from DB
|
|
}
|
|
else
|
|
{
|
|
objLog.ErrorLogFile("GlobalRepository GcmSendNotification10", errorResponse, path, errorlogtf);
|
|
// Console.WriteLine("FCM Error: " + errorResponse);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
objLog.ErrorLogFile("GlobalRepository GcmSendNotification11", ex.Message, path, errorlogtf);
|
|
// Console.WriteLine("Exception sending FCM: " + ex.Message);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
public bool SendFCMNotificationWithMessage(string fcmToken, string projectId, string accessToken, string ticketid, string current_status, string previous_status, string show_message, string display_message,string message_tag,string notification_title)
|
|
{
|
|
string url = $"https://fcm.googleapis.com/v1/projects/{projectId}/messages:send";
|
|
objLog.ErrorLogFile("GlobalRepository GcmSendNotificationtest1", url, path, errorlogtf);
|
|
var request = (HttpWebRequest)WebRequest.Create(url);
|
|
request.Method = "POST";
|
|
request.ContentType = "application/json";
|
|
request.Headers["Authorization"] = "Bearer " + accessToken;
|
|
string jsonPayload = "";
|
|
objLog.ErrorLogFile("GlobalRepository GcmSendNotificationtest2", accessToken, path, errorlogtf);
|
|
|
|
var payload1 = new
|
|
{
|
|
message = new
|
|
{
|
|
token = fcmToken,
|
|
data = new
|
|
{
|
|
TicketId = ticketid,
|
|
NotificationCode = "null", // Change null to "null"
|
|
NotificationMessage = "2",
|
|
CurrentStatus = current_status,
|
|
PreviosStatus = previous_status,
|
|
ShowMessage = show_message,
|
|
MessageTag = message_tag,
|
|
Notification_Title = notification_title,
|
|
DisplayMessge = display_message
|
|
}
|
|
}
|
|
|
|
|
|
};
|
|
jsonPayload = new JavaScriptSerializer().Serialize(payload1);
|
|
|
|
objLog.ErrorLogFile("GlobalRepository GcmSendNotification14", jsonPayload, path, errorlogtf);
|
|
try
|
|
{
|
|
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
|
|
{
|
|
streamWriter.Write(jsonPayload);
|
|
streamWriter.Flush();
|
|
streamWriter.Close();
|
|
}
|
|
|
|
var response = (HttpWebResponse)request.GetResponse();
|
|
using (var streamReader = new StreamReader(response.GetResponseStream()))
|
|
{
|
|
string result = streamReader.ReadToEnd();
|
|
Console.WriteLine("FCM Response: " + result);
|
|
objLog.ErrorLogFile("GlobalRepository GcmSendNotification12", result, path, errorlogtf);
|
|
objLog.ErrorLogFile("GlobalRepository GcmSendNotification12", jsonPayload, path, errorlogtf);
|
|
}
|
|
return true;
|
|
}
|
|
catch (WebException webEx)
|
|
{
|
|
using (var streamReader = new StreamReader(webEx.Response.GetResponseStream()))
|
|
{
|
|
string errorResponse = streamReader.ReadToEnd();
|
|
if (errorResponse.Contains("UNREGISTERED"))
|
|
{
|
|
objLog.ErrorLogFile("GlobalRepository GcmSendNotification9", "FCM token is unregistered. Removing from database...", path, errorlogtf);
|
|
// objModel.TicketId = model.TicketId;
|
|
// Console.WriteLine("FCM token is unregistered. Removing from database...");
|
|
// Remove token from DB
|
|
}
|
|
else
|
|
{
|
|
objLog.ErrorLogFile("GlobalRepository GcmSendNotification10", errorResponse, path, errorlogtf);
|
|
// Console.WriteLine("FCM Error: " + errorResponse);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
objLog.ErrorLogFile("GlobalRepository GcmSendNotification11", ex.Message, path, errorlogtf);
|
|
// Console.WriteLine("Exception sending FCM: " + ex.Message);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Signs JWT using RSA with SHA256
|
|
/// </summary>
|
|
static string SignWithRSA(string data, string privateKeyPem)
|
|
{
|
|
byte[] keyBytes = Convert.FromBase64String(privateKeyPem.Replace("-----BEGIN PRIVATE KEY-----", "").Replace("-----END PRIVATE KEY-----", "").Replace("\n", ""));
|
|
// RSACryptoServiceProvider rsa = DecodeRSAPrivateKey(keyBytes);
|
|
RSACryptoServiceProvider rsa = DecodeRSAPrivateKey(privateKeyPem);
|
|
|
|
byte[] dataBytes = Encoding.UTF8.GetBytes(data);
|
|
byte[] signedBytes = rsa.SignData(dataBytes, CryptoConfig.MapNameToOID("SHA256"));
|
|
|
|
return $"{data}.{Base64UrlEncode(signedBytes)}";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts standard Base64 to Base64Url encoding
|
|
/// </summary>
|
|
static string Base64UrlEncode(object input)
|
|
{
|
|
string base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(input.ToString()));
|
|
return base64.TrimEnd('=').Replace('+', '-').Replace('/', '_');
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts Base64 to Base64Url encoding
|
|
/// </summary>
|
|
static string Base64UrlEncode(byte[] input)
|
|
{
|
|
string base64 = Convert.ToBase64String(input);
|
|
return base64.TrimEnd('=').Replace('+', '-').Replace('/', '_');
|
|
}
|
|
|
|
/// <summary>
|
|
/// Decodes a RSA private key
|
|
/// </summary>
|
|
//static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privateKeyBytes)
|
|
//{
|
|
// RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
|
|
// rsa.DecodeRSAPrivateKey
|
|
// // rsa.ImportPkcs8PrivateKey(new ReadOnlySpan<byte>(privateKeyBytes), out _);
|
|
// return rsa;
|
|
//}
|
|
static RSACryptoServiceProvider DecodeRSAPrivateKey(string privateKeyPem)
|
|
{
|
|
try
|
|
{
|
|
// Remove headers and footers from PEM
|
|
string privateKeyBase64 = privateKeyPem
|
|
.Replace("-----BEGIN PRIVATE KEY-----", "")
|
|
.Replace("-----END PRIVATE KEY-----", "")
|
|
.Replace("\n", "")
|
|
.Replace("\r", "")
|
|
.Trim();
|
|
|
|
// Convert Base64 to byte array
|
|
byte[] privateKeyBytes = Convert.FromBase64String(privateKeyBase64);
|
|
|
|
return DecodeRSAPrivateKeyFromBytes(privateKeyBytes);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine("Error decoding private key: " + ex.Message);
|
|
return null;
|
|
}
|
|
}
|
|
public async Task<string> SendFCMNotification2(string accesstoken)
|
|
{
|
|
string str = "success";
|
|
var client = new HttpClient();
|
|
var request = new HttpRequestMessage(HttpMethod.Post, "https://fcm.googleapis.com/v1/projects/eicher-eos-androidx/messages:send");
|
|
request.Headers.Add("Sender", "15420367036");
|
|
request.Headers.Add("Authorization", "Bearer " + accesstoken);
|
|
// request.Headers.Add("Authorization", "Bearer ya29.c.c0ASRK0GYrBkOReMU8gE-qqt9I3kd0Cop-00wEWRe1iBJBnQ71yWSujJ7S1QHsqngKLQk1ifiDslxNVwdEN8j6NOF9UmOhL9qnWcHpnXM4lXQOBJPyXF6-9eF-zRT1ZVA8Xcrb_EzY8BHsKokQoLcE5JVLDuOcZqqsAhdBZ3Wv5Z5WxweHtOHBhZtXkG3451K3vCLi5nhG-5RploO3PrGoXsMqmJKmXo3jBTO7u9YmMtnrKkK-Pd4cSPyn2pL6qIfJsqi3yn8tFX0X1OGR8gw8rp40AnX3ZL1NdtGoojOUmgBXnLn0Md81EiSdb7ejGfv4aagbIbfOFtcn98a6dls6IhvUWR2u0irEwl5UYQDLz1O-gdVQjaky5fAG384Kc92vQ7X1ct811pm6edjivJIak5Fofb5knYdbg9i9hiluf5teb0a20lsbnyJqQkbeWQjOk_SlhUiee-MeMzfZvcx4rSFOfrsj9RhfQ9voetzYM6znw1l1YBi5klIasd3V7labyrd6kqdcF_ZFqMyse5ButW50R14gZ9UJFgbg2nmYiilx833hkkv5elxX0JSO9rvzaawncZp5apW4Mpp7-6UJ0b6bid8RvZtXsBnMixwOmUiBfSVp2lROBtWu9nrRrvh_QVb5dBllrgbBb1bgwScSo4XcgnY3mcdwYFVB_lk10exd-4RJlqj9kf5QJF3hRY3IjOW02kJiZq2WeQ6w6gu5X3sXRo8cul8surm0i5XMzqB5wqgafeexVda1Osz7t6kvXj7mgnk57Jmxjc823eknaM9Fn-cax-IuR8sze9f2c4rSV2h8sbwbcI3cvinp6ocBnhIoSvFBBjFhhXs5wBVMY_rRZbQijjj8-016JkbIb9ztWYruynx5g2oq3IhWpje_xfnMIjughaboVgmi9c-nvcdQSMygxOOI4Zdmi2YOomv0b2WjvW-wBwtoeJ2gerY3o9xmdX-1wcrdyXsISidezSoz_eWIIVx12ikmzcJ0X5rSoJMhBYti0--");
|
|
var content = new StringContent("{\r\n \"message\": {\r\n \"token\":\"eZM2vdFeaTA:APA91bG56oeC0Sv6TeBj1Sf4v4KfBEVpRkGL24yLgXNKU5bKOwZboQiHRbtnWMxzl1yt7bf2zrzyEJNI6HJwDY2iXmwZSJTF0RpRAqTb4pcpOHr7AZA0Gwk\",\r\n \"data\": {\"TicketId\":\"TICKETID-278411\",\"NotificationCode\":null,\"NotificationMessage\":\"2\"}\r\n }\r\n}\r\n\r\n", null, "application/json");
|
|
request.Content = content;
|
|
var response = await client.SendAsync(request);
|
|
response.EnsureSuccessStatusCode();
|
|
Console.WriteLine(await response.Content.ReadAsStringAsync());
|
|
return str;
|
|
}
|
|
/// <summary>
|
|
/// Decodes an RSA private key from byte array (Compatible with .NET 4.5)
|
|
/// </summary>
|
|
static RSACryptoServiceProvider DecodeRSAPrivateKeyFromBytes(byte[] privateKeyBytes)
|
|
{
|
|
try
|
|
{
|
|
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
|
|
using (MemoryStream ms = new MemoryStream(privateKeyBytes))
|
|
using (BinaryReader br = new BinaryReader(ms))
|
|
{
|
|
byte[] seqOid = { 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00 };
|
|
|
|
if (br.ReadByte() != 0x30)
|
|
return null;
|
|
|
|
ReadASNLength(br);
|
|
if (br.ReadByte() != 0x02)
|
|
return null;
|
|
|
|
int version = br.ReadByte();
|
|
if (version != 0x00)
|
|
return null;
|
|
|
|
if (!CompareByteArrays(br.ReadBytes(15), seqOid))
|
|
return null;
|
|
|
|
if (br.ReadByte() != 0x04)
|
|
return null;
|
|
|
|
ReadASNLength(br);
|
|
if (br.ReadByte() != 0x30)
|
|
return null;
|
|
|
|
ReadASNLength(br);
|
|
|
|
RSAParameters rsaParams = new RSAParameters
|
|
{
|
|
Modulus = ReadASNInteger(br),
|
|
Exponent = ReadASNInteger(br),
|
|
D = ReadASNInteger(br),
|
|
P = ReadASNInteger(br),
|
|
Q = ReadASNInteger(br),
|
|
DP = ReadASNInteger(br),
|
|
DQ = ReadASNInteger(br),
|
|
InverseQ = ReadASNInteger(br)
|
|
};
|
|
|
|
rsa.ImportParameters(rsaParams);
|
|
return rsa;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine("Error decoding RSA private key from bytes: " + ex.Message);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads an ASN.1 length
|
|
/// </summary>
|
|
static int ReadASNLength(BinaryReader br)
|
|
{
|
|
int length = br.ReadByte();
|
|
if ((length & 0x80) == 0)
|
|
return length;
|
|
|
|
int bytesToRead = length & 0x7F;
|
|
return br.ReadByte();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads an ASN.1 Integer
|
|
/// </summary>
|
|
static byte[] ReadASNInteger(BinaryReader br)
|
|
{
|
|
if (br.ReadByte() != 0x02)
|
|
return null;
|
|
|
|
int length = ReadASNLength(br);
|
|
return br.ReadBytes(length);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Compares two byte arrays
|
|
/// </summary>
|
|
static bool CompareByteArrays(byte[] a, byte[] b)
|
|
{
|
|
if (a.Length != b.Length)
|
|
return false;
|
|
|
|
for (int i = 0; i < a.Length; i++)
|
|
{
|
|
if (a[i] != b[i])
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
}
|
|
}
|