sln build fix (again), tests fixes, audit work and doctors work
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using StellaOps.Doctor.Models;
|
||||
using StellaOps.Doctor.Plugins;
|
||||
|
||||
namespace StellaOps.Doctor.Plugins.Cryptography.Checks;
|
||||
|
||||
/// <summary>
|
||||
/// Validates eIDAS (EU qualified signatures) provider configuration.
|
||||
/// </summary>
|
||||
public sealed class EidasProviderCheck : IDoctorCheck
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string CheckId => "check.crypto.eidas";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => "eIDAS Provider";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Description => "Validates eIDAS qualified signature provider configuration";
|
||||
|
||||
/// <inheritdoc />
|
||||
public DoctorSeverity DefaultSeverity => DoctorSeverity.Info;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<string> Tags => ["cryptography", "eidas", "qualified", "eu", "regional"];
|
||||
|
||||
/// <inheritdoc />
|
||||
public TimeSpan EstimatedDuration => TimeSpan.FromMilliseconds(100);
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool CanRun(DoctorPluginContext context)
|
||||
{
|
||||
// Only run if eIDAS is configured or enabled
|
||||
var eidasEnabled = context.Configuration.GetValue<bool?>("Cryptography:Eidas:Enabled")
|
||||
?? context.Configuration.GetValue<bool?>("Cryptography:EnableEidas");
|
||||
|
||||
return eidasEnabled == true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<DoctorCheckResult> RunAsync(DoctorPluginContext context, CancellationToken ct)
|
||||
{
|
||||
var result = context.CreateResult(CheckId, "stellaops.doctor.cryptography", DoctorCategory.Cryptography.ToString());
|
||||
|
||||
var eidasProvider = context.Configuration.GetValue<string>("Cryptography:Eidas:Provider")
|
||||
?? "pkcs11";
|
||||
|
||||
var trustListUrl = context.Configuration.GetValue<string>("Cryptography:Eidas:TrustListUrl")
|
||||
?? "https://ec.europa.eu/tools/lotl/eu-lotl.xml";
|
||||
|
||||
var issues = new List<string>();
|
||||
var providerInfo = new Dictionary<string, string>
|
||||
{
|
||||
["ConfiguredProvider"] = eidasProvider,
|
||||
["TrustListUrl"] = trustListUrl
|
||||
};
|
||||
|
||||
switch (eidasProvider.ToLowerInvariant())
|
||||
{
|
||||
case "pkcs11":
|
||||
CheckPkcs11Eidas(issues, providerInfo, context.Configuration);
|
||||
break;
|
||||
|
||||
case "certificate":
|
||||
CheckCertificateEidas(issues, providerInfo, context.Configuration);
|
||||
break;
|
||||
|
||||
case "remote":
|
||||
CheckRemoteEidas(issues, providerInfo, context.Configuration);
|
||||
break;
|
||||
|
||||
default:
|
||||
issues.Add($"Unknown eIDAS provider: {eidasProvider}");
|
||||
break;
|
||||
}
|
||||
|
||||
if (issues.Count > 0)
|
||||
{
|
||||
return Task.FromResult(result
|
||||
.Warn($"{issues.Count} eIDAS provider issue(s)")
|
||||
.WithEvidence("eIDAS configuration", e =>
|
||||
{
|
||||
foreach (var kvp in providerInfo)
|
||||
{
|
||||
e.Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
})
|
||||
.WithCauses(issues.ToArray())
|
||||
.WithRemediation(r => r
|
||||
.AddManualStep(1, "Configure provider", "Configure PKCS#11 library or certificate store for eIDAS")
|
||||
.AddManualStep(2, "Verify trust list", "Ensure EU Trust List is accessible"))
|
||||
.WithVerification("stella doctor --check check.crypto.eidas")
|
||||
.Build());
|
||||
}
|
||||
|
||||
return Task.FromResult(result
|
||||
.Pass("eIDAS provider is configured")
|
||||
.WithEvidence("eIDAS configuration", e =>
|
||||
{
|
||||
foreach (var kvp in providerInfo)
|
||||
{
|
||||
e.Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
})
|
||||
.Build());
|
||||
}
|
||||
|
||||
private static void CheckPkcs11Eidas(List<string> issues, Dictionary<string, string> providerInfo, IConfiguration config)
|
||||
{
|
||||
providerInfo["Provider"] = "PKCS#11 (Smart Card/HSM)";
|
||||
|
||||
var pkcs11Library = config.GetValue<string>("Cryptography:Eidas:Pkcs11Library");
|
||||
if (string.IsNullOrWhiteSpace(pkcs11Library))
|
||||
{
|
||||
issues.Add("PKCS#11 library path not configured for eIDAS");
|
||||
providerInfo["Library"] = "(not set)";
|
||||
}
|
||||
else if (!File.Exists(pkcs11Library))
|
||||
{
|
||||
issues.Add($"PKCS#11 library not found: {pkcs11Library}");
|
||||
providerInfo["Library"] = pkcs11Library;
|
||||
}
|
||||
else
|
||||
{
|
||||
providerInfo["Library"] = pkcs11Library;
|
||||
}
|
||||
|
||||
var slotId = config.GetValue<int?>("Cryptography:Eidas:SlotId");
|
||||
providerInfo["SlotId"] = slotId?.ToString() ?? "(auto-detect)";
|
||||
}
|
||||
|
||||
private static void CheckCertificateEidas(List<string> issues, Dictionary<string, string> providerInfo, IConfiguration config)
|
||||
{
|
||||
providerInfo["Provider"] = "Certificate Store";
|
||||
|
||||
var certThumbprint = config.GetValue<string>("Cryptography:Eidas:CertificateThumbprint");
|
||||
var certPath = config.GetValue<string>("Cryptography:Eidas:CertificatePath");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(certThumbprint))
|
||||
{
|
||||
providerInfo["CertificateThumbprint"] = certThumbprint;
|
||||
|
||||
// Try to find certificate in store
|
||||
try
|
||||
{
|
||||
using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
|
||||
store.Open(OpenFlags.ReadOnly);
|
||||
var certs = store.Certificates.Find(
|
||||
X509FindType.FindByThumbprint,
|
||||
certThumbprint,
|
||||
validOnly: false);
|
||||
|
||||
if (certs.Count == 0)
|
||||
{
|
||||
issues.Add($"Certificate with thumbprint {certThumbprint[..8]}... not found in store");
|
||||
}
|
||||
else
|
||||
{
|
||||
var cert = certs[0];
|
||||
providerInfo["CertificateSubject"] = cert.Subject;
|
||||
providerInfo["CertificateExpiry"] = cert.NotAfter.ToString("yyyy-MM-dd");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
issues.Add($"Cannot access certificate store: {ex.Message}");
|
||||
}
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(certPath))
|
||||
{
|
||||
providerInfo["CertificatePath"] = certPath;
|
||||
if (!File.Exists(certPath))
|
||||
{
|
||||
issues.Add($"Certificate file not found: {certPath}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
issues.Add("No certificate thumbprint or path configured for eIDAS");
|
||||
}
|
||||
}
|
||||
|
||||
private static void CheckRemoteEidas(List<string> issues, Dictionary<string, string> providerInfo, IConfiguration config)
|
||||
{
|
||||
providerInfo["Provider"] = "Remote Signing Service";
|
||||
|
||||
var remoteEndpoint = config.GetValue<string>("Cryptography:Eidas:RemoteEndpoint");
|
||||
if (string.IsNullOrWhiteSpace(remoteEndpoint))
|
||||
{
|
||||
issues.Add("Remote eIDAS signing endpoint not configured");
|
||||
providerInfo["Endpoint"] = "(not set)";
|
||||
}
|
||||
else
|
||||
{
|
||||
providerInfo["Endpoint"] = remoteEndpoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user