119 lines
4.1 KiB
C#
119 lines
4.1 KiB
C#
using System;
|
|
using System.Net.Http;
|
|
using System.Net.Http.Headers;
|
|
using System.Security.Cryptography.X509Certificates;
|
|
using System.Threading.Tasks;
|
|
|
|
// This file is part of the EonaCat project(s) which is released under the Apache License.
|
|
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
|
|
|
namespace EonaCat.SecretVault.Client
|
|
{
|
|
/// <summary>
|
|
/// Client for securely storing and retrieving secrets from a SecretVault server.
|
|
/// </summary>
|
|
public class SecretVaultClient : IDisposable
|
|
{
|
|
private readonly HttpClient _http;
|
|
private readonly string _baseUrl;
|
|
|
|
public SecretVaultClient(string baseUrl, string apiKey, bool isHttps = true, X509Certificate2? expectedCert = null)
|
|
{
|
|
if (isHttps)
|
|
{
|
|
if (!baseUrl.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
throw new ArgumentException("Base URL must use HTTPS for secure communication.");
|
|
}
|
|
}
|
|
|
|
_baseUrl = baseUrl.TrimEnd('/') + "/";
|
|
|
|
var handler = new HttpClientHandler();
|
|
|
|
if (expectedCert != null)
|
|
{
|
|
handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
|
|
{
|
|
return cert != null && cert.GetCertHashString() == expectedCert.GetCertHashString();
|
|
};
|
|
}
|
|
|
|
_http = new HttpClient(handler)
|
|
{
|
|
BaseAddress = new Uri(_baseUrl),
|
|
Timeout = TimeSpan.FromSeconds(10)
|
|
};
|
|
|
|
_http.DefaultRequestHeaders.Add("X-Api-Key", apiKey);
|
|
}
|
|
|
|
public SecretVaultClient(HttpClient httpClient, string apiKey, bool isHttps = true, X509Certificate2? expectedCert = null)
|
|
{
|
|
_http = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
|
|
|
|
if (isHttps)
|
|
{
|
|
if (!_http.BaseAddress.AbsoluteUri.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
throw new ArgumentException("Base URL must use HTTPS for secure communication.");
|
|
}
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(apiKey))
|
|
{
|
|
throw new ArgumentException("API key cannot be null or empty", nameof(apiKey));
|
|
}
|
|
_http.DefaultRequestHeaders.Add("X-Api-Key", apiKey);
|
|
|
|
if (expectedCert != null)
|
|
{
|
|
var handler = new HttpClientHandler();
|
|
handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
|
|
{
|
|
return cert != null && cert.GetCertHashString() == expectedCert.GetCertHashString();
|
|
};
|
|
|
|
// Dispose the old HttpClient if it exists
|
|
_http?.Dispose();
|
|
_http = new HttpClient(handler)
|
|
{
|
|
BaseAddress = _http.BaseAddress,
|
|
Timeout = _http.Timeout,
|
|
MaxResponseContentBufferSize = _http.MaxResponseContentBufferSize,
|
|
};
|
|
|
|
_http.DefaultRequestHeaders.Add("X-Api-Key", apiKey);
|
|
}
|
|
}
|
|
|
|
public async Task StoreAsync(string key, string secret)
|
|
{
|
|
var content = new StringContent($"\"{secret}\"");
|
|
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
|
|
await _http.PostAsync($"api/secrets/store?key={Uri.EscapeDataString(key)}", content);
|
|
}
|
|
|
|
public async Task DeleteAsync(string key)
|
|
{
|
|
await _http.DeleteAsync($"api/secrets/delete?key={Uri.EscapeDataString(key)}");
|
|
}
|
|
|
|
public async Task<string?> RetrieveAsync(string key)
|
|
{
|
|
var res = await _http.GetAsync($"api/secrets/retrieve?key={Uri.EscapeDataString(key)}");
|
|
if (!res.IsSuccessStatusCode)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return await res.Content.ReadAsStringAsync();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_http.Dispose();
|
|
}
|
|
}
|
|
}
|