304 lines
13 KiB
C#
304 lines
13 KiB
C#
|
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
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// Header filter AES key, value and User-Agent
|
|||
|
/// </summary>
|
|||
|
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";
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Sends https request to the server
|
|||
|
/// </summary>
|
|||
|
/// <typeparam name="T">Type of return object</typeparam>
|
|||
|
/// <param name="host">host of server</param>
|
|||
|
/// <param name="controller">controller of request</param>
|
|||
|
/// <param name="action">action of request</param>
|
|||
|
/// <param name="request">encrypted request, that should be signed and sent</param>
|
|||
|
/// <param name="keyPair">keyPair for encryption/decryption/signing(optional)</param>
|
|||
|
/// <returns>Returns given typed object</returns>
|
|||
|
public static T? Post<T>(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<T>(json, new JsonSerializerOptions
|
|||
|
{
|
|||
|
PropertyNameCaseInsensitive = true
|
|||
|
});
|
|||
|
}
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
Log.Error(ex, "ServiceShared.Https.Request", "Post<T>(string, string, string, EncryptedRequest, KeyPair");
|
|||
|
}
|
|||
|
|
|||
|
return (T?)result;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets Public Key of server
|
|||
|
/// </summary>
|
|||
|
/// <param name="host">host of server</param>
|
|||
|
/// <param name="controller">exchange controller of server(default exchange)</param>
|
|||
|
/// <param name="action">exchange action of controller(default key)</param>
|
|||
|
/// <param name="keyPair">keyPair for encryption/decryption/signing(optional)</param>
|
|||
|
/// <returns>Returns given typed object</returns>
|
|||
|
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<PublicKey>(json, new JsonSerializerOptions
|
|||
|
{
|
|||
|
PropertyNameCaseInsensitive = true
|
|||
|
});
|
|||
|
}
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
Log.Error(ex, "ServiceShared.Https.Request", "GetPublicKey(string, string, string, KeyPair");
|
|||
|
}
|
|||
|
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Sends https request to the server
|
|||
|
/// </summary>
|
|||
|
/// <param name="host">host of server</param>
|
|||
|
/// <param name="controller">exchange controller of server(default exchange)</param>
|
|||
|
/// <param name="action">exchange action of controller(default key)</param>
|
|||
|
/// <param name="keyPair">keyPair for encryption/decryption/signing(optional)</param>
|
|||
|
/// <returns>Returns response as string</returns>
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Sends https request to the server
|
|||
|
/// </summary>
|
|||
|
/// <param name="host">host of server</param>
|
|||
|
/// <param name="controller">controller of request</param>
|
|||
|
/// <param name="action">action of request</param>
|
|||
|
/// <param name="request">encrypted request, that should be signed and sent</param>
|
|||
|
/// <param name="keyPair">keyPair for encryption/decryption/signing(optional)</param>
|
|||
|
/// <returns>Returns response as string</returns>
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Sends https request to the server and does not wait for response
|
|||
|
/// </summary>
|
|||
|
/// <param name="host">host of server</param>
|
|||
|
/// <param name="controller">controller of request</param>
|
|||
|
/// <param name="action">action of request</param>
|
|||
|
/// <param name="request">encrypted request, that should be signed and sent</param>
|
|||
|
/// <param name="keyPair">keyPair for encryption/decryption/signing(optional)</param>
|
|||
|
/// <returns>Returns response as string</returns>
|
|||
|
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)");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Validates user agent
|
|||
|
/// </summary>
|
|||
|
/// <param name="userAgent">User-Agent from header, that should be validated</param>
|
|||
|
/// <returns>returns true if User-Agent is valid</returns>
|
|||
|
public static bool ValidUserAgent(string userAgent)
|
|||
|
{
|
|||
|
return (!string.IsNullOrEmpty(userAgent) && userAgent == TrustedUserAgent);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Validates header hash with AES key
|
|||
|
/// </summary>
|
|||
|
/// <param name="headerHash">header hash of client, that should be validated</param>
|
|||
|
/// <returns>returns true if header hash is valid</returns>
|
|||
|
public static bool ValidHeaderHash(string headerHash)
|
|||
|
{
|
|||
|
return (!string.IsNullOrEmpty(headerHash) && AES.Decrypt(headerHash, AES.GetKey(HeaderSecureHash)) == HeaderValueSecureHash);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Returns Header Value Secure Hash for Trusted Response(Signature)
|
|||
|
/// </summary>
|
|||
|
/// <returns></returns>
|
|||
|
public static string GeServerHeaderValueSecureHash()
|
|||
|
{
|
|||
|
return HeaderValueSecureHash;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Returns Header Secure Hash for Trusted Response
|
|||
|
/// </summary>
|
|||
|
/// <returns></returns>
|
|||
|
public static string GeServerHash()
|
|||
|
{
|
|||
|
return AES.Encrypt(HeaderValueSecureHash, AES.GetKey(HeaderSecureHash));
|
|||
|
}
|
|||
|
}
|
|||
|
}
|