201 lines
7.6 KiB
C#
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;
|
|
}
|
|
}
|