diff --git a/src/main.lib/Plugins/StorePlugins/CertificateStore/CertificateStore.cs b/src/main.lib/Plugins/StorePlugins/CertificateStore/CertificateStore.cs
index 2c1b6d98..f7efcbfd 100644
--- a/src/main.lib/Plugins/StorePlugins/CertificateStore/CertificateStore.cs
+++ b/src/main.lib/Plugins/StorePlugins/CertificateStore/CertificateStore.cs
@@ -27,7 +27,6 @@ internal class CertificateStore : IStorePlugin, IDisposable
internal const string Name = "CertificateStore";
private const string DefaultStoreName = nameof(StoreName.My);
private readonly ILogService _log;
- private readonly ISettingsService _settings;
private readonly string _storeName;
private readonly IIISClient _iisClient;
private readonly CertificateStoreOptions _options;
@@ -43,7 +42,6 @@ public CertificateStore(
_log = log;
_iisClient = iisClient;
_options = options;
- _settings = settings;
_keyFinder = keyFinder;
_storeName = options.StoreName ?? DefaultStore(settings, iisClient);
if (string.Equals(_storeName, "Personal", StringComparison.InvariantCultureIgnoreCase) ||
@@ -53,7 +51,7 @@ public CertificateStore(
// config files, because that's what the store is called in mmc
_storeName = nameof(StoreName.My);
}
- _storeClient = new CertificateStoreClient(_storeName, StoreLocation.LocalMachine, _log);
+ _storeClient = new CertificateStoreClient(_storeName, StoreLocation.LocalMachine, _log, settings);
_runLevel = runLevel;
}
@@ -95,37 +93,6 @@ public static string DefaultStore(ISettingsService settings, IIISClient client)
}
else
{
- var exportable =
- _settings.Store.CertificateStore.PrivateKeyExportable == true ||
- #pragma warning disable CS0618 // Type or member is obsolete
- (_settings.Store.CertificateStore.PrivateKeyExportable == null && _settings.Security.PrivateKeyExportable == true);
- #pragma warning restore CS0618 // Type or member is obsolete
-
- var baseFlags = X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet;
- var finalFlags = baseFlags;
- if (exportable)
- {
- finalFlags |= X509KeyStorageFlags.Exportable;
- }
- _log.Debug("Storing certificate with flags {flags}", finalFlags);
-
- if (_settings.Store.CertificateStore.UseNextGenerationCryptoApi != true)
- {
- // Should always be exportable before we attempt to convert,
- // because otherwise we won't be able to get to the private key
- store = _storeClient.ApplyFlags(store, baseFlags | X509KeyStorageFlags.Exportable);
-
- // If the ConvertCertificate fails we fall back to the original
- // input certificate wit the base flags applied
- store = _storeClient.ConvertCertificate(store, finalFlags);
- store ??= _storeClient.ApplyFlags(input.Certificate, baseFlags);
- }
- else
- {
- // Do not attempt conversion, just apply the final flags
- store = _storeClient.ApplyFlags(store, finalFlags);
- }
-
_log.Information("Installing certificate in the certificate store");
_storeClient.InstallCertificate(store);
if (!_runLevel.HasFlag(RunLevel.Test))
diff --git a/src/main.lib/Plugins/StorePlugins/CertificateStore/CertificateStoreClient.cs b/src/main.lib/Plugins/StorePlugins/CertificateStore/CertificateStoreClient.cs
index e31cd710..a6a0dccc 100644
--- a/src/main.lib/Plugins/StorePlugins/CertificateStore/CertificateStoreClient.cs
+++ b/src/main.lib/Plugins/StorePlugins/CertificateStore/CertificateStoreClient.cs
@@ -12,15 +12,17 @@ public class CertificateStoreClient : IDisposable
private readonly X509Store _store;
private X509Store? _imStore;
private readonly ILogService _log;
+ private readonly ISettingsService _settings;
private readonly StoreLocation _location;
private bool disposedValue;
- public CertificateStoreClient(string storeName, StoreLocation storeLocation, ILogService log)
+ public CertificateStoreClient(string storeName, StoreLocation storeLocation, ILogService log, ISettingsService settings)
{
_log = log;
_location = storeLocation;
_log.Debug("Certificate store name: {_storeName}", storeName);
_store = new X509Store(storeName, storeLocation);
+ _settings = settings;
}
public X509Certificate2? FindByThumbprint(string thumbprint) => GetCertificate(x => string.Equals(x.Thumbprint, thumbprint));
@@ -42,6 +44,16 @@ public void InstallCertificate(X509Certificate2 certificate)
{
_log.Information(LogType.All, "Adding certificate {FriendlyName} to store {name}", certificate.FriendlyName, _store.Name);
_log.Verbose("{sub} - {iss} ({thumb})", certificate.Subject, certificate.Issuer, certificate.Thumbprint);
+ var flags = X509KeyStorageFlags.PersistKeySet;
+ if (_location == StoreLocation.CurrentUser)
+ {
+ flags |= X509KeyStorageFlags.UserKeySet;
+ }
+ else
+ {
+ flags |= X509KeyStorageFlags.MachineKeySet;
+ }
+ certificate = ProcessCertificate(certificate, flags);
_store.Add(certificate);
}
catch
@@ -137,7 +149,7 @@ public void UninstallCertificate(X509Certificate2 certificate)
///
///
///
- public X509Certificate2 ApplyFlags(X509Certificate2 original, X509KeyStorageFlags flags)
+ private static X509Certificate2 ApplyFlags(X509Certificate2 original, X509KeyStorageFlags flags)
{
// If no RSA key is present, we only export and re-fallback to
// set the correct flags on the certificate.
@@ -148,6 +160,47 @@ public X509Certificate2 ApplyFlags(X509Certificate2 original, X509KeyStorageFlag
};
}
+ ///
+ /// Apply certificate flags and convert private key provider if asked
+ ///
+ ///
+ ///
+ ///
+ private X509Certificate2 ProcessCertificate(X509Certificate2 input, X509KeyStorageFlags baseFlags)
+ {
+ var exportable =
+ _settings.Store.CertificateStore.PrivateKeyExportable == true ||
+ #pragma warning disable CS0618 // Type or member is obsolete
+ (_settings.Store.CertificateStore.PrivateKeyExportable == null && _settings.Security.PrivateKeyExportable == true);
+
+ var finalFlags = baseFlags;
+ if (exportable)
+ {
+ finalFlags |= X509KeyStorageFlags.Exportable;
+ }
+ _log.Debug("Storing certificate with flags {flags}", finalFlags);
+
+ X509Certificate2? store;
+ if (_settings.Store.CertificateStore.UseNextGenerationCryptoApi != true)
+ {
+ // Should always be exportable before we attempt to convert,
+ // because otherwise we won't be able to get to the private key
+ store = ApplyFlags(input, baseFlags | X509KeyStorageFlags.Exportable);
+
+ // If the ConvertCertificate fails it returns null, and when that
+ // happend we apply the final flags to the original input instead
+ store = ConvertCertificate(store, finalFlags);
+ store ??= ApplyFlags(input, finalFlags);
+ }
+ else
+ {
+ // Do not attempt conversion, just apply the final flags
+ store = ApplyFlags(input, finalFlags);
+ }
+ return store;
+ }
+
+
///
/// Set the right flags on the certificate and
/// convert the private key to the right cryptographic
@@ -156,7 +209,7 @@ public X509Certificate2 ApplyFlags(X509Certificate2 original, X509KeyStorageFlag
///
///
///
- public X509Certificate2? ConvertCertificate(X509Certificate2 original, X509KeyStorageFlags flags)
+ private X509Certificate2? ConvertCertificate(X509Certificate2 original, X509KeyStorageFlags flags)
{
try
{
@@ -213,12 +266,17 @@ public X509Certificate2 ApplyFlags(X509Certificate2 original, X509KeyStorageFlag
// means we're left with a pfx generated with the
// 'wrong' Crypto provider therefor delete it to
// make sure it's retried on the next run.
- _log.Warning("Error converting private key to Microsoft RSA SChannel Cryptographic Provider");
+ _log.Warning("Error converting key to legacy CryptoAPI, using CNG instead.");
_log.Verbose("{ex}", ex);
return null;
}
}
+ ///
+ /// Find certificate in the store
+ ///
+ ///
+ ///
public X509Certificate2? GetCertificate(Func filter)
{
var possibles = new List();
diff --git a/src/main.lib/Plugins/StorePlugins/CertificateStore/FindPrivateKey.cs b/src/main.lib/Plugins/StorePlugins/CertificateStore/FindPrivateKey.cs
index dae1f56b..1a186af1 100644
--- a/src/main.lib/Plugins/StorePlugins/CertificateStore/FindPrivateKey.cs
+++ b/src/main.lib/Plugins/StorePlugins/CertificateStore/FindPrivateKey.cs
@@ -46,12 +46,16 @@ partial class FindPrivateKey
static string GetKeyFileName(X509Certificate2 cert)
{
var ecdsa = cert.GetECDsaPrivateKey();
- if (ecdsa is ECDsaCng ecdsaCng && !string.IsNullOrWhiteSpace(ecdsaCng.Key.UniqueName))
+ if (ecdsa is ECDsaCng ecdsaCng &&
+ ecdsaCng.Key != null &&
+ !string.IsNullOrWhiteSpace(ecdsaCng.Key.UniqueName))
{
return ecdsaCng.Key.UniqueName;
}
var rsa = cert.GetRSAPrivateKey();
- if (rsa is RSACng rsaCng && !string.IsNullOrWhiteSpace(rsaCng.Key.UniqueName))
+ if (rsa is RSACng rsaCng &&
+ rsaCng.Key != null &&
+ !string.IsNullOrWhiteSpace(rsaCng.Key.UniqueName))
{
return rsaCng.Key.UniqueName;
}
diff --git a/src/plugin.store.userstore/UserStore.cs b/src/plugin.store.userstore/UserStore.cs
index 48421f96..0ad0afe7 100644
--- a/src/plugin.store.userstore/UserStore.cs
+++ b/src/plugin.store.userstore/UserStore.cs
@@ -22,14 +22,12 @@ internal class UserStore : IStorePlugin, IDisposable
internal const string Name = "UserStore";
private const string DefaultStoreName = nameof(StoreName.My);
private readonly ILogService _log;
- private readonly ISettingsService _settings;
private readonly CertificateStoreClient _storeClient;
public UserStore(ILogService log, ISettingsService settings)
{
_log = log;
- _settings = settings;
- _storeClient = new CertificateStoreClient(DefaultStoreName, StoreLocation.CurrentUser, _log);
+ _storeClient = new CertificateStoreClient(DefaultStoreName, StoreLocation.CurrentUser, _log, settings);
}
public Task Save(ICertificateInfo input)
@@ -41,40 +39,8 @@ public UserStore(ILogService log, ISettingsService settings)
}
else
{
- var exportable =
- _settings.Store.CertificateStore.PrivateKeyExportable == true ||
- #pragma warning disable CS0618 // Type or member is obsolete
- (_settings.Store.CertificateStore.PrivateKeyExportable == null && _settings.Security.PrivateKeyExportable == true);
- #pragma warning restore CS0618 // Type or member is obsolete
-
- var baseFlags = X509KeyStorageFlags.UserKeySet | X509KeyStorageFlags.PersistKeySet;
- var finalFlags = baseFlags;
- if (exportable)
- {
- finalFlags |= X509KeyStorageFlags.Exportable;
- }
- _log.Debug("Storing certificate with flags {flags}", finalFlags);
-
- var store = input.Certificate;
- if (_settings.Store.CertificateStore.UseNextGenerationCryptoApi != true)
- {
- // Should always be exportable before we attempt to convert,
- // because otherwise we won't be able to get to the private key
- store = _storeClient.ApplyFlags(store, baseFlags | X509KeyStorageFlags.Exportable);
-
- // If the ConvertCertificate fails we fall back to the original
- // input certificate wit the base flags applied
- store = _storeClient.ConvertCertificate(store, finalFlags);
- store ??= _storeClient.ApplyFlags(input.Certificate, baseFlags);
- }
- else
- {
- // Do not attempt conversion, just apply the final flags
- store = _storeClient.ApplyFlags(store, finalFlags);
- }
-
_log.Information("Installing certificate in the certificate store");
- _storeClient.InstallCertificate(store);
+ _storeClient.InstallCertificate(input.Certificate);
}
return Task.FromResult(new StoreInfo() {
Name = Name,