new job get-certificate-info
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
namespace CertMgr.CertGen;
|
||||
namespace CertMgr.Certificates.CertGen;
|
||||
|
||||
public class CertGenException : Exception
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace CertMgr.CertGen;
|
||||
namespace CertMgr.Certificates.CertGen;
|
||||
|
||||
public enum CertificateAlgorithm
|
||||
{
|
||||
@@ -5,7 +5,7 @@ using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
using CertMgr.Core.Exceptions;
|
||||
|
||||
namespace CertMgr.CertGen;
|
||||
namespace CertMgr.Certificates.CertGen;
|
||||
|
||||
internal abstract class CertificateGeneratorBase<TAlgorithm, TSettings> : ICertificateGenerator
|
||||
where TAlgorithm : AsymmetricAlgorithm
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
using CertMgr.Core.Exceptions;
|
||||
|
||||
namespace CertMgr.CertGen;
|
||||
namespace CertMgr.Certificates.CertGen;
|
||||
|
||||
public sealed class CertificateManager
|
||||
{
|
||||
@@ -4,7 +4,7 @@ using System.Security.Cryptography.X509Certificates;
|
||||
using CertMgr.Core.Utils;
|
||||
using CertMgr.Core.Validation;
|
||||
|
||||
namespace CertMgr.CertGen;
|
||||
namespace CertMgr.Certificates.CertGen;
|
||||
|
||||
public sealed class CertificateSettings
|
||||
{
|
||||
@@ -3,7 +3,7 @@ using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
using CertMgr.Core.Exceptions;
|
||||
|
||||
namespace CertMgr.CertGen;
|
||||
namespace CertMgr.Certificates.CertGen;
|
||||
|
||||
internal sealed class EcdsaCertificateGenerator : CertificateGeneratorBase<ECDsa, EcdsaGeneratorSettings>
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace CertMgr.CertGen;
|
||||
namespace CertMgr.Certificates.CertGen;
|
||||
|
||||
public enum EcdsaCurve
|
||||
{
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
using CertMgr.Core.Exceptions;
|
||||
|
||||
namespace CertMgr.CertGen;
|
||||
namespace CertMgr.Certificates.CertGen;
|
||||
|
||||
public sealed class EcdsaGeneratorSettings : GeneratorSettings
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace CertMgr.CertGen;
|
||||
namespace CertMgr.Certificates.CertGen;
|
||||
|
||||
public abstract class GeneratorSettings
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace CertMgr.CertGen;
|
||||
namespace CertMgr.Certificates.CertGen;
|
||||
|
||||
public enum GeneratorType
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace CertMgr.CertGen;
|
||||
namespace CertMgr.Certificates.CertGen;
|
||||
|
||||
public enum HashAlgorithm
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace CertMgr.CertGen;
|
||||
namespace CertMgr.Certificates.CertGen;
|
||||
|
||||
public interface ICertificateGenerator : IAsyncDisposable
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace CertMgr.CertGen;
|
||||
namespace CertMgr.Certificates.CertGen;
|
||||
|
||||
[Flags]
|
||||
public enum KeyUsage
|
||||
@@ -3,7 +3,7 @@ using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
using CertMgr.Core.Exceptions;
|
||||
|
||||
namespace CertMgr.CertGen;
|
||||
namespace CertMgr.Certificates.CertGen;
|
||||
|
||||
internal sealed class RsaCertificateGenerator : CertificateGeneratorBase<RSA, RsaGeneratorSettings>
|
||||
{
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
using CertMgr.Core.Exceptions;
|
||||
|
||||
namespace CertMgr.CertGen;
|
||||
namespace CertMgr.Certificates.CertGen;
|
||||
|
||||
public sealed class RsaGeneratorSettings : GeneratorSettings
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace CertMgr.CertGen;
|
||||
namespace CertMgr.Certificates.CertGen;
|
||||
|
||||
public enum RsaKeySize
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace CertMgr.CertGen;
|
||||
namespace CertMgr.Certificates.CertGen;
|
||||
|
||||
public enum SANKind
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace CertMgr.CertGen;
|
||||
namespace CertMgr.Certificates.CertGen;
|
||||
|
||||
public sealed class SubjectAlternateName : IEquatable<SubjectAlternateName>
|
||||
{
|
||||
@@ -3,7 +3,7 @@
|
||||
using CertMgr.Core.Log;
|
||||
using CertMgr.Core.Utils;
|
||||
|
||||
namespace CertMgr.CertGen;
|
||||
namespace CertMgr.Certificates.CertGen;
|
||||
|
||||
public sealed class SubjectAlternateNames : IReadOnlyCollection<SubjectAlternateName>
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using CertMgr.Core.Utils;
|
||||
|
||||
namespace CertMgr.CertGen.Utils;
|
||||
namespace CertMgr.Certificates.CertGen.Utils;
|
||||
|
||||
public sealed class CollectionEquivalencyComparer<T> : IEqualityComparer<IEnumerable<T>> where T : notnull
|
||||
{
|
||||
@@ -4,7 +4,7 @@ using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
using CertMgr.Core.Validation;
|
||||
|
||||
namespace CertMgr.CertGen.Utils;
|
||||
namespace CertMgr.Certificates.CertGen.Utils;
|
||||
|
||||
public sealed class SubjectValidator : IValueValidator<string>
|
||||
{
|
||||
42
certmgr/Certificates/CertStoreSearcher.cs
Normal file
42
certmgr/Certificates/CertStoreSearcher.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System.Diagnostics;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
using CertMgr.Core.Filters;
|
||||
using CertMgr.Jobs;
|
||||
|
||||
namespace CertMgr.Certificates;
|
||||
|
||||
public sealed class CertStoreSearcher
|
||||
{
|
||||
public CertStoreSearcher(CertStore store)
|
||||
{
|
||||
Store = store;
|
||||
}
|
||||
|
||||
private CertStore Store { [DebuggerStepThrough] get; }
|
||||
|
||||
public async Task<IReadOnlyList<X509Certificate2>> SearchAsync(IFilter<X509Certificate2> filter, CancellationToken cancellationToken)
|
||||
{
|
||||
List<X509Certificate2> certificates = new List<X509Certificate2>();
|
||||
|
||||
using (X509Store store = new X509Store((StoreName)Store.Name, (StoreLocation)Store.Location))
|
||||
{
|
||||
store.Open(OpenFlags.ReadOnly);
|
||||
|
||||
foreach (X509Certificate2 cert in store.Certificates)
|
||||
{
|
||||
if (await filter.IsMatchAsync(cert, cancellationToken))
|
||||
{
|
||||
certificates.Add(cert);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return certificates;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("store = {0}/{1}", Store.Location, Store.Name);
|
||||
}
|
||||
}
|
||||
21
certmgr/Certificates/Filters/ByThumbprintFilter.cs
Normal file
21
certmgr/Certificates/Filters/ByThumbprintFilter.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System.Diagnostics;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace CertMgr.Certificates.Filters;
|
||||
|
||||
internal sealed class ByThumbprintFilter : CertificateFilter
|
||||
{
|
||||
internal ByThumbprintFilter(string thumbprint)
|
||||
: base(CertificateFilterType.Thumbprint)
|
||||
{
|
||||
Thumbprint = thumbprint;
|
||||
}
|
||||
|
||||
private string Thumbprint { [DebuggerStepThrough] get; }
|
||||
|
||||
protected override Task<bool> DoIsMatchAsync(X509Certificate2 value, CancellationToken cancellationToken)
|
||||
{
|
||||
bool result = string.Equals(value.Thumbprint, Thumbprint, StringComparison.OrdinalIgnoreCase);
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
}
|
||||
21
certmgr/Certificates/Filters/CertificateFilter.cs
Normal file
21
certmgr/Certificates/Filters/CertificateFilter.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System.Diagnostics;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
using CertMgr.Core.Filters;
|
||||
|
||||
namespace CertMgr.Certificates.Filters;
|
||||
|
||||
public abstract class CertificateFilter : Filter<X509Certificate2>
|
||||
{
|
||||
internal CertificateFilter(CertificateFilterType type)
|
||||
{
|
||||
Type = type;
|
||||
}
|
||||
|
||||
public CertificateFilterType Type { [DebuggerStepThrough] get; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("type = {0}", Type);
|
||||
}
|
||||
}
|
||||
6
certmgr/Certificates/Filters/CertificateFilterType.cs
Normal file
6
certmgr/Certificates/Filters/CertificateFilterType.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace CertMgr.Certificates.Filters;
|
||||
|
||||
public enum CertificateFilterType
|
||||
{
|
||||
Thumbprint = 1
|
||||
}
|
||||
16
certmgr/Certificates/Filters/KnownFilters.cs
Normal file
16
certmgr/Certificates/Filters/KnownFilters.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
using CertMgr.Core.Filters;
|
||||
|
||||
namespace CertMgr.Certificates.Filters;
|
||||
|
||||
public static class KnownFilters
|
||||
{
|
||||
public static class Certificate
|
||||
{
|
||||
public static IFilter<X509Certificate2> ByThumbprint(string thumbprint)
|
||||
{
|
||||
return new ByThumbprintFilter(thumbprint);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using CertMgr.Core.Utils;
|
||||
/*using CertMgr.Core.Utils;
|
||||
|
||||
namespace CertMgr.Core;
|
||||
|
||||
@@ -13,3 +13,4 @@ public class CertMgrException : Exception
|
||||
{
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace CertMgr.Core.Converters;
|
||||
|
||||
public class ConverterContext
|
||||
{
|
||||
public static readonly ConverterContext Empty = new ConverterContext();
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace CertMgr.Core.Converters;
|
||||
|
||||
public class EnumConverterContext : ConverterContext
|
||||
{
|
||||
internal EnumConverterContext(Type targetType)
|
||||
{
|
||||
TargetType = targetType;
|
||||
}
|
||||
|
||||
public Type TargetType { [DebuggerStepThrough] get; }
|
||||
}
|
||||
@@ -4,38 +4,20 @@ namespace CertMgr.Core.Converters.Impl;
|
||||
|
||||
internal class StorageKindConverter : ValueConverter<IStorage>
|
||||
{
|
||||
// private const char Separator = '|';
|
||||
|
||||
protected override Task<IStorage?> DoConvertAsync(string rawValue, Type targetType, CancellationToken cancellationToken)
|
||||
{
|
||||
ReadOnlySpan<char> storageTypeSpan = rawValue.AsSpan();
|
||||
|
||||
// int storageTypeSplitIndex = rawSpan.IndexOf(Separator);
|
||||
// if (storageTypeSplitIndex == -1)
|
||||
// {
|
||||
// return Task.FromResult((IStorage?)EmptyStorage.Empty);
|
||||
// }
|
||||
|
||||
IStorage? storage;
|
||||
|
||||
// ReadOnlySpan<char> storageTypeSpan = rawSpan.Slice(0, storageTypeSplitIndex);
|
||||
// ReadOnlySpan<char> storageDefinition = rawSpan.Slice(storageTypeSplitIndex + 1);
|
||||
switch (storageTypeSpan)
|
||||
{
|
||||
case "file":
|
||||
storage = new FileStorage();
|
||||
// if (!TryGetFileStore(storageDefinition, out storage))
|
||||
// {
|
||||
// storage = EmptyStorage.Empty;
|
||||
// }
|
||||
break;
|
||||
case "certstore":
|
||||
case "cert-store":
|
||||
storage = new CertStoreStorage();
|
||||
//if (!TryGetCertStore(storageDefinition, out storage))
|
||||
//{
|
||||
// storage = EmptyStorage.Empty;
|
||||
//}
|
||||
break;
|
||||
default:
|
||||
storage = EmptyStorage.Empty;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
using CertMgr.Core.Utils;
|
||||
|
||||
namespace CertMgr.Core.Storage;
|
||||
namespace CertMgr.Core.Filters;
|
||||
|
||||
public abstract class Filter<T> : IFilter<T>
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace CertMgr.Core.Storage;
|
||||
namespace CertMgr.Core.Filters;
|
||||
|
||||
public interface IFilter<T>
|
||||
{
|
||||
@@ -45,6 +45,10 @@ internal sealed class JobExecutor
|
||||
errorLevel = result.ErrorLevel;
|
||||
|
||||
CLog.Info("Executing job '{0}'... done (finished with error-level {1}, took {2})", job.Name, errorLevel, sw.Elapsed);
|
||||
if (!string.IsNullOrEmpty(result.Message))
|
||||
{
|
||||
CLog.Info(result.Message);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Diagnostics;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
using CertMgr.Core.Filters;
|
||||
using CertMgr.Core.Utils;
|
||||
|
||||
namespace CertMgr.Core.Storage;
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace CertMgr.Core.Storage;
|
||||
|
||||
public static class CertificateFilter
|
||||
{
|
||||
private abstract class CertFilter : Filter<X509Certificate2>
|
||||
{
|
||||
}
|
||||
|
||||
private sealed class ByThumbprintFilter : CertFilter
|
||||
{
|
||||
public ByThumbprintFilter(string thumbprint)
|
||||
{
|
||||
Thumbprint = thumbprint;
|
||||
}
|
||||
|
||||
public string Thumbprint { [DebuggerStepThrough] get; }
|
||||
|
||||
protected override Task<bool> DoIsMatchAsync(X509Certificate2 value, CancellationToken cancellationToken)
|
||||
{
|
||||
bool result = string.Equals(value.Thumbprint, Thumbprint, StringComparison.OrdinalIgnoreCase);
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
namespace CertMgr.Core.Storage;
|
||||
/*namespace CertMgr.Core.Storage;
|
||||
|
||||
public enum StorageType
|
||||
{
|
||||
File = 1
|
||||
}
|
||||
*/
|
||||
@@ -25,7 +25,7 @@ internal static class Extenders
|
||||
{ typeof(object), "object" },
|
||||
};
|
||||
|
||||
public static string ToSeparatedList<T>(this IEnumerable<T> items, Func<T, string> formatter, string itemSeparator, string? lastItemSeparator = null)
|
||||
public static string ToSeparatedList<T>(this IEnumerable<T> items, Func<T, string?>? formatter = null, string? itemSeparator = ",", string? lastItemSeparator = null)
|
||||
{
|
||||
using StringBuilderCache.ScopedBuilder lease = StringBuilderCache.AcquireScoped();
|
||||
StringBuilder sb = lease.Builder;
|
||||
@@ -35,7 +35,7 @@ internal static class Extenders
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static void ToSeparatedList<T>(this IEnumerable<T> items, StringBuilder sb, Func<T, string> formatter, string itemSeparator, string? lastItemSeparator = null)
|
||||
public static void ToSeparatedList<T>(this IEnumerable<T> items, StringBuilder sb, Func<T, string?>? formatter, string? itemSeparator = null, string? lastItemSeparator = null)
|
||||
{
|
||||
if (!items.Any())
|
||||
{
|
||||
@@ -245,4 +245,9 @@ internal static class Extenders
|
||||
return isOneOf;
|
||||
}
|
||||
|
||||
public static void AppendFormatLine(this StringBuilder sb, string format, params object[] args)
|
||||
{
|
||||
sb.AppendFormat(format, args);
|
||||
sb.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
23
certmgr/Jobs/CertStore.cs
Normal file
23
certmgr/Jobs/CertStore.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
using CertMgr.Core.Storage;
|
||||
|
||||
namespace CertMgr.Jobs;
|
||||
|
||||
public sealed class CertStore
|
||||
{
|
||||
public CertStore(CertStoreLocation location, CertStoreName name)
|
||||
{
|
||||
Location = location;
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public CertStoreLocation Location { [DebuggerStepThrough] get; }
|
||||
|
||||
public CertStoreName Name { [DebuggerStepThrough] get; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0}/{1}", Location, Name);
|
||||
}
|
||||
}
|
||||
48
certmgr/Jobs/CertStoreConverter.cs
Normal file
48
certmgr/Jobs/CertStoreConverter.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using CertMgr.Core.Converters;
|
||||
using CertMgr.Core.Log;
|
||||
using CertMgr.Core.Storage;
|
||||
using CertMgr.Core.Utils;
|
||||
|
||||
namespace CertMgr.Jobs;
|
||||
|
||||
internal sealed class CertStoreConverter : ValueConverter<CertStore>
|
||||
{
|
||||
protected override Task<CertStore?> DoConvertAsync(string rawValue, Type targetType, CancellationToken cancellationToken)
|
||||
{
|
||||
CertStore? store = null;
|
||||
|
||||
if (string.IsNullOrEmpty(rawValue))
|
||||
{
|
||||
return Task.FromResult(store);
|
||||
}
|
||||
|
||||
ReadOnlySpan<char> span = rawValue.AsSpan();
|
||||
int separatorIndex = span.IndexOf('/');
|
||||
if (separatorIndex > -1 && separatorIndex < span.Length - 1)
|
||||
{
|
||||
ReadOnlySpan<char> locationSpan = span.Slice(0, separatorIndex);
|
||||
if (Enum.TryParse(locationSpan, true, out CertStoreLocation location) && Enum.IsDefined(location))
|
||||
{
|
||||
ReadOnlySpan<char> nameSpan = span.Slice(separatorIndex + 1, span.Length - separatorIndex - 1);
|
||||
if (Enum.TryParse(nameSpan, true, out CertStoreName name) && Enum.IsDefined(name))
|
||||
{
|
||||
store = new CertStore(location, name);
|
||||
}
|
||||
else
|
||||
{
|
||||
CLog.Error("Failed to parse cert-store name from value '{0}'. Value '{1}' is not valid name (available values: {2})", rawValue, nameSpan.ToString(), Enum.GetNames<CertStoreName>().ToSeparatedList());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CLog.Error("Failed to parse cert-store location from value '{0}'. Value '{1}' is not valid location (available values: {2})", rawValue, locationSpan.ToString(), Enum.GetNames<CertStoreLocation>().ToSeparatedList());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CLog.Error("Cannot parse cert-store from value '{0}'. Value must be '<location>/<name>'", rawValue);
|
||||
}
|
||||
|
||||
return Task.FromResult(store);
|
||||
}
|
||||
}
|
||||
52
certmgr/Jobs/CertificateFilterConverter.cs
Normal file
52
certmgr/Jobs/CertificateFilterConverter.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
using CertMgr.Certificates.Filters;
|
||||
using CertMgr.Core.Converters;
|
||||
using CertMgr.Core.Filters;
|
||||
using CertMgr.Core.Log;
|
||||
using CertMgr.Core.Utils;
|
||||
|
||||
namespace CertMgr.Jobs;
|
||||
|
||||
internal sealed class CertificateFilterConverter : ValueConverter<IFilter<X509Certificate2>>
|
||||
{
|
||||
protected override Task<IFilter<X509Certificate2>?> DoConvertAsync(string rawValue, Type targetType, CancellationToken cancellationToken)
|
||||
{
|
||||
IFilter<X509Certificate2>? filter = null;
|
||||
|
||||
if (string.IsNullOrEmpty(rawValue))
|
||||
{
|
||||
return Task.FromResult(filter);
|
||||
}
|
||||
|
||||
ReadOnlySpan<char> span = rawValue.AsSpan();
|
||||
int separatorIndex = span.IndexOf(':');
|
||||
if (separatorIndex > -1 && separatorIndex < span.Length - 1)
|
||||
{
|
||||
ReadOnlySpan<char> filterTypeSpan = span.Slice(0, separatorIndex);
|
||||
if (Enum.TryParse(filterTypeSpan, true, out CertificateFilterType filterType) && Enum.IsDefined(filterType))
|
||||
{
|
||||
ReadOnlySpan<char> nameSpan = span.Slice(separatorIndex + 1, span.Length - separatorIndex - 1);
|
||||
string value = nameSpan.ToString();
|
||||
switch (filterType)
|
||||
{
|
||||
case CertificateFilterType.Thumbprint:
|
||||
filter = KnownFilters.Certificate.ByThumbprint(value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CLog.Error("Failed to parse filter type from value '{0}'. (available values: {1})", rawValue, Enum.GetNames<CertificateFilterType>().ToSeparatedList());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CLog.Error("Cannot parse filter from value '{0}'. Value must be '<filter-type>:<filtering-value>'", rawValue);
|
||||
}
|
||||
|
||||
return Task.FromResult(filter);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
using CertMgr.CertGen;
|
||||
using CertMgr.Certificates.CertGen;
|
||||
using CertMgr.Core.Exceptions;
|
||||
using CertMgr.Core.Jobs;
|
||||
using CertMgr.Core.Log;
|
||||
@@ -9,17 +9,17 @@ using CertMgr.Core.Utils;
|
||||
|
||||
namespace CertMgr.Jobs;
|
||||
|
||||
public sealed class CreateCertificateJob : Job<CertificateSettings>
|
||||
public sealed class CreateCertificateJob : Job<CreateCertificateSettings>
|
||||
{
|
||||
public const string ID = "create-certificate";
|
||||
|
||||
protected override async Task<JobResult> DoExecuteAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
CertificateSettings cs = Settings;
|
||||
CreateCertificateSettings cs = Settings;
|
||||
CLog.Info("creating certificate using settings: subject = '{0}', algorithm = '{1}', curve = '{2}'", cs.Subject, cs.Algorithm?.ToString() ?? "<null>", cs.Curve);
|
||||
|
||||
GeneratorSettings gs = CreateGeneratorSettings();
|
||||
CertGen.CertificateSettings cgcs = await CreateCertificateSettingsAsync(cancellationToken).ConfigureAwait(false);
|
||||
CertificateSettings cgcs = await CreateCertificateSettingsAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
CertificateManager cm = new CertificateManager();
|
||||
using (X509Certificate2 cert = await cm.CreateAsync(cgcs, gs, cancellationToken).ConfigureAwait(false))
|
||||
@@ -65,11 +65,11 @@ public sealed class CreateCertificateJob : Job<CertificateSettings>
|
||||
return gs;
|
||||
}
|
||||
|
||||
private async Task<CertGen.CertificateSettings> CreateCertificateSettingsAsync(CancellationToken cancellationToken)
|
||||
private async Task<CertificateSettings> CreateCertificateSettingsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
CertGen.CertificateSettings cgcs = new CertGen.CertificateSettings();
|
||||
CertificateSettings cgcs = new CertificateSettings();
|
||||
|
||||
cgcs.SubjectName = Settings.Subject;
|
||||
cgcs.ValidityPeriod = Settings.ValidityPeriod.HasValue ? Settings.ValidityPeriod.Value : TimeSpan.FromDays(365);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
using CertMgr.CertGen;
|
||||
using CertMgr.CertGen.Utils;
|
||||
using CertMgr.Certificates.CertGen;
|
||||
using CertMgr.Certificates.CertGen.Utils;
|
||||
using CertMgr.Core.Attributes;
|
||||
using CertMgr.Core.Converters.Impl;
|
||||
using CertMgr.Core.Jobs;
|
||||
@@ -11,13 +11,13 @@ using CertMgr.Core.Validation;
|
||||
|
||||
namespace CertMgr.Jobs;
|
||||
|
||||
public sealed class CertificateSettings : JobSettings
|
||||
public sealed class CreateCertificateSettings : JobSettings
|
||||
{
|
||||
public CertificateSettings()
|
||||
public CreateCertificateSettings()
|
||||
{
|
||||
Algorithm = CertificateAlgorithm.ECDsa;
|
||||
Curve = EcdsaCurve.P384;
|
||||
HashAlgorithm = CertGen.HashAlgorithm.Sha384;
|
||||
HashAlgorithm = Certificates.CertGen.HashAlgorithm.Sha384;
|
||||
ValidityPeriod = TimeSpan.FromDays(365);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ public sealed class CertificateSettings : JobSettings
|
||||
[Setting("friendly-name")]
|
||||
public string? FriendlyName { [DebuggerStepThrough] get; [DebuggerStepThrough] set; }
|
||||
|
||||
[Setting("key-usage", Default = CertGen.KeyUsage.None, Converter = typeof(EnumConverter))]
|
||||
[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))]
|
||||
107
certmgr/Jobs/GetCertificateInfoJob.cs
Normal file
107
certmgr/Jobs/GetCertificateInfoJob.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
|
||||
using CertMgr.Certificates;
|
||||
using CertMgr.Core.Jobs;
|
||||
using CertMgr.Core.Log;
|
||||
using CertMgr.Core.Utils;
|
||||
|
||||
namespace CertMgr.Jobs;
|
||||
|
||||
public sealed class GetCertificateInfoJob : Job<GetCertificateInfoSettings>
|
||||
{
|
||||
public const string ID = "get-certificate-info";
|
||||
|
||||
protected override async Task<JobResult> DoExecuteAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
X509Certificate2? cert = null;
|
||||
|
||||
if (!string.IsNullOrEmpty(Settings.File))
|
||||
{
|
||||
if (File.Exists(Settings.File))
|
||||
{
|
||||
try
|
||||
{
|
||||
cert = X509CertificateLoader.LoadPkcs12FromFile(Settings.File, Settings.Password);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception(string.Format("Failed to load certificate from file '{0}'", Settings.File), e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CLog.Error("File doesn't exist: '{0}'", Settings.File);
|
||||
}
|
||||
}
|
||||
else if (Settings.Store != null)
|
||||
{
|
||||
if (Settings.Filter != null)
|
||||
{
|
||||
CertStoreSearcher searcher = new CertStoreSearcher(Settings.Store);
|
||||
IReadOnlyList<X509Certificate2> certs = await searcher.SearchAsync(Settings.Filter, cancellationToken).ConfigureAwait(false);
|
||||
cert = certs.FirstOrDefault();
|
||||
}
|
||||
else
|
||||
{
|
||||
CLog.Error("Filter must be specified when info on certificate from cert-store is requested");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CLog.Error("Either file or cert-store must be specified when requesting info on certificate");
|
||||
}
|
||||
|
||||
if (cert != null)
|
||||
{
|
||||
using (StringBuilderCache.ScopedBuilder lease = StringBuilderCache.AcquireScoped(256))
|
||||
{
|
||||
int padSize = 24;
|
||||
StringBuilder sb = lease.Builder;
|
||||
sb.AppendLine();
|
||||
AppendWithPadding(sb, "Subject", cert.Subject.StartsWith("CN=") ? cert.Subject.Substring(3) : cert.Subject, padSize);
|
||||
AppendWithPadding(sb, "Issuer", cert.Issuer.StartsWith("CN=") ? cert.Issuer.Substring(3) : cert.Issuer, padSize);
|
||||
AppendWithPadding(sb, "Not Before", cert.NotBefore.ToString("s"), padSize);
|
||||
AppendWithPadding(sb, "Not After", cert.NotAfter.ToString("s"), padSize);
|
||||
AppendWithPadding(sb, "Signature Algorithm", cert.SignatureAlgorithm.FriendlyName, padSize);
|
||||
AppendWithPadding(sb, "Thumbprint", cert.Thumbprint, padSize);
|
||||
if (!string.IsNullOrEmpty(cert.FriendlyName))
|
||||
{
|
||||
AppendWithPadding(sb, "FriendlyName", cert.FriendlyName, padSize);
|
||||
}
|
||||
foreach (X509Extension ext in cert.Extensions)
|
||||
{
|
||||
string[] values = ext.Format(true).TrimEnd(Environment.NewLine.ToCharArray()).Split(Environment.NewLine);
|
||||
if ((values.Length > 1 && !string.IsNullOrEmpty(values[values.Length - 1])) || values.Length > 2)
|
||||
{
|
||||
AppendWithPadding(sb, ext.Oid?.FriendlyName ?? ext.Oid?.Value, string.Empty, padSize);
|
||||
foreach (string value in values)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
sb.AppendFormatLine(" - {0}", value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AppendWithPadding(sb, ext.Oid?.FriendlyName ?? ext.Oid?.Value, values[0], padSize);
|
||||
}
|
||||
}
|
||||
|
||||
return CreateSuccess(sb.ToString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return CreateFailure("No certificate");
|
||||
}
|
||||
}
|
||||
|
||||
private static void AppendWithPadding(StringBuilder sb, string name, string value, int padSize)
|
||||
{
|
||||
string padStr = padSize >= name.Length ? new string(' ', padSize - name.Length) : string.Empty;
|
||||
sb.AppendFormatLine("{0}{1}: {2}", name, padStr, value);
|
||||
}
|
||||
}
|
||||
23
certmgr/Jobs/GetCertificateInfoSettings.cs
Normal file
23
certmgr/Jobs/GetCertificateInfoSettings.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System.Diagnostics;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
using CertMgr.Core.Attributes;
|
||||
using CertMgr.Core.Filters;
|
||||
using CertMgr.Core.Jobs;
|
||||
|
||||
namespace CertMgr.Jobs;
|
||||
|
||||
public sealed class GetCertificateInfoSettings : JobSettings
|
||||
{
|
||||
[Setting("file")]
|
||||
public string? File { [DebuggerStepThrough] get; [DebuggerStepThrough] set; }
|
||||
|
||||
[Setting("cert-store", Converter = typeof(CertStoreConverter))]
|
||||
public CertStore? Store { [DebuggerStepThrough] get; [DebuggerStepThrough] set; }
|
||||
|
||||
[Setting("filter", Converter = typeof(CertificateFilterConverter))]
|
||||
public IFilter<X509Certificate2>? Filter { [DebuggerStepThrough] get; [DebuggerStepThrough] set; }
|
||||
|
||||
[Setting("password", IsSecret = true)]
|
||||
public string? Password { [DebuggerStepThrough] get; [DebuggerStepThrough] set; }
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using CertMgr.CertGen;
|
||||
using CertMgr.Certificates.CertGen;
|
||||
using CertMgr.Core.Converters;
|
||||
|
||||
namespace CertMgr.Jobs;
|
||||
|
||||
@@ -6,30 +6,41 @@ internal static class Program
|
||||
{
|
||||
private static async Task<int> Main(string[] args)
|
||||
{
|
||||
// args = [
|
||||
// "--job=get-certificate-info",
|
||||
// "--file=c:\\mycert-ecdsa.pfx",
|
||||
// "--password="
|
||||
// ];
|
||||
args = [
|
||||
"--job=create-certificate",
|
||||
"--issuer-kind=file",
|
||||
"--issuer-file=o|c:\\friend2.pfx",
|
||||
"--issuer-password=aaa",
|
||||
"--subject=CN=hello",
|
||||
"--san=world",
|
||||
"--san=DNS:zdrastvujte",
|
||||
"--san=IP:192.168.131.1",
|
||||
"--algorithm=ecdsa",
|
||||
"--ecdsa-curve=p384",
|
||||
"--validity-period=2d",
|
||||
"--job=get-certificate-info",
|
||||
"--cert-store=user/my",
|
||||
"--filter=thumbprint:b395149b6079553eea92d9a73ffc97463e8976ff"
|
||||
];
|
||||
|
||||
// "--certificate-target=file|w|c:\\mycert-ecdsa.pfx",
|
||||
// "--certificate-password=aaa",
|
||||
|
||||
// "--target-kind=file",
|
||||
// "--target-file=w|c:\\mycert-ecdsa.pfx",
|
||||
// "--target-password=aaa",
|
||||
|
||||
"--target-kind=certstore",
|
||||
"--target-certstore=machine|my|exportable",
|
||||
"--target-password=aaa"
|
||||
];
|
||||
// args = [
|
||||
// "--job=create-certificate",
|
||||
// "--issuer-kind=file",
|
||||
// "--issuer-file=o|c:\\friend2.pfx",
|
||||
// "--issuer-password=aaa",
|
||||
// "--subject=CN=hello",
|
||||
// "--san=world",
|
||||
// "--san=DNS:zdrastvujte",
|
||||
// "--san=IP:192.168.131.1",
|
||||
// "--algorithm=ecdsa",
|
||||
// "--ecdsa-curve=p384",
|
||||
// "--validity-period=2d",
|
||||
//
|
||||
// // "--certificate-target=file|w|c:\\mycert-ecdsa.pfx",
|
||||
// // "--certificate-password=aaa",
|
||||
//
|
||||
// // "--target-kind=file",
|
||||
// // "--target-file=w|c:\\mycert-ecdsa.pfx",
|
||||
// // "--target-password=aaa",
|
||||
//
|
||||
// "--target-kind=certstore",
|
||||
// "--target-certstore=machine|my|exportable",
|
||||
// "--target-password=aaa"
|
||||
// ];
|
||||
|
||||
// --certificate-target=certstore|machine|my
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using CertMgr.CertGen.Utils;
|
||||
using CertMgr.Certificates.CertGen.Utils;
|
||||
using CertMgr.Core.Validation;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
Reference in New Issue
Block a user