Files
123123/ZA.CoreService.ESBCertificateManager/Setup/AlwaysEncryptedCertificateService.cs
2026-07-28 08:03:12 +02:00

154 lines
4.5 KiB
C#

using System.Security.Cryptography.X509Certificates;
using ZA.CoreService.ESBCertificateManager.Models;
using static ZA.CoreService.ESBCertificateManager.Models.RuntimeSettings;
namespace ZA.CoreService.ESBCertificateManager.Services;
public sealed class AlwaysEncryptedCertificateService
{
private readonly string _expectedThumbprint;
private readonly StoreName _storeName;
private readonly StoreLocation _storeLocation;
public AlwaysEncryptedCertificateService(
AlwaysEncryptedSettings settings)
{
if (string.IsNullOrWhiteSpace(
settings.CertificateThumbprint))
{
throw new InvalidOperationException(
"AlwaysEncrypted:CertificateThumbprint fehlt.");
}
_expectedThumbprint = NormalizeThumbprint(
settings.CertificateThumbprint);
if (!Enum.TryParse(
settings.StoreName,
ignoreCase: true,
out _storeName))
{
throw new InvalidOperationException(
$"Ungültiger Zertifikatsspeicher: '{settings.StoreName}'.");
}
if (!Enum.TryParse(
settings.StoreLocation,
ignoreCase: true,
out _storeLocation))
{
throw new InvalidOperationException(
$"Ungültiger Zertifikatsspeicherort: " +
$"'{settings.StoreLocation}'.");
}
}
public CertificateCheckResult CheckCertificate()
{
using X509Store store = new(
_storeName,
_storeLocation);
store.Open(OpenFlags.ReadOnly);
X509Certificate2? certificate = store.Certificates
.Find(
X509FindType.FindByThumbprint,
_expectedThumbprint,
validOnly: false)
.OfType<X509Certificate2>()
.FirstOrDefault();
if (certificate is null)
{
return new CertificateCheckResult(
IsValid: false,
Message:
$"Das Always-Encrypted-Zertifikat wurde nicht gefunden. " +
$"Speicher: {_storeLocation}\\{_storeName}, " +
$"Thumbprint: {_expectedThumbprint}");
}
if (!certificate.HasPrivateKey)
{
return new CertificateCheckResult(
IsValid: false,
Message:
"Das Always-Encrypted-Zertifikat wurde gefunden, " +
"besitzt aber keinen verfügbaren privaten Schlüssel.");
}
return new CertificateCheckResult(
IsValid: true,
Message:
"Always-Encrypted-Zertifikat und privater " +
"Schlüssel sind verfügbar.");
}
public void ImportPfx(
string pfxFilePath,
string pfxPassword)
{
if (string.IsNullOrWhiteSpace(pfxFilePath))
{
throw new ArgumentException(
"Es wurde keine PFX-Datei ausgewählt.",
nameof(pfxFilePath));
}
if (!File.Exists(pfxFilePath))
{
throw new FileNotFoundException(
"Die ausgewählte PFX-Datei wurde nicht gefunden.",
pfxFilePath);
}
X509KeyStorageFlags flags =
X509KeyStorageFlags.UserKeySet
| X509KeyStorageFlags.PersistKeySet;
using X509Certificate2 certificate = new(
pfxFilePath,
pfxPassword,
flags);
string importedThumbprint =
NormalizeThumbprint(certificate.Thumbprint);
if (!string.Equals(
importedThumbprint,
_expectedThumbprint,
StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException(
"Die ausgewählte PFX-Datei gehört nicht zum " +
"erwarteten Always-Encrypted-Zertifikat.");
}
if (!certificate.HasPrivateKey)
{
throw new InvalidOperationException(
"Die ausgewählte PFX-Datei enthält keinen privaten Schlüssel.");
}
using X509Store store = new(
_storeName,
_storeLocation);
store.Open(OpenFlags.ReadWrite);
store.Add(certificate);
}
private static string NormalizeThumbprint(
string thumbprint)
{
return thumbprint
.Replace(" ", string.Empty)
.Trim()
.ToUpperInvariant();
}
}
public sealed record CertificateCheckResult(
bool IsValid,
string Message);