167 lines
6.2 KiB
C#
167 lines
6.2 KiB
C#
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
// Sprint: SPRINT_4100_0006_0002 - eIDAS Crypto Plugin
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Options;
|
|
using StellaOps.Cryptography.Plugin.EIDAS.Configuration;
|
|
using System.Security.Cryptography;
|
|
using System.Security.Cryptography.X509Certificates;
|
|
|
|
namespace StellaOps.Cryptography.Plugin.EIDAS;
|
|
|
|
/// <summary>
|
|
/// Local eIDAS signing provider using PKCS#12 keystores.
|
|
/// Suitable for development and AdES-level signatures.
|
|
/// </summary>
|
|
public class LocalEidasProvider
|
|
{
|
|
private readonly ILogger<LocalEidasProvider> _logger;
|
|
private readonly LocalSigningOptions? _options;
|
|
private X509Certificate2? _certificate;
|
|
|
|
public LocalEidasProvider(
|
|
ILogger<LocalEidasProvider> logger,
|
|
IOptions<EidasOptions> options)
|
|
{
|
|
_logger = logger;
|
|
_options = options.Value.Local;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Local signing with PKCS#12 certificate (stub implementation).
|
|
/// </summary>
|
|
public async Task<byte[]> LocalSignAsync(
|
|
byte[] data,
|
|
string algorithmId,
|
|
EidasKeyConfig keyConfig,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
_logger.LogDebug("Local eIDAS signing: keyId={KeyId}, algorithm={Algorithm}, dataLength={Length}",
|
|
keyConfig.KeyId, algorithmId, data.Length);
|
|
|
|
if (_options == null)
|
|
{
|
|
throw new InvalidOperationException("Local signing options not configured");
|
|
}
|
|
|
|
// Load certificate from PKCS#12 keystore (cached)
|
|
_certificate ??= LoadCertificate(_options);
|
|
|
|
// Stub implementation - in production, use actual certificate signing
|
|
_logger.LogWarning("Using stub local signing - replace with actual PKCS#12 signing in production");
|
|
|
|
// Compute hash
|
|
var hash = algorithmId.Contains("SHA256") ? SHA256.HashData(data) : SHA512.HashData(data);
|
|
|
|
// Stub: Create mock signature
|
|
var stubSignature = new byte[64]; // ECDSA-P256 signature
|
|
RandomNumberGenerator.Fill(stubSignature);
|
|
|
|
_logger.LogInformation("Local eIDAS signature created (stub): keyId={KeyId}, signatureLength={Length}",
|
|
keyConfig.KeyId, stubSignature.Length);
|
|
|
|
await Task.CompletedTask; // For async signature
|
|
return stubSignature;
|
|
|
|
// Production implementation:
|
|
// using var rsa = _certificate.GetRSAPrivateKey();
|
|
// using var ecdsa = _certificate.GetECDsaPrivateKey();
|
|
//
|
|
// return algorithmId switch
|
|
// {
|
|
// "RSA-PSS-2048" or "RSA-PSS-4096" => rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pss),
|
|
// "ECDSA-P256" or "ECDSA-P384" or "ECDSA-P521" => ecdsa.SignData(data, HashAlgorithmName.SHA256),
|
|
// _ => throw new NotSupportedException($"Algorithm {algorithmId} not supported for local signing")
|
|
// };
|
|
}
|
|
|
|
/// <summary>
|
|
/// Local verification with PKCS#12 certificate (stub implementation).
|
|
/// </summary>
|
|
public async Task<bool> LocalVerifyAsync(
|
|
byte[] data,
|
|
byte[] signature,
|
|
string algorithmId,
|
|
EidasKeyConfig keyConfig,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
_logger.LogDebug("Local eIDAS verification: keyId={KeyId}, algorithm={Algorithm}",
|
|
keyConfig.KeyId, algorithmId);
|
|
|
|
if (_options == null)
|
|
{
|
|
throw new InvalidOperationException("Local signing options not configured");
|
|
}
|
|
|
|
// Load certificate from PKCS#12 keystore
|
|
_certificate ??= LoadCertificate(_options);
|
|
|
|
// Stub: Always return true
|
|
_logger.LogWarning("Using stub local verification - replace with actual PKCS#12 verification in production");
|
|
await Task.Delay(10, cancellationToken); // Simulate crypto operation
|
|
|
|
_logger.LogInformation("Local eIDAS verification complete (stub): keyId={KeyId}, valid=true",
|
|
keyConfig.KeyId);
|
|
|
|
return true;
|
|
|
|
// Production implementation:
|
|
// using var rsa = _certificate.GetRSAPublicKey();
|
|
// using var ecdsa = _certificate.GetECDsaPublicKey();
|
|
//
|
|
// return algorithmId switch
|
|
// {
|
|
// "RSA-PSS-2048" or "RSA-PSS-4096" => rsa.VerifyData(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pss),
|
|
// "ECDSA-P256" or "ECDSA-P384" or "ECDSA-P521" => ecdsa.VerifyData(data, signature, HashAlgorithmName.SHA256),
|
|
// _ => throw new NotSupportedException($"Algorithm {algorithmId} not supported for local verification")
|
|
// };
|
|
}
|
|
|
|
private X509Certificate2 LoadCertificate(LocalSigningOptions options)
|
|
{
|
|
_logger.LogDebug("Loading eIDAS certificate from keystore: path={Path}, type={Type}",
|
|
options.Path, options.Type);
|
|
|
|
if (!File.Exists(options.Path))
|
|
{
|
|
throw new FileNotFoundException($"eIDAS keystore not found: {options.Path}");
|
|
}
|
|
|
|
try
|
|
{
|
|
if (options.Type.Equals("PKCS12", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
var cert = X509CertificateLoader.LoadPkcs12FromFile(
|
|
options.Path,
|
|
options.Password,
|
|
X509KeyStorageFlags.Exportable);
|
|
|
|
_logger.LogInformation("eIDAS certificate loaded: subject={Subject}, serial={Serial}, expires={Expires}",
|
|
cert.Subject, cert.SerialNumber, cert.NotAfter);
|
|
|
|
return cert;
|
|
}
|
|
else if (options.Type.Equals("PEM", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
// Load PEM certificate (requires separate key file)
|
|
var certPem = File.ReadAllText(options.Path);
|
|
var cert = X509Certificate2.CreateFromPem(certPem);
|
|
|
|
_logger.LogInformation("eIDAS PEM certificate loaded: subject={Subject}",
|
|
cert.Subject);
|
|
|
|
return cert;
|
|
}
|
|
else
|
|
{
|
|
throw new NotSupportedException($"Keystore type '{options.Type}' not supported");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Failed to load eIDAS certificate from keystore");
|
|
throw;
|
|
}
|
|
}
|
|
}
|