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 /// /// making object of LoggingUtility class available to this class /// LoggingUtility objLog = new LoggingUtility(); /// /// making the data-log file path available to this class /// string path = HttpContext.Current.Server.MapPath(ConfigurationManager.AppSettings["PathLog"]); /// /// making error log file path available to this class /// string errorlogtf = (ConfigurationManager.AppSettings["ErrorLog"]); #endregion /// /// Generates a Firebase Access Token manually using RSA256 /// public string GetFirebaseAccessToken() { try { string serviceAccountPath = (ConfigurationManager.AppSettings["FCMJson"]); var json = File.ReadAllText(serviceAccountPath); var serviceAccountData = new JavaScriptSerializer().Deserialize>(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; } } /// /// Sends an FCM notification with error handling (For .NET 4.5) /// 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; } /// /// Signs JWT using RSA with SHA256 /// 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)}"; } /// /// Converts standard Base64 to Base64Url encoding /// static string Base64UrlEncode(object input) { string base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(input.ToString())); return base64.TrimEnd('=').Replace('+', '-').Replace('/', '_'); } /// /// Converts Base64 to Base64Url encoding /// static string Base64UrlEncode(byte[] input) { string base64 = Convert.ToBase64String(input); return base64.TrimEnd('=').Replace('+', '-').Replace('/', '_'); } /// /// Decodes a RSA private key /// //static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privateKeyBytes) //{ // RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); // rsa.DecodeRSAPrivateKey // // rsa.ImportPkcs8PrivateKey(new ReadOnlySpan(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 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; } /// /// Decodes an RSA private key from byte array (Compatible with .NET 4.5) /// 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; } } /// /// Reads an ASN.1 length /// static int ReadASNLength(BinaryReader br) { int length = br.ReadByte(); if ((length & 0x80) == 0) return length; int bytesToRead = length & 0x7F; return br.ReadByte(); } /// /// Reads an ASN.1 Integer /// static byte[] ReadASNInteger(BinaryReader br) { if (br.ReadByte() != 0x02) return null; int length = ReadASNLength(br); return br.ReadBytes(length); } /// /// Compares two byte arrays /// 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; } } }