85 lines
3.2 KiB
C#
85 lines
3.2 KiB
C#
using System.Net.Http.Headers;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using OCSentinelCli.Models;
|
|
|
|
namespace OCSentinelCli.Transport;
|
|
|
|
internal sealed class N8nUploadClient
|
|
{
|
|
public UploadResult UploadJson(
|
|
string webhookUrl,
|
|
string machineName,
|
|
string clientVersion,
|
|
string payloadJson,
|
|
string sharedSecret,
|
|
int timeoutSeconds)
|
|
{
|
|
string timestamp = DateTimeOffset.UtcNow.ToString("O");
|
|
string nonce = Guid.NewGuid().ToString("N");
|
|
string payloadHash = ComputeSha256(payloadJson);
|
|
string signature = ComputeSignature(machineName, timestamp, nonce, clientVersion, payloadHash, sharedSecret);
|
|
|
|
using var httpClient = new HttpClient
|
|
{
|
|
Timeout = TimeSpan.FromSeconds(Math.Max(5, timeoutSeconds))
|
|
};
|
|
|
|
using var request = new HttpRequestMessage(HttpMethod.Post, webhookUrl)
|
|
{
|
|
Content = new StringContent(payloadJson, Encoding.UTF8, "application/json")
|
|
};
|
|
|
|
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
|
request.Headers.Add("X-ATN-Device", machineName);
|
|
request.Headers.Add("X-ATN-Timestamp", timestamp);
|
|
request.Headers.Add("X-ATN-Nonce", nonce);
|
|
request.Headers.Add("X-ATN-Version", clientVersion);
|
|
request.Headers.Add("X-ATN-Payload-SHA256", payloadHash);
|
|
request.Headers.Add("X-ATN-Signature", signature);
|
|
|
|
try
|
|
{
|
|
using HttpResponseMessage response = httpClient.Send(request);
|
|
string responseText = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
|
|
|
|
return new UploadResult
|
|
{
|
|
Success = response.IsSuccessStatusCode,
|
|
StatusCode = (int)response.StatusCode,
|
|
Message = string.IsNullOrWhiteSpace(responseText) ? response.ReasonPhrase ?? string.Empty : responseText,
|
|
Nonce = nonce,
|
|
PayloadSha256 = payloadHash
|
|
};
|
|
}
|
|
catch (Exception exception) when (exception is HttpRequestException or TaskCanceledException)
|
|
{
|
|
return new UploadResult
|
|
{
|
|
Success = false,
|
|
StatusCode = 0,
|
|
Message = exception.Message,
|
|
Nonce = nonce,
|
|
PayloadSha256 = payloadHash
|
|
};
|
|
}
|
|
}
|
|
|
|
private static string ComputeSha256(string value)
|
|
{
|
|
byte[] bytes = Encoding.UTF8.GetBytes(value);
|
|
byte[] hash = SHA256.HashData(bytes);
|
|
return Convert.ToHexString(hash).ToLowerInvariant();
|
|
}
|
|
|
|
private static string ComputeSignature(string machineName, string timestamp, string nonce, string version, string payloadHash, string sharedSecret)
|
|
{
|
|
string canonical = string.Join("\n", machineName, timestamp, nonce, version, payloadHash);
|
|
byte[] secretBytes = Encoding.UTF8.GetBytes(sharedSecret);
|
|
byte[] canonicalBytes = Encoding.UTF8.GetBytes(canonical);
|
|
using var hmac = new HMACSHA256(secretBytes);
|
|
byte[] hash = hmac.ComputeHash(canonicalBytes);
|
|
return Convert.ToHexString(hash).ToLowerInvariant();
|
|
}
|
|
}
|