using Newtonsoft.Json.Linq; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using Azure.Identity; using Azure.Security.KeyVault.Secrets; namespace CDP { public class Helpers { public static byte[] GenerateAES256Key() { using (Aes aesAlg = Aes.Create()) { aesAlg.KeySize = 256; aesAlg.GenerateKey(); return aesAlg.Key; } } // hashes a string and removes the dashes (-) public static string HashToHex(string str) { using (SHA256 sha256Hash = SHA256.Create()) { byte[] bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(str)); return BitConverter.ToString(bytes).Replace("-", string.Empty).ToLower(); } } // takes a short hash and converts it to normal hash. // Circle Auth uses hashes stored as hex...use this to convert to that format. public static string ConvertShortHashToHex(string shortHash) { // Add padding characters ('=') to the short hash if needed while (shortHash.Length % 4 != 0) { shortHash += "="; } // Decode the Base64 short hash to bytes byte[] hashBytes = Convert.FromBase64String(shortHash); // Convert the bytes to a hexadecimal string string hexHash = BitConverter.ToString(hashBytes).Replace("-", "").ToLower(); return hexHash; } // Generates an AES Key and converts it to base64 public static string GenerateAES256KeyToBase64() { return Convert.ToBase64String(GenerateAES256Key()); } // Generates a guid, removes the - and then converts it to base64 public static string GenerateShortGuid() { Guid guid = Guid.NewGuid(); byte[] bytes = guid.ToByteArray(); string shortGuid = Convert.ToBase64String(bytes); // Remove padding characters from the end of the Base64 string shortGuid = shortGuid.TrimEnd('='); return shortGuid; } public static Guid LengthenShortGuid(string shortGuid) { // Add padding characters to the end of the Base64 string, if needed while (shortGuid.Length % 4 != 0) { shortGuid += "="; } // Convert the Base64 string back to bytes byte[] bytes = Convert.FromBase64String(shortGuid); // Create a new GUID from the bytes Guid guid = new Guid(bytes); return guid; } public static bool VerifyData(string originalMessage, string signedMessage) { bool success = false; using (var rsa = new RSACryptoServiceProvider()) { var encoder = new UTF8Encoding(); byte[] bytesToVerify = encoder.GetBytes(originalMessage); byte[] signedBytes = Convert.FromBase64String(signedMessage); try { rsa.FromXmlString(Constants.PublicKey); SHA512Managed Hash = new SHA512Managed(); byte[] hashedData = Hash.ComputeHash(signedBytes); success = rsa.VerifyData(bytesToVerify, CryptoConfig.MapNameToOID("SHA512"), signedBytes); } catch (CryptographicException e) { Console.WriteLine(e.Message); } finally { rsa.PersistKeyInCsp = false; } } return success; } public static string ComputeSignature(string stringToSign, string secret) { using (var hmacsha256 = new HMACSHA256(System.Text.ASCIIEncoding.UTF8.GetBytes(secret))) { var bytes = Encoding.ASCII.GetBytes(stringToSign); var hashedBytes = hmacsha256.ComputeHash(bytes); return Convert.ToBase64String(hashedBytes); } } public static async Task GetSession(string sessionId) { string toSign = string.Format($"?s={sessionId}"); string sig = Helpers.ComputeSignature(toSign, Constants.CDPWriteKey); string URL = string.Format($"https://circleauth.gocircle.ai/api/session/{toSign}&signature={sig}"); try { HttpClient client = new HttpClient(); client.Timeout = new TimeSpan(0, 1, 0, 0); client.DefaultRequestHeaders.Add("x-ua-appKey", Constants.CDPAppKey); var response = await client.GetAsync(URL); if (response.StatusCode == System.Net.HttpStatusCode.OK) { string responseString = await response.Content.ReadAsStringAsync(); dynamic obj = JObject.Parse(responseString); dynamic data = obj.data; return data; } } catch (Exception e) { Console.WriteLine(e.Message); } return null; } public static async Task ExpireSession(string sessionId, string userId) { try { var dataObj = new { sessionID = sessionId, userID = userId }; var sig = Helpers.ComputeSignature(JsonConvert.SerializeObject(dataObj), Constants.CDPWriteKey); var obj = new { data = dataObj, signature = sig }; HttpClient client = new HttpClient(); client.Timeout = new TimeSpan(0, 1, 0, 0); client.DefaultRequestHeaders.Add("x-ua-appKey", Constants.CDPAppKey); var content = new StringContent(JsonConvert.SerializeObject(obj), Encoding.UTF8, "application/json"); var r = client.PostAsync("https://circleaccess.circlesecurity.ai/api/user/session/expire", content).Result; return r.StatusCode == HttpStatusCode.OK; } catch (Exception e) { Console.WriteLine(e); } return false; } /// /// this is the HASH that Circle Auth likes, it's just a SHA265 in hex. /// public static string HashText(string rawData) { try { // Create a SHA256 using SHA256 sha256Hash = SHA256.Create(); byte[] bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(rawData)); StringBuilder builder = new(); for (int i = 0; i < bytes.Length; i++) { builder.Append(bytes[i].ToString("x2")); } return builder.ToString(); } catch (Exception ex) { return null; } } public static string HashAndShortenText(string text) { // Hash the email address using SHA-256 using (SHA256 sha256 = SHA256.Create()) { byte[] hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(text)); string shortHash = Convert.ToBase64String(hashBytes); // make the short hash URL friendly shortHash = shortHash.Replace("+", "-").Replace("/", "_"); // Remove padding characters from the end of the Base64 string shortHash = shortHash.TrimEnd('='); return shortHash; } } public static async Task CreateSession(string customerAppKey, string returnURL, string payloadJson, string webHook = "") { var client = new HttpClient(); // wacky hack since I can't declare anonymous variables without initializing them var payloadObject = new { Test = "123" }; var dataObj = new { payload = payloadJson, customerAppKey = customerAppKey, customID = "blahORama", returnUrl = returnURL, mobileUrl = returnURL, webhookUrl = webHook }; var sig = Helpers.ComputeSignature(JsonConvert.SerializeObject(dataObj), Constants.CDPWriteKey); var obj = new { data = dataObj, signature = sig }; string json = JsonConvert.SerializeObject(obj); client.DefaultRequestHeaders.Add("x-ua-appKey", Constants.CDPAppKey); HttpContent c = new StringContent(json, Encoding.UTF8, "application/json"); var response = await client.PostAsync(new Uri("https://circleaccess.circlesecurity.ai/api/single/create"), c); SingleLogin sl = new SingleLogin(); if (response.StatusCode == System.Net.HttpStatusCode.OK) { string body = response.Content.ReadAsStringAsync().Result; //right! dynamic stuff = JObject.Parse(body); sl.QRCodeUrl = stuff.data.qrcode.ToString(); sl.LoginId = stuff.data.singleLoginID.ToString(); return sl; } return null; } } public class KeyVaultService { private readonly string _keyVaultUrl; public KeyVaultService(string keyVaultUrl) { _keyVaultUrl = keyVaultUrl; } public async Task SetSecretAsync(string secretName, string secretValue) { #if DEBUG var client = new SecretClient(new Uri(_keyVaultUrl), new VisualStudioCredential()); #else var client = new SecretClient(new Uri(_keyVaultUrl), new DefaultAzureCredential()); #endif // Set the secret in the Key Vault await client.SetSecretAsync(secretName, secretValue); } public async Task GetSecretAsync(string secretName) { #if DEBUG var client = new SecretClient(new Uri(_keyVaultUrl), new VisualStudioCredential()); #else var client = new SecretClient(new Uri(_keyVaultUrl), new DefaultAzureCredential()); #endif // Set the secret in the Key Vault KeyVaultSecret kvs = await client.GetSecretAsync(secretName); return kvs.Value; } } public class SingleLogin { public string QRCodeUrl { get; set; } public string LoginId { get; set; } public SingleLogin() { } public override string ToString() { return QRCodeUrl; } } }