fixes
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -117,23 +117,11 @@ internal abstract class CertificateGeneratorBase<TAlgorithm, TSettings> : ICerti
|
|||||||
using (X509Certificate2 publicOnlyCert = request.Create(settings.Issuer.SubjectName, sgen, notBefore, notAfter, serial))
|
using (X509Certificate2 publicOnlyCert = request.Create(settings.Issuer.SubjectName, sgen, notBefore, notAfter, serial))
|
||||||
{
|
{
|
||||||
cert = JoinPrivateKey(publicOnlyCert, privateKey);
|
cert = JoinPrivateKey(publicOnlyCert, privateKey);
|
||||||
// using (X509Certificate2 temp = JoinPrivateKey(publicOnlyCert, privateKey))
|
|
||||||
// {
|
|
||||||
// // Generated instance of the cert can't be added to cert-store (private key is missing) if requested by the caller.
|
|
||||||
// // To avoid this recreate the cert
|
|
||||||
// cert = Recreate(temp, settings);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cert = request.CreateSelfSigned(notBefore, notAfter);
|
cert = request.CreateSelfSigned(notBefore, notAfter);
|
||||||
// using (X509Certificate2 temp = request.CreateSelfSigned(notBefore, notAfter))
|
|
||||||
// {
|
|
||||||
// // Generated instance of the cert can't be added to cert-store (private key is missing) if requested by the caller.
|
|
||||||
// // To avoid this recreate the cert
|
|
||||||
// cert = Recreate(temp, settings);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,7 +158,7 @@ internal abstract class CertificateGeneratorBase<TAlgorithm, TSettings> : ICerti
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
AsymmetricAlgorithm pk = issuerCertificate.PrivateKey;
|
AsymmetricAlgorithm? pk = issuerCertificate.PrivateKey;
|
||||||
throw new CertGenException("Unsupported type of private-key: '{0}'", pk?.GetType().FullName ?? "<null>");
|
throw new CertGenException("Unsupported type of private-key: '{0}'", pk?.GetType().FullName ?? "<null>");
|
||||||
}
|
}
|
||||||
catch (CertGenException)
|
catch (CertGenException)
|
||||||
@@ -201,22 +189,13 @@ internal abstract class CertificateGeneratorBase<TAlgorithm, TSettings> : ICerti
|
|||||||
return serial;
|
return serial;
|
||||||
}
|
}
|
||||||
|
|
||||||
private X509Certificate2 Recreate(X509Certificate2 source, CertificateSettings settings)
|
|
||||||
{
|
|
||||||
byte[] data = source.Export(X509ContentType.Pfx, string.Empty);
|
|
||||||
X509KeyStorageFlags flags = X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet;
|
|
||||||
if (!settings.ExportableKeys)
|
|
||||||
{
|
|
||||||
flags &= ~X509KeyStorageFlags.Exportable;
|
|
||||||
}
|
|
||||||
|
|
||||||
X509Certificate2 target = X509CertificateLoader.LoadPkcs12(data, string.Empty);
|
|
||||||
target.FriendlyName = settings.FriendlyName;
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string CreateCommonName(string? subjectName)
|
private string CreateCommonName(string? subjectName)
|
||||||
{
|
{
|
||||||
|
if (subjectName == null)
|
||||||
|
{
|
||||||
|
throw new CertGenException("Cannot create common-name for certificate as subject-name is null");
|
||||||
|
}
|
||||||
|
|
||||||
string cn;
|
string cn;
|
||||||
|
|
||||||
if (subjectName.StartsWith("CN=", StringComparison.OrdinalIgnoreCase))
|
if (subjectName.StartsWith("CN=", StringComparison.OrdinalIgnoreCase))
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace CertMgr.CertGen.Utils;
|
namespace CertMgr.CertGen.Utils;
|
||||||
|
|
||||||
public sealed class CollectionEquivalencyComparer<T> : IEqualityComparer<IEnumerable<T>>
|
public sealed class CollectionEquivalencyComparer<T> : IEqualityComparer<IEnumerable<T>> where T : notnull
|
||||||
{
|
{
|
||||||
public bool Equals(IEnumerable<T>? x, IEnumerable<T>? y)
|
public bool Equals(IEnumerable<T>? x, IEnumerable<T>? y)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ public sealed class SettingAttribute : Attribute
|
|||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
IsMandatory = false;
|
IsMandatory = false;
|
||||||
|
AlternateNames = Array.Empty<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Name { [DebuggerStepThrough] get; }
|
public string Name { [DebuggerStepThrough] get; }
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ public sealed class CliParser
|
|||||||
{
|
{
|
||||||
public Task<RawArguments> ParseAsync(string[] args, CancellationToken cancellationToken)
|
public Task<RawArguments> ParseAsync(string[] args, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
CLog.Info("parsing arguments...");
|
CLog.Info("Parsing arguments...");
|
||||||
|
|
||||||
RawArguments rawArgs = new RawArguments(args.Length);
|
RawArguments rawArgs = new RawArguments(args.Length);
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ public sealed class CliParser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CLog.Info("parsing arguments... done (found {0} arguments)", rawArgs.Count);
|
CLog.Info("Parsing arguments... done (found {0} arguments)", rawArgs.Count);
|
||||||
|
|
||||||
return Task.FromResult(rawArgs);
|
return Task.FromResult(rawArgs);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ public sealed class StringConverter : ValueConverter<string>
|
|||||||
{
|
{
|
||||||
protected override Task<string?> DoConvertAsync(string rawValue, Type targetType, CancellationToken cancellationToken)
|
protected override Task<string?> DoConvertAsync(string rawValue, Type targetType, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return Task.FromResult(rawValue);
|
return Task.FromResult((string?)rawValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,11 @@ public sealed class TimeSpanConverter : ValueConverter<TimeSpan?>
|
|||||||
|
|
||||||
TimeSpan? result;
|
TimeSpan? result;
|
||||||
|
|
||||||
if (int.TryParse(valueSpan, out int value))
|
if (TimeSpan.TryParse(valueSpan, out TimeSpan tmp))
|
||||||
|
{
|
||||||
|
result = tmp;
|
||||||
|
}
|
||||||
|
else if (int.TryParse(valueSpan, out int value))
|
||||||
{
|
{
|
||||||
switch (unit)
|
switch (unit)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Reflection;
|
using System.Diagnostics;
|
||||||
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
using CertMgr.Core.Cli;
|
using CertMgr.Core.Cli;
|
||||||
@@ -24,10 +25,32 @@ internal sealed class JobExecutor
|
|||||||
|
|
||||||
if (jobs.TryGet(rawArg.Values.First(), out JobDescriptor? descriptor))
|
if (jobs.TryGet(rawArg.Values.First(), out JobDescriptor? descriptor))
|
||||||
{
|
{
|
||||||
IJob job = await CreateJobAsync(descriptor, rawArgs, cancellationToken).ConfigureAwait(false);
|
IJob job;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
job = await CreateJobAsync(descriptor, rawArgs, cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
CLog.Error(e, "Failed to instantiate job '{0}'", rawArg.Values.First());
|
||||||
|
throw new JobException(e, "Failed to instantiate job '{0}'", rawArg.Values.First());
|
||||||
|
}
|
||||||
|
|
||||||
JobResult result = await job.ExecuteAsync(cancellationToken).ConfigureAwait(false);
|
Stopwatch sw = Stopwatch.StartNew();
|
||||||
errorLevel = result.ErrorLevel;
|
try
|
||||||
|
{
|
||||||
|
CLog.Info("Executing job '{0}'...", job.Name);
|
||||||
|
|
||||||
|
JobResult result = await job.ExecuteAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
errorLevel = result.ErrorLevel;
|
||||||
|
|
||||||
|
CLog.Info("Executing job '{0}'... done (finished with error-level {1}, took {2})", job.Name, errorLevel, sw.Elapsed);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
CLog.Info("Executing job '{0}'... done (threw exception after {1})", job.Name, sw.Elapsed);
|
||||||
|
throw new JobException(e, "Failed to execute job '{0}'", job.Name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -49,7 +49,14 @@ internal sealed class JobRegistry
|
|||||||
{
|
{
|
||||||
if (jobName == null && settingsType != null)
|
if (jobName == null && settingsType != null)
|
||||||
{
|
{
|
||||||
CLog.Error(errorMessage);
|
if (errorMessage != null)
|
||||||
|
{
|
||||||
|
CLog.Error(errorMessage);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CLog.Error("Failed to get name of job from type '{0}'", type.ToString(false));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (jobName != null && settingsType == null)
|
if (jobName != null && settingsType == null)
|
||||||
{
|
{
|
||||||
@@ -59,7 +66,7 @@ internal sealed class JobRegistry
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CLog.Info("Loading job registry... done (registered {0} jobs)", _items.Count);
|
CLog.Info("Loading job registry... done (found {0} jobs)", _items.Count);
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using CertMgr.Core.Cli;
|
|||||||
using CertMgr.Core.Converters;
|
using CertMgr.Core.Converters;
|
||||||
using CertMgr.Core.Jobs;
|
using CertMgr.Core.Jobs;
|
||||||
using CertMgr.Core.Log;
|
using CertMgr.Core.Log;
|
||||||
|
using CertMgr.Core.Utils;
|
||||||
using CertMgr.Core.Validation;
|
using CertMgr.Core.Validation;
|
||||||
|
|
||||||
namespace CertMgr.Core;
|
namespace CertMgr.Core;
|
||||||
@@ -34,21 +35,29 @@ internal sealed class SettingsBuilder
|
|||||||
{
|
{
|
||||||
(bool isCollection, Type elementType) = GetValueType(propertyInfo);
|
(bool isCollection, Type elementType) = GetValueType(propertyInfo);
|
||||||
|
|
||||||
(bool converted, object? convertedValue) = await ConvertRawValueAsync(settingAttribute, rawArg, isCollection, elementType, cancellationToken).ConfigureAwait(false);
|
try
|
||||||
if (converted)
|
|
||||||
{
|
{
|
||||||
propertyInfo.SetValue(settings, convertedValue);
|
(bool converted, object? convertedValue) = await ConvertRawValueAsync(settingAttribute, rawArg, isCollection, propertyInfo.PropertyType, elementType, cancellationToken).ConfigureAwait(false);
|
||||||
|
if (converted)
|
||||||
if (settingAttribute.Validator != null)
|
|
||||||
{
|
{
|
||||||
ISettingValidator? validator = (ISettingValidator?)Activator.CreateInstance(settingAttribute.Validator, [settingAttribute.Name]);
|
propertyInfo.SetValue(settings, convertedValue);
|
||||||
if (validator != null)
|
|
||||||
|
if (settingAttribute.Validator != null)
|
||||||
{
|
{
|
||||||
ValidationResult valres = await validator.ValidateAsync(convertedValue, cancellationToken).ConfigureAwait(false);
|
ISettingValidator? validator = (ISettingValidator?)Activator.CreateInstance(settingAttribute.Validator, [settingAttribute.Name]);
|
||||||
settings.ValidationResults.Add(valres);
|
if (validator != null)
|
||||||
|
{
|
||||||
|
ValidationResult valres = await validator.ValidateAsync(convertedValue, cancellationToken).ConfigureAwait(false);
|
||||||
|
settings.ValidationResults.Add(valres);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
CLog.Error(e, "Failed to process property '{0}' (of type '{1}') in settings of type '{2}'", propertyInfo.Name, propertyInfo.PropertyType.ToString(false), _settingsType.ToString(false));
|
||||||
|
throw new CertMgrException(e, "Failed to process property '{0}' (of type '{1}') in settings of type '{2}'", propertyInfo.Name, propertyInfo.PropertyType.ToString(false), _settingsType.ToString(false));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (settingAttribute.IsMandatory)
|
else if (settingAttribute.IsMandatory)
|
||||||
{
|
{
|
||||||
@@ -65,7 +74,7 @@ internal sealed class SettingsBuilder
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
CLog.Error("Default value for argument '{0}' is specified, but its type is '{1}' instead of expected '{2}'", settingAttribute.Name, settingAttribute.Default?.GetType().Name ?? "<null>", elementType);
|
CLog.Error("Default value for argument '{0}' is specified, but its type is '{1}' instead of expected '{2}'", settingAttribute.Name, settingAttribute.Default?.GetType().ToString(false) ?? "<null>", elementType.ToString(false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -141,7 +150,7 @@ internal sealed class SettingsBuilder
|
|||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<(bool success, object? convertedValue)> ConvertRawValueAsync(SettingAttribute settingAttribute, RawArgument rawArg, bool isCollection, Type targetType, CancellationToken cancellationToken)
|
private async Task<(bool success, object? convertedValue)> ConvertRawValueAsync(SettingAttribute settingAttribute, RawArgument rawArg, bool isCollection, Type collectionType, Type elementType, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
bool success = false;
|
bool success = false;
|
||||||
object? convertedValue = null;
|
object? convertedValue = null;
|
||||||
@@ -150,10 +159,12 @@ internal sealed class SettingsBuilder
|
|||||||
{
|
{
|
||||||
if (TryGetCustomConverter(settingAttribute, out IValueConverter? customConverter))
|
if (TryGetCustomConverter(settingAttribute, out IValueConverter? customConverter))
|
||||||
{
|
{
|
||||||
List<object?> values = new List<object?>();
|
Type listType = typeof(List<>).MakeGenericType(elementType);
|
||||||
|
IList values = (IList)Activator.CreateInstance(listType)!;
|
||||||
|
|
||||||
foreach (string rawValue in rawArg.Values)
|
foreach (string rawValue in rawArg.Values)
|
||||||
{
|
{
|
||||||
convertedValue = await customConverter.ConvertAsync(rawValue, targetType, cancellationToken).ConfigureAwait(false);
|
convertedValue = await customConverter.ConvertAsync(rawValue, elementType, cancellationToken).ConfigureAwait(false);
|
||||||
values.Add(convertedValue);
|
values.Add(convertedValue);
|
||||||
}
|
}
|
||||||
convertedValue = values;
|
convertedValue = values;
|
||||||
@@ -161,15 +172,17 @@ internal sealed class SettingsBuilder
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
List<object?> values = new List<object?>();
|
Type listType = typeof(List<>).MakeGenericType(elementType);
|
||||||
|
IList values = (IList)Activator.CreateInstance(listType)!;
|
||||||
|
|
||||||
foreach (string rawValue in rawArg.Values)
|
foreach (string rawValue in rawArg.Values)
|
||||||
{
|
{
|
||||||
if (TryConvertValue(rawValue, targetType, out convertedValue))
|
if (TryConvertValue(rawValue, elementType, out convertedValue))
|
||||||
{
|
{
|
||||||
values.Add(convertedValue);
|
values.Add(convertedValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
convertedValue = values;
|
convertedValue = values; // BuildCollectionValue(collectionType, elementType, values);
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -177,10 +190,10 @@ internal sealed class SettingsBuilder
|
|||||||
{
|
{
|
||||||
if (TryGetCustomConverter(settingAttribute, out IValueConverter? customConverter))
|
if (TryGetCustomConverter(settingAttribute, out IValueConverter? customConverter))
|
||||||
{
|
{
|
||||||
convertedValue = await customConverter.ConvertAsync(rawArg.Values.First(), targetType, cancellationToken).ConfigureAwait(false);
|
convertedValue = await customConverter.ConvertAsync(rawArg.Values.First(), elementType, cancellationToken).ConfigureAwait(false);
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
else if (TryConvertValue(rawArg.Values.First(), targetType, out convertedValue))
|
else if (TryConvertValue(rawArg.Values.First(), elementType, out convertedValue))
|
||||||
{
|
{
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
@@ -192,6 +205,58 @@ internal sealed class SettingsBuilder
|
|||||||
return (success, convertedValue);
|
return (success, convertedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static object BuildCollectionValue(Type collectionType, Type elementType, IReadOnlyList<object?> items)
|
||||||
|
{
|
||||||
|
// convert source collection with 'items' of type 'object?' to collection with items of requested type:
|
||||||
|
Type listType = typeof(List<>).MakeGenericType(elementType);
|
||||||
|
IList typedList = (IList)Activator.CreateInstance(listType)!;
|
||||||
|
|
||||||
|
foreach (object? item in items)
|
||||||
|
{
|
||||||
|
typedList.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (collectionType.IsArray)
|
||||||
|
{
|
||||||
|
Array array = Array.CreateInstance(elementType, typedList.Count);
|
||||||
|
typedList.CopyTo(array, 0);
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
// it is either IEnumerable<T> or ICollection<T> or IList<T> or List<T>
|
||||||
|
if (collectionType.IsAssignableFrom(listType))
|
||||||
|
{
|
||||||
|
return typedList;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we do not know the type, but try to instantiate 'collectionType' and if it implements ICollection<>, then fill it
|
||||||
|
object? instance = Activator.CreateInstance(collectionType);
|
||||||
|
if (instance != null)
|
||||||
|
{
|
||||||
|
Type? iCollectionT = collectionType
|
||||||
|
.GetInterfaces()
|
||||||
|
.FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(ICollection<>));
|
||||||
|
|
||||||
|
if (iCollectionT != null)
|
||||||
|
{
|
||||||
|
MethodInfo? add = iCollectionT.GetMethod("Add");
|
||||||
|
if (add != null)
|
||||||
|
{
|
||||||
|
foreach (object? item in typedList)
|
||||||
|
{
|
||||||
|
add.Invoke(instance, new object?[] { item });
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// try it as array, we fail anyway
|
||||||
|
Array fallback = Array.CreateInstance(elementType, typedList.Count);
|
||||||
|
typedList.CopyTo(fallback, 0);
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
private bool TryGetCustomConverter(SettingAttribute settingAttribute, [NotNullWhen(true)] out IValueConverter? customConverter)
|
private bool TryGetCustomConverter(SettingAttribute settingAttribute, [NotNullWhen(true)] out IValueConverter? customConverter)
|
||||||
{
|
{
|
||||||
customConverter = null;
|
customConverter = null;
|
||||||
@@ -266,67 +331,14 @@ internal sealed class SettingsBuilder
|
|||||||
return (true, type.GetGenericArguments()[0]);
|
return (true, type.GetGenericArguments()[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type.GetInterfaces().Any(i =>
|
foreach (Type i in type.GetInterfaces())
|
||||||
i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
|
|
||||||
{
|
{
|
||||||
return (true, type.GetGenericArguments()[0]);
|
if (i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>))
|
||||||
|
{
|
||||||
|
return (true, i.GetGenericArguments()[0]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (type.IsGenericType && typeof(IEnumerable<>).IsAssignableFrom(type.GetGenericTypeDefinition()))
|
|
||||||
// {
|
|
||||||
// return (true, type.GetGenericArguments()[0]);
|
|
||||||
// }
|
|
||||||
// if (t.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICollection<>)))
|
|
||||||
// {
|
|
||||||
// return (true, t.GetInterfaces().First(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICollection<>)).GetGenericArguments()[0]);
|
|
||||||
// }
|
|
||||||
// if (t.IsGenericType && (t.GetGenericTypeDefinition() == typeof(List<>)))
|
|
||||||
// {
|
|
||||||
// return (true, t.GetGenericArguments()[0]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
return (false, null);
|
return (false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsEnumerableType(Type type)
|
|
||||||
{
|
|
||||||
if (type == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == typeof(string))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type.IsArray)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof(IEnumerable).IsAssignableFrom(type))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type.GetInterfaces().Any(i =>
|
|
||||||
i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Type UnwrapNullable(Type type)
|
|
||||||
{
|
|
||||||
Type? underlying = Nullable.GetUnderlyingType(type);
|
|
||||||
if (underlying != null)
|
|
||||||
{
|
|
||||||
return underlying;
|
|
||||||
}
|
|
||||||
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ internal static class Extenders
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool Equivalent<T>(this IEnumerable<T?> left, IEnumerable<T?> right, IEqualityComparer<T?>? comparer = null)
|
public static bool Equivalent<T>(this IEnumerable<T?> left, IEnumerable<T?> right, IEqualityComparer<T?>? comparer = null) where T : notnull
|
||||||
{
|
{
|
||||||
if (ReferenceEquals(left, right))
|
if (ReferenceEquals(left, right))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -99,7 +99,6 @@ internal static class StringBuilderCache
|
|||||||
if (Interlocked.Exchange(ref _disposed, 1) == 0)
|
if (Interlocked.Exchange(ref _disposed, 1) == 0)
|
||||||
{
|
{
|
||||||
StringBuilder? sb = _sb;
|
StringBuilder? sb = _sb;
|
||||||
_sb = null;
|
|
||||||
Release(sb);
|
Release(sb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -121,7 +120,7 @@ internal static class StringBuilderCache
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
public StringBuilder Builder => _sb;
|
public StringBuilder Builder => _disposed == 0 ? _sb : throw new ObjectDisposedException(nameof(ScopedBuilder));
|
||||||
|
|
||||||
public static implicit operator StringBuilder(ScopedBuilder b)
|
public static implicit operator StringBuilder(ScopedBuilder b)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ internal static class StringFormatter
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
e.GetType();
|
||||||
|
|
||||||
using StringBuilderCache.ScopedBuilder lease = StringBuilderCache.AcquireScoped();
|
using StringBuilderCache.ScopedBuilder lease = StringBuilderCache.AcquireScoped();
|
||||||
StringBuilder sb = lease.Builder;
|
StringBuilder sb = lease.Builder;
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ public sealed class CertificateSettings : JobSettings
|
|||||||
[Setting("is-certificate-authority", Default = false, AlternateNames = ["isca"])]
|
[Setting("is-certificate-authority", Default = false, AlternateNames = ["isca"])]
|
||||||
public bool IsCertificateAuthority { [DebuggerStepThrough] get; [DebuggerStepThrough] set; }
|
public bool IsCertificateAuthority { [DebuggerStepThrough] get; [DebuggerStepThrough] set; }
|
||||||
|
|
||||||
[Setting("issuer", Converter = typeof(StorageConverter))]
|
[Setting("issuer-certificate", Converter = typeof(StorageConverter))]
|
||||||
public IStorage? Issuer { [DebuggerStepThrough] get; [DebuggerStepThrough] set; }
|
public IStorage? Issuer { [DebuggerStepThrough] get; [DebuggerStepThrough] set; }
|
||||||
|
|
||||||
[Setting("issuer-password")]
|
[Setting("issuer-password")]
|
||||||
|
|||||||
@@ -6,8 +6,17 @@ internal static class Program
|
|||||||
{
|
{
|
||||||
private static async Task<int> Main(string[] args)
|
private static async Task<int> Main(string[] args)
|
||||||
{
|
{
|
||||||
args = ["--job=create-certificate", "--issuer=file|o|c:\\friend2.pfx", "--issuer-password=aaa", "--subject=hello", "--san=world", "--algorithm=ecdsa", "--ecdsa-curve=p384", "--storage=file|w|c:\\mycert.pfx", "--validity-period=2d"];
|
args = [
|
||||||
// args = ["--job=create-certificate", "--subject=hello", "--algorithm=ecdsa", "--curve=p384"];
|
"--job=create-certificate",
|
||||||
|
"--issuer-certificate=file|o|c:\\friend2.pfx",
|
||||||
|
"--issuer-password=aaa",
|
||||||
|
"--subject=hello",
|
||||||
|
"--san=world",
|
||||||
|
"--algorithm=ecdsa",
|
||||||
|
"--ecdsa-curve=p384",
|
||||||
|
"--storage=file|w|c:\\mycert.pfx",
|
||||||
|
"--validity-period=2d" ];
|
||||||
|
|
||||||
using CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromMinutes(1));
|
using CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromMinutes(1));
|
||||||
|
|
||||||
JobExecutor executor = new JobExecutor();
|
JobExecutor executor = new JobExecutor();
|
||||||
|
|||||||
Reference in New Issue
Block a user