using ServiceShared.Crypto; using ServiceShared.Models.Request; using ServiceShared.Models.Response; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Security; using System.Text; using System.Text.Json; using System.Threading.Tasks; namespace ServiceShared.Https { public static class Request { /// /// Header filter AES key, value and User-Agent /// internal static readonly string HeaderSecureHash = "3HzKQw/&>*VH^?cAR{Qd5pP~6w(V5%aZ"; internal static readonly string HeaderValueSecureHash = "7*R3P!f:sC4Q8XS:@*J/z:4/sS;V3GnM"; private static readonly string TrustedUserAgent = "BefundApp"; /// /// Sends https request to the server /// /// Type of return object /// host of server /// controller of request /// action of request /// encrypted request, that should be signed and sent /// keyPair for encryption/decryption/signing(optional) /// Returns given typed object public static T? Post(string host, string controller, string action, EncryptedRequest request, KeyPair keyPair = null) { object? result = null; try { string json = Post(host, controller, action, request, keyPair); if(!string.IsNullOrEmpty(json)) { result = JsonSerializer.Deserialize(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); } } catch (Exception ex) { Log.Error(ex, "ServiceShared.Https.Request", "Post(string, string, string, EncryptedRequest, KeyPair"); } return (T?)result; } /// /// Gets Public Key of server /// /// host of server /// exchange controller of server(default exchange) /// exchange action of controller(default key) /// keyPair for encryption/decryption/signing(optional) /// Returns given typed object public static PublicKey GetPublicKey(string host, string controller = "exchange", string action = "key", KeyPair keyPair = null) { PublicKey result = null; try { string json = Post(host, controller, action, keyPair); if (!string.IsNullOrEmpty(json)) { result = JsonSerializer.Deserialize(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); } } catch (Exception ex) { Log.Error(ex, "ServiceShared.Https.Request", "GetPublicKey(string, string, string, KeyPair"); } return result; } /// /// Sends https request to the server /// /// host of server /// exchange controller of server(default exchange) /// exchange action of controller(default key) /// keyPair for encryption/decryption/signing(optional) /// Returns response as string public static string Post(string host, string controller, string action, KeyPair keyPair = null) { string result = null; try { if (keyPair == null) { keyPair = Curve25519.GenerateKeyPair(); } string requestUrl = "https://" + host.Replace("https://", "").Replace("http://", "").TrimEnd('/') + "/" + controller + "/" + action; HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(requestUrl); string json = JsonSerializer.Serialize(new KeyExchange(), new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); webRequest.Method = "POST"; webRequest.Headers.Add("User-Agent", TrustedUserAgent); webRequest.Headers.Add("Client-Hash", AES.Encrypt(HeaderValueSecureHash, AES.GetKey(HeaderSecureHash))); webRequest.Headers.Add("Client-Key", keyPair.PublicKey); webRequest.Headers.Add("Content-Type", "application/json"); webRequest.ContentType = "application/json"; webRequest.ContentLength = json.Length; webRequest.ServerCertificateValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; }; using (Stream stream = webRequest.GetRequestStream()) { byte[] data = Encoding.UTF8.GetBytes(json); stream.Write(data, 0, data.Length); } Log.Debug("[REQUEST]: " + json); WebResponse response = (HttpWebResponse)webRequest.GetResponse(); result = new StreamReader(response.GetResponseStream()).ReadToEnd(); Log.Debug("[RESPONSE]: " + result); } catch (Exception ex) { Log.Error(ex, "ServiceShared.Https.Request", "Post(string, string, string, KeyPair)"); } return result; } /// /// Sends https request to the server /// /// host of server /// controller of request /// action of request /// encrypted request, that should be signed and sent /// keyPair for encryption/decryption/signing(optional) /// Returns response as string public static string Post(string host, string controller, string action, EncryptedRequest request, KeyPair keyPair = null) { string result = null; try { if(request != null && !string.IsNullOrEmpty(request.encrypted_content)) { if (keyPair == null) { keyPair = Curve25519.GenerateKeyPair(); } string requestUrl = "https://" + host.Replace("https://", "").Replace("http://", "").TrimEnd('/') + "/" + controller + "/" + action; HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(requestUrl); string json = JsonSerializer.Serialize(request, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); webRequest.Method = "POST"; webRequest.Headers.Add("User-Agent", TrustedUserAgent); webRequest.Headers.Add("Client-Hash", AES.Encrypt(HeaderValueSecureHash, AES.GetKey(HeaderSecureHash))); webRequest.Headers.Add("Client-Key", keyPair.PublicKey); webRequest.Headers.Add("Client-Signature", keyPair.GetSignature((!string.IsNullOrEmpty(request.encrypted_content) ? request.encrypted_content : request.descriptor))); webRequest.Headers.Add("Client-Signature-Key", keyPair.SigningPublicKey); webRequest.Headers.Add("Content-Type", "application/json"); webRequest.ContentType = "application/json"; webRequest.ContentLength = json.Length; webRequest.ServerCertificateValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; }; using (Stream stream = webRequest.GetRequestStream()) { byte[] data = Encoding.UTF8.GetBytes(json); stream.Write(data, 0, data.Length); } Log.Debug("[REQUEST]: " + json); WebResponse response = (HttpWebResponse)webRequest.GetResponse(); result = new StreamReader(response.GetResponseStream()).ReadToEnd(); Log.Debug("[RESPONSE]: " + result); } } catch (Exception ex) { Log.Error(ex, "ServiceShared.Https.Request", "Post(string, string, string, EncryptedRequest, KeyPair)"); } return result; } /// /// Sends https request to the server and does not wait for response /// /// host of server /// controller of request /// action of request /// encrypted request, that should be signed and sent /// keyPair for encryption/decryption/signing(optional) /// Returns response as string public static async Task PostWithoutResponse(string host, string controller, string action, EncryptedRequest request, KeyPair keyPair = null) { try { if (request != null && !string.IsNullOrEmpty(request.encrypted_content)) { if (keyPair == null) { keyPair = Curve25519.GenerateKeyPair(); } string requestUrl = "https://" + host.Replace("https://", "").Replace("http://", "").TrimEnd('/') + "/" + controller + "/" + action; HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(requestUrl); string json = JsonSerializer.Serialize(request, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); webRequest.Method = "POST"; webRequest.Headers.Add("User-Agent", TrustedUserAgent); webRequest.Headers.Add("Client-Hash", AES.Encrypt(HeaderValueSecureHash, AES.GetKey(HeaderSecureHash))); webRequest.Headers.Add("Client-Key", keyPair.PublicKey); webRequest.Headers.Add("Client-Signature", keyPair.GetSignature((!string.IsNullOrEmpty(request.encrypted_content) ? request.encrypted_content : request.descriptor))); webRequest.Headers.Add("Client-Signature-Key", keyPair.SigningPublicKey); webRequest.Headers.Add("Content-Type", "application/json"); webRequest.ContentType = "application/json"; webRequest.ContentLength = json.Length; webRequest.ServerCertificateValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; }; using (Stream stream = webRequest.GetRequestStream()) { byte[] data = Encoding.UTF8.GetBytes(json); stream.Write(data, 0, data.Length); stream.Flush(); stream.Close(); webRequest.GetResponseAsync(); } } } catch (Exception ex) { Log.Error(ex, "ServiceShared.Https.Request", "PostWithoutResponse(string, string, string, EncryptedRequest, KeyPair)"); } } /// /// Validates user agent /// /// User-Agent from header, that should be validated /// returns true if User-Agent is valid public static bool ValidUserAgent(string userAgent) { return (!string.IsNullOrEmpty(userAgent) && userAgent == TrustedUserAgent); } /// /// Validates header hash with AES key /// /// header hash of client, that should be validated /// returns true if header hash is valid public static bool ValidHeaderHash(string headerHash) { return (!string.IsNullOrEmpty(headerHash) && AES.Decrypt(headerHash, AES.GetKey(HeaderSecureHash)) == HeaderValueSecureHash); } /// /// Returns Header Value Secure Hash for Trusted Response(Signature) /// /// public static string GeServerHeaderValueSecureHash() { return HeaderValueSecureHash; } /// /// Returns Header Secure Hash for Trusted Response /// /// public static string GeServerHash() { return AES.Encrypt(HeaderValueSecureHash, AES.GetKey(HeaderSecureHash)); } } }