Files
certmgr/certmgr/Jobs/CreateCertificateSettings.cs
2025-11-02 18:32:52 +01:00

201 lines
7.6 KiB
C#

using System.Diagnostics;
using CertMgr.Certificates.CertGen;
using CertMgr.Certificates.CertGen.Utils;
using CertMgr.Core.Attributes;
using CertMgr.Core.Converters.Impl;
using CertMgr.Core.Jobs;
using CertMgr.Core.Storage;
using CertMgr.Core.Utils;
using CertMgr.Core.Validation;
namespace CertMgr.Jobs;
public sealed class CreateCertificateSettings : JobSettings
{
public CreateCertificateSettings()
{
Algorithm = CertificateAlgorithm.ECDsa;
Curve = EcdsaCurve.P384;
HashAlgorithm = Certificates.CertGen.HashAlgorithm.Sha384;
ValidityPeriod = TimeSpan.FromDays(365);
}
[Setting("subject", IsMandatory = true, Validator = typeof(SubjectValidator))]
public string? Subject { [DebuggerStepThrough] get; [DebuggerStepThrough] set; }
// value can be:
// - plain hostname
// - DNS:hostname (i.e. with 'DNS:' prefix)
// - IP:ip-address (i.e. with 'IP:' prefix)
// other types (URI, UPN, email) are not supported
[Setting("subject-alternate-name", AlternateNames = ["san"], Validator = typeof(SubjectAlternateNamesValidator))]
public IReadOnlyCollection<string>? SubjectAlternateNames { [DebuggerStepThrough] get; [DebuggerStepThrough] set; }
[Setting("friendly-name")]
public string? FriendlyName { [DebuggerStepThrough] get; [DebuggerStepThrough] set; }
[Setting("key-usage", Default = Certificates.CertGen.KeyUsage.None, Converter = typeof(EnumConverter))]
public KeyUsage? KeyUsage { [DebuggerStepThrough] get; [DebuggerStepThrough] set; }
[Setting("algorithm", Default = CertificateAlgorithm.ECDsa, Converter = typeof(EnumConverter))]
public CertificateAlgorithm? Algorithm { [DebuggerStepThrough] get; [DebuggerStepThrough] set; }
[Setting("ecdsa-curve")]
public EcdsaCurve? Curve { [DebuggerStepThrough] get; [DebuggerStepThrough] set; }
[Setting("rsa-key-size", Converter = typeof(RsaKeySizeConverter))]
public RsaKeySize? RsaKeySize { [DebuggerStepThrough] get; [DebuggerStepThrough] set; }
[Setting("hash-algorithm", AlternateNames = ["ha"])]
public HashAlgorithm? HashAlgorithm { [DebuggerStepThrough] get; [DebuggerStepThrough] set; }
[Setting("is-certificate-authority", Default = false, AlternateNames = ["isca"])]
public bool IsCertificateAuthority { [DebuggerStepThrough] get; [DebuggerStepThrough] set; }
[Setting("issuer-kind", Converter = typeof(StorageKindConverter))]
public IStorage? Issuer { [DebuggerStepThrough] get; [DebuggerStepThrough] set; }
[Setting("issuer-file", Converter = typeof(FileStorageContextConverter))]
public FileStorageContext? IssuerFileContext { [DebuggerStepThrough] get; [DebuggerStepThrough] set; }
[Setting("issuer-certstore", Converter = typeof(CertStoreStorageContextConverter))]
public CertStoreStorageContext? IssuerCertStoreContext { [DebuggerStepThrough] get; [DebuggerStepThrough] set; }
// [Setting("issuer-certificate", Converter = typeof(StorageConverter))]
// public IStorage? Issuer { [DebuggerStepThrough] get; [DebuggerStepThrough] set; }
[Setting("issuer-password", IsSecret = true)]
public string? IssuerPassword { [DebuggerStepThrough] get; [DebuggerStepThrough] set; }
[Setting("validity-period", Default = "1y", Converter = typeof(TimeSpanConverter))]
public TimeSpan? ValidityPeriod { [DebuggerStepThrough] get; [DebuggerStepThrough] set; }
// [Setting("certificate-target", IsMandatory = true, Converter = typeof(StorageConverter))]
// public IStorage? Storage { [DebuggerStepThrough] get; [DebuggerStepThrough] set; }
[Setting("target-password", IsSecret = true)]
public string? Password { [DebuggerStepThrough] get; [DebuggerStepThrough] set; }
[Setting("target-kind", Converter = typeof(StorageKindConverter))]
public IStorage? Target { [DebuggerStepThrough] get; [DebuggerStepThrough] set; }
[Setting("target-file", Converter = typeof(FileStorageContextConverter))]
public FileStorageContext? TargetFileContext { [DebuggerStepThrough] get; [DebuggerStepThrough] set; }
[Setting("target-certstore", Converter = typeof(CertStoreStorageContextConverter))]
public CertStoreStorageContext? TargetCertStoreContext { [DebuggerStepThrough] get; [DebuggerStepThrough] set; }
public StorageContext GetTargetContext()
{
if (Target == null)
{
throw new JobException("Target storage is not properly configured");
}
StorageContext? ctx;
switch (Target)
{
case FileStorage:
ctx = TargetFileContext;
break;
case CertStoreStorage:
if (TargetCertStoreContext != null && Password != null)
{
TargetCertStoreContext.Password = Password;
}
ctx = TargetCertStoreContext;
break;
default:
ctx = null;
break;
}
if (ctx == null)
{
throw new JobException("context for target storage of type '{0}' is not properly set", Target.GetType().ToString(false));
}
return ctx;
}
public StorageContext GetIssuerContext()
{
if (Issuer == null)
{
throw new JobException("Issuer storage is not properly configured");
}
StorageContext? ctx;
switch (Issuer)
{
case FileStorage:
ctx = IssuerFileContext;
break;
case CertStoreStorage:
ctx = IssuerCertStoreContext;
break;
default:
ctx = null;
break;
}
if (ctx == null)
{
throw new JobException("context for issuer storage of type '{0}' is not properly set", Issuer.GetType().ToString(false));
}
return ctx;
}
protected override Task DoValidateAsync(ValidationResults results, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(Subject))
{
results.AddInvalid(nameof(Subject), "must not be empty");
}
if (Algorithm == CertificateAlgorithm.ECDsa)
{
if (!Curve.HasValue || !Enum.IsDefined(Curve.Value))
{
results.AddInvalid(nameof(Curve), "valid value must be specified: '{0}'", Curve?.ToString() ?? "<null>");
}
}
else if (Algorithm == CertificateAlgorithm.RSA)
{
if (!RsaKeySize.HasValue || !Enum.IsDefined<RsaKeySize>(RsaKeySize.Value))
{
results.AddInvalid(nameof(RsaKeySize), "value value must be specified: '{0}'", RsaKeySize?.ToString() ?? "<null>");
}
}
if (!HashAlgorithm.HasValue || !Enum.IsDefined(HashAlgorithm.Value))
{
results.AddInvalid(nameof(HashAlgorithm), "value value must be specified: '{0}'", HashAlgorithm?.ToString() ?? "<null>");
}
if (Target == null)
{
results.AddInvalid(nameof(Target), "must be specified");
}
if (Target is FileStorage)
{
if (TargetFileContext is null)
{
results.AddInvalid(nameof(TargetFileContext), "value must be specified when target is '{0}'", Target.GetType().ToString(false));
}
}
else if (Target is CertStoreStorage)
{
if (TargetCertStoreContext is null)
{
results.AddInvalid(nameof(TargetCertStoreContext), "value must be specified when target is '{0}'", Target.GetType().ToString(false));
}
}
return Task.CompletedTask;
}
}