713 lines
30 KiB
C#
713 lines
30 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Security.AccessControl;
|
|
using System.Security.Cryptography;
|
|
using System.Security.Cryptography.X509Certificates;
|
|
using System.Security.Principal;
|
|
using Alkami.Ops.Common.Exceptions;
|
|
using Alkami.Ops.Common.Extensions;
|
|
using Alkami.Ops.Common.NativeMethods;
|
|
|
|
namespace Alkami.Ops.Common.Cryptography
|
|
{
|
|
public static class CertificateHelper
|
|
{
|
|
private const int CertStoreProvSystem = 10;
|
|
private const int CertSystemStoreCurrentUser = (1 << 16);
|
|
private const int CertSystemStoreLocalMachine = (2 << 16);
|
|
|
|
/// <summary>
|
|
/// Finds a Certificate By Thumbprint
|
|
/// </summary>
|
|
/// <param name="thumbPrint"></param>
|
|
/// <param name="storeName"></param>
|
|
/// <param name="location"></param>
|
|
/// <param name="hostName"></param>
|
|
/// <param name="machineName"></param>
|
|
/// <returns></returns>
|
|
/// <exception cref="System.ArgumentNullException"></exception>
|
|
/// <exception cref="System.ArgumentException"></exception>
|
|
/// <exception cref="System.InvalidOperationException"></exception>
|
|
/// <exception cref="System.Security.Cryptography.CryptographicException"></exception>
|
|
/// <exception cref="System.Security.SecurityException"></exception>
|
|
public static X509Certificate2 FindCertificateByThumbprint(string thumbPrint, StoreName storeName,
|
|
StoreLocation location, string hostName, string machineName = null)
|
|
{
|
|
var searchLocal =
|
|
string.Equals(hostName, Environment.MachineName, StringComparison.OrdinalIgnoreCase) ||
|
|
string.Equals(machineName, Environment.MachineName, StringComparison.OrdinalIgnoreCase) ||
|
|
string.Equals(hostName, "localhost", StringComparison.OrdinalIgnoreCase);
|
|
|
|
if (searchLocal)
|
|
{
|
|
return SearchLocalStore(thumbPrint, storeName, location, X509FindType.FindByThumbprint);
|
|
}
|
|
|
|
return SearchRemoteStoreByThumbprint(thumbPrint, storeName, location, hostName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds the WMSvc Certificate for the local machine
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public static X509Certificate2 FindIISIssuedCertificate()
|
|
{
|
|
var certificates = GetAllCertificates(StoreName.My, StoreLocation.LocalMachine, string.Empty);
|
|
|
|
foreach (var certificate in certificates)
|
|
{
|
|
var friendlyName = certificate.FriendlyName;
|
|
|
|
if (friendlyName.StartsWith("WMSVC", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return certificate;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Searches for Certificates by Subject or Subject Alternate Name
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Additionally searches for a "wildcard" match, such as *.dev.alkamitech.com
|
|
/// </remarks>
|
|
/// <param name="subjectOrSAN"></param>
|
|
/// <param name="storeName"></param>
|
|
/// <param name="storeLocation"></param>
|
|
/// <param name="machineName"></param>
|
|
/// <returns></returns>
|
|
public static X509Certificate2 FindCertificatebySubjectOrSAN(string subjectOrSAN, StoreName storeName, StoreLocation storeLocation, string machineName = null)
|
|
{
|
|
var segments = subjectOrSAN.Split('.');
|
|
segments[0] = "*";
|
|
var wildcardUri = string.Join(".", segments);
|
|
|
|
// If all we got is a *, we won't search for wildcards, since it would be unreliable
|
|
var searchWildCard = (wildcardUri != "*");
|
|
|
|
var certificates = GetAllCertificates(storeName, storeLocation, machineName);
|
|
|
|
foreach (var certificate in certificates)
|
|
{
|
|
var subjectAlternateNames = certificate.Extensions.OfType<X509Extension>().FirstOrDefault(e => e.Oid.FriendlyName == "Subject Alternative Name");
|
|
var subject = certificate.GetNameInfo(X509NameType.SimpleName, false);
|
|
|
|
if (subjectAlternateNames != null)
|
|
{
|
|
var asnData = new AsnEncodedData(subjectAlternateNames.Oid, subjectAlternateNames.RawData);
|
|
if (asnData.Format(true).IndexOf(subjectOrSAN, StringComparison.OrdinalIgnoreCase) >= 0 ||
|
|
(searchWildCard && asnData.Format(true).IndexOf(wildcardUri, StringComparison.OrdinalIgnoreCase) >= 0))
|
|
{
|
|
return certificate;
|
|
}
|
|
}
|
|
|
|
if ((subject != null) && (subject.IndexOf(subjectOrSAN, StringComparison.OrdinalIgnoreCase) >= 0 ||
|
|
(searchWildCard && subject.IndexOf(wildcardUri, StringComparison.OrdinalIgnoreCase) >= 0)))
|
|
{
|
|
return certificate;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Loads a Certificate in to the Specified Store
|
|
/// </summary>
|
|
/// <param name="certificate"></param>
|
|
/// <param name="storeName"></param>
|
|
/// <param name="location"></param>
|
|
/// <exception cref="System.ArgumentException"></exception>
|
|
/// <exception cref="System.Security.Cryptography.CryptographicException"></exception>
|
|
/// <exception cref="System.Security.SecurityException"></exception>
|
|
public static void LoadCertificateToStore(X509Certificate2 certificate, StoreName storeName, StoreLocation location)
|
|
{
|
|
var store = new X509Store(storeName, location);
|
|
|
|
try
|
|
{
|
|
store.Open(OpenFlags.ReadWrite);
|
|
store.Add(certificate);
|
|
}
|
|
finally
|
|
{
|
|
store.Close();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Loads a Certificate in to the Specified Store
|
|
/// </summary>
|
|
/// <param name="certificatePath"></param>
|
|
/// <param name="storeName"></param>
|
|
/// <param name="location"></param>
|
|
/// <param name="password"></param>
|
|
/// <exception cref="System.ArgumentException"></exception>
|
|
/// <exception cref="System.Security.Cryptography.CryptographicException"></exception>
|
|
/// <exception cref="System.Security.SecurityException"></exception>
|
|
public static void LoadCertificateToStore(string certificatePath, StoreName storeName, StoreLocation location, string password = null)
|
|
{
|
|
X509KeyStorageFlags flags;
|
|
|
|
if (location == StoreLocation.LocalMachine)
|
|
{
|
|
flags = X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet;
|
|
}
|
|
else
|
|
{
|
|
flags = X509KeyStorageFlags.Exportable | X509KeyStorageFlags.UserKeySet | X509KeyStorageFlags.PersistKeySet;
|
|
}
|
|
|
|
|
|
var certificate = !string.IsNullOrEmpty(password)
|
|
? new X509Certificate2(certificatePath, password, flags)
|
|
: new X509Certificate2(certificatePath);
|
|
|
|
LoadCertificateToStore(certificate, storeName, location);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Loads the Certificate, discarding the private key, to a store
|
|
/// </summary>
|
|
/// <param name="certificatePath"></param>
|
|
/// <param name="storeName"></param>
|
|
/// <param name="location"></param>
|
|
public static void LoadCertificateFromPFXToStore(string certificatePath, StoreName storeName, StoreLocation location)
|
|
{
|
|
var certificateOnly = new X509Certificate2(certificatePath);
|
|
LoadCertificateToStore(certificateOnly, storeName, location);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes a certificate or certificates from a store by name or thumbprint
|
|
/// </summary>
|
|
/// <param name="certificateNameOrThumbprint"></param>
|
|
/// <param name="storeName"></param>
|
|
/// <param name="location"></param>
|
|
public static void RemoveCertificateFromStore(string certificateNameOrThumbprint, StoreName storeName, StoreLocation location)
|
|
{
|
|
var store = new X509Store(storeName, location);
|
|
|
|
try
|
|
{
|
|
store.Open(OpenFlags.ReadWrite);
|
|
|
|
var certificateCollection = store.Certificates.Find(
|
|
certificateNameOrThumbprint.IsBase64String()
|
|
? X509FindType.FindByThumbprint
|
|
: X509FindType.FindBySubjectName,
|
|
certificateNameOrThumbprint, false);
|
|
|
|
store.RemoveRange(certificateCollection);
|
|
}
|
|
finally
|
|
{
|
|
store.Close();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validates a certificate chain and effective and expiration dates
|
|
/// </summary>
|
|
/// <param name="certificateNameOrThumbprint"></param>
|
|
/// <param name="storeName"></param>
|
|
/// <param name="location"></param>
|
|
public static void ValidateCertificate(string certificateNameOrThumbprint, StoreName storeName, StoreLocation location)
|
|
{
|
|
var store = new X509Store(storeName, location);
|
|
|
|
try
|
|
{
|
|
store.Open(OpenFlags.ReadWrite);
|
|
|
|
var certificateCollection = store.Certificates.Find(
|
|
certificateNameOrThumbprint.IsBase64String()
|
|
? X509FindType.FindByThumbprint
|
|
: X509FindType.FindBySubjectName,
|
|
certificateNameOrThumbprint, false);
|
|
|
|
foreach (var certificate in certificateCollection)
|
|
{
|
|
DateTime effectiveDate;
|
|
if (!DateTime.TryParse(certificate.GetEffectiveDateString(), out effectiveDate))
|
|
{
|
|
throw new InvalidCertificateException("Could not read certificate effective date", certificate.FriendlyName,
|
|
certificate.Thumbprint);
|
|
}
|
|
|
|
DateTime expirationDate;
|
|
if (!DateTime.TryParse(certificate.GetExpirationDateString(), out expirationDate))
|
|
{
|
|
throw new InvalidCertificateException("Could not read certificate expiration date", certificate.FriendlyName,
|
|
certificate.Thumbprint);
|
|
}
|
|
|
|
if (effectiveDate > DateTime.Now)
|
|
{
|
|
var message = $"Certificate effective date {effectiveDate:yyyy-mm-dd hh:mm:ss} is not yet in effect";
|
|
throw new InvalidCertificateException(message, certificate.FriendlyName, certificate.Thumbprint, effectiveDate, expirationDate);
|
|
|
|
}
|
|
|
|
if (expirationDate < DateTime.Now)
|
|
{
|
|
var message = $"Certificate expiration date {expirationDate:yyyy-mm-dd hh:mm:ss} has already passed";
|
|
throw new InvalidCertificateException(message, certificate.FriendlyName, certificate.Thumbprint, effectiveDate, expirationDate);
|
|
}
|
|
|
|
if (!certificate.Verify())
|
|
{
|
|
throw new InvalidCertificateException("Certificate chain validation failed", certificate.FriendlyName, certificate.Thumbprint,
|
|
effectiveDate, expirationDate);
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
store.Close();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Grants rights to private key files on disk
|
|
/// </summary>
|
|
/// <param name="certificateNameOrThumbprint"></param>
|
|
/// <param name="storeName"></param>
|
|
/// <param name="location"></param>
|
|
/// <param name="user"></param>
|
|
/// <exception cref="System.ArgumentNullException"></exception>
|
|
/// <exception cref="System.ArgumentOutOfRangeException"></exception>
|
|
/// <exception cref="System.ArgumentException"></exception>
|
|
/// <exception cref="System.ComponentModel.Win32Exception"></exception>
|
|
/// <exception cref="System.FormatException"></exception>
|
|
/// <exception cref="System.InvalidOperationException"></exception>
|
|
/// <exception cref="System.IO.DirectoryNotFoundException"></exception>
|
|
/// <exception cref="System.IO.FileNotFoundException"></exception>
|
|
/// <exception cref="System.IO.PathTooLongException"></exception>
|
|
/// <exception cref="System.IO.IOException"></exception>
|
|
/// <exception cref="System.NotSupportedException"></exception>
|
|
/// <exception cref="System.ObjectDisposedException"></exception>
|
|
/// <exception cref="System.OverflowException"></exception>
|
|
/// <exception cref="System.PlatformNotSupportedException"></exception>
|
|
/// <exception cref="System.Security.Cryptography.CryptographicException"></exception>
|
|
/// <exception cref="System.Security.Cryptography.CryptographicUnexpectedOperationException"></exception>
|
|
/// <exception cref="System.Security.SecurityException"></exception>
|
|
/// <exception cref="System.SystemException"></exception>
|
|
/// <exception cref="System.UnauthorizedAccessException"></exception>
|
|
public static void GrantRightsToPrivateKeys(string certificateNameOrThumbprint, StoreName storeName, StoreLocation location, string user)
|
|
{
|
|
var cert = SearchLocalStore(certificateNameOrThumbprint, storeName, location, certificateNameOrThumbprint.IsBase64String()
|
|
? X509FindType.FindByThumbprint
|
|
: X509FindType.FindBySubjectName);
|
|
|
|
var rsaKey = cert.PrivateKey as RSACryptoServiceProvider;
|
|
if (rsaKey == null)
|
|
{
|
|
throw new PrivateKeyNotFoundException(
|
|
"The key file could not be found for the certificate",
|
|
cert.GetNameInfo(X509NameType.SimpleName, false),
|
|
cert.Thumbprint,
|
|
storeName.ToString(),
|
|
location.ToString()
|
|
);
|
|
}
|
|
|
|
var keyFilePath = FindKeyLocation(rsaKey.CspKeyContainerInfo.UniqueKeyContainerName);
|
|
var pkFile = new FileInfo(Path.Combine(keyFilePath, rsaKey.CspKeyContainerInfo.UniqueKeyContainerName));
|
|
|
|
GrantRightsToPrivateKeys(pkFile, user);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Export a Certificate by Name or Thumbprint
|
|
/// </summary>
|
|
/// <param name="certificateNameOrThumbprint"></param>
|
|
/// <param name="storeName"></param>
|
|
/// <param name="location"></param>
|
|
/// <param name="exportPath"></param>
|
|
/// <param name="password"></param>
|
|
public static void ExportCertificate(string certificateNameOrThumbprint, StoreName storeName, StoreLocation location, string exportPath, string password = null)
|
|
{
|
|
var cert = SearchLocalStore(certificateNameOrThumbprint, storeName, location, certificateNameOrThumbprint.IsBase64String()
|
|
? X509FindType.FindByThumbprint
|
|
: X509FindType.FindBySubjectName);
|
|
|
|
ExportCertificate(cert, exportPath, password);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Exports a specific certificate to the destination exportPath folder.
|
|
/// </summary>
|
|
/// <param name="certificate">The certificate to be exported.</param>
|
|
/// <param name="exportPath">The path of the folder to export the certificate to.</param>
|
|
/// <param name="password">Optional password for the certificate.</param>
|
|
public static void ExportCertificate(X509Certificate2 certificate, string exportPath, string password = null)
|
|
{
|
|
// Don't export expired certificates.
|
|
bool expired = DateTime.Now > certificate.NotAfter;
|
|
if (expired)
|
|
{
|
|
return;
|
|
}
|
|
|
|
bool hasPassword = !string.IsNullOrEmpty(password);
|
|
|
|
// Get the byte data of the certificate.
|
|
byte[] bytes;
|
|
string extension;
|
|
if (hasPassword)
|
|
{
|
|
if (!certificate.HasPrivateKey)
|
|
{
|
|
extension = ".cer";
|
|
bytes = certificate.Export(X509ContentType.Cert);
|
|
}
|
|
else
|
|
{
|
|
|
|
extension = ".pfx";
|
|
bytes = certificate.Export(X509ContentType.Pkcs12, password);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
extension = ".cer";
|
|
bytes = certificate.Export(X509ContentType.Cert);
|
|
}
|
|
|
|
// Determine the name of the file by friendly name and thumbprint for uniqueness.
|
|
string certName = certificate.GetNameInfo(X509NameType.SimpleName, false).GetSanitizedFileName() + "-" + certificate.Thumbprint;
|
|
|
|
// Save the cert.
|
|
var exportFileName = Path.Combine(exportPath, certName + extension);
|
|
File.WriteAllBytes(exportFileName, bytes);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Export All Certificates from a Specific Store
|
|
/// </summary>
|
|
/// <param name="storeName"></param>
|
|
/// <param name="location"></param>
|
|
/// <param name="exportPath"></param>
|
|
/// <param name="password"></param>
|
|
public static List<CertificateExportException> ExportAllCertificates(StoreName storeName, StoreLocation location, string exportPath, string password = null)
|
|
{
|
|
var store = new X509Store(storeName, location);
|
|
var exceptionList = new List<CertificateExportException>();
|
|
|
|
try
|
|
{
|
|
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
|
|
|
|
foreach (var cert in store.Certificates)
|
|
{
|
|
try
|
|
{
|
|
ExportCertificate(cert, exportPath, password);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
exceptionList.Add(new CertificateExportException("Could not Export Certificate Due to Errors",
|
|
cert.GetNameInfo(X509NameType.SimpleName, false), cert.SubjectName.Name,
|
|
cert.Thumbprint, ex.Message));
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
store.Close();
|
|
}
|
|
|
|
return exceptionList;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns all Certificates from the Specified Store as an X509Certificate2Collection
|
|
/// </summary>
|
|
/// <param name="storeName"></param>
|
|
/// <param name="location"></param>
|
|
/// <param name="machineName"></param>
|
|
/// <returns></returns>
|
|
public static X509Certificate2Collection GetAllCertificates(StoreName storeName, StoreLocation location, string machineName = null)
|
|
{
|
|
return (string.Equals(machineName, Environment.MachineName, StringComparison.OrdinalIgnoreCase) ||
|
|
string.IsNullOrEmpty(machineName))
|
|
? GetCertificatesFromLocalStore(storeName, location)
|
|
: GetCertificatesFromRemoteStore(storeName, location, machineName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns Certificates from the Specified Remote Store
|
|
/// </summary>
|
|
/// <param name="storeName"></param>
|
|
/// <param name="location"></param>
|
|
/// <param name="machineName"></param>
|
|
/// <returns></returns>
|
|
private static X509Certificate2Collection GetCertificatesFromRemoteStore(StoreName storeName, StoreLocation location, string machineName)
|
|
{
|
|
var safeHandle = IntPtr.Zero;
|
|
|
|
try
|
|
{
|
|
safeHandle = GetRemoteCertificateStore(storeName, location, machineName, safeHandle);
|
|
|
|
if (safeHandle == IntPtr.Zero)
|
|
{
|
|
return new X509Certificate2Collection();
|
|
}
|
|
|
|
var currentCertContext = IntPtr.Zero;
|
|
|
|
var store = new X509Store("temp");
|
|
store.Open(OpenFlags.ReadWrite);
|
|
|
|
do
|
|
{
|
|
currentCertContext = SafeNativeMethods.CertEnumCertificatesInStore(safeHandle, currentCertContext);
|
|
|
|
if (currentCertContext == IntPtr.Zero)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
store.Add(new X509Certificate2(currentCertContext));
|
|
|
|
} while (currentCertContext != (IntPtr)0);
|
|
|
|
return store.Certificates;
|
|
}
|
|
finally
|
|
{
|
|
if (safeHandle != IntPtr.Zero)
|
|
{
|
|
SafeNativeMethods.CertCloseStore(safeHandle, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns Certificates from the Specified Local Store
|
|
/// </summary>
|
|
/// <param name="storeName"></param>
|
|
/// <param name="location"></param>
|
|
/// <returns></returns>
|
|
private static X509Certificate2Collection GetCertificatesFromLocalStore(StoreName storeName, StoreLocation location)
|
|
{
|
|
var store = new X509Store(storeName, location);
|
|
|
|
try
|
|
{
|
|
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
|
|
return store.Certificates;
|
|
}
|
|
finally
|
|
{
|
|
store.Close();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Grants rights to private key files on disk
|
|
/// </summary>
|
|
/// <param name="pkFile"></param>
|
|
/// <param name="user"></param>
|
|
/// <exception cref="System.ArgumentNullException"></exception>
|
|
/// <exception cref="System.ArgumentOutOfRangeException"></exception>
|
|
/// <exception cref="System.ArgumentException"></exception>
|
|
/// <exception cref="System.IO.IOException"></exception>
|
|
/// <exception cref="System.PlatformNotSupportedException"></exception>
|
|
/// <exception cref="System.Security.AccessControl.PrivilegeNotHeldException"></exception>
|
|
/// <exception cref="System.SystemException"></exception>
|
|
/// <exception cref="System.UnauthorizedAccessException"></exception>
|
|
public static void GrantRightsToPrivateKeys(FileInfo pkFile, string user)
|
|
{
|
|
if (!pkFile.Exists)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var fs = pkFile.GetAccessControl();
|
|
|
|
var account = new NTAccount(user);
|
|
fs.AddAccessRule(new FileSystemAccessRule(account, FileSystemRights.FullControl, AccessControlType.Allow));
|
|
|
|
pkFile.SetAccessControl(fs);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Safely Searches a Local Certificate Store
|
|
/// </summary>
|
|
/// <param name="name"></param>
|
|
/// <param name="storeName"></param>
|
|
/// <param name="location"></param>
|
|
/// <param name="findType"></param>
|
|
/// <returns></returns>
|
|
private static X509Certificate2 SearchLocalStore(string name, StoreName storeName, StoreLocation location, X509FindType findType)
|
|
{
|
|
var store = new X509Store(storeName, location);
|
|
|
|
try
|
|
{
|
|
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
|
|
|
|
return store.Certificates.Find(findType, name, false)
|
|
.OfType<X509Certificate2>()
|
|
.FirstOrDefault();
|
|
}
|
|
finally
|
|
{
|
|
store.Close();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Searches a Remote Store By Thumbprint
|
|
/// </summary>
|
|
/// <param name="thumbPrint"></param>
|
|
/// <param name="storeName"></param>
|
|
/// <param name="location"></param>
|
|
/// <param name="machineName"></param>
|
|
/// <param name="password"></param>
|
|
/// <returns>a X509Certificate2</returns>
|
|
private static X509Certificate2 SearchRemoteStoreByThumbprint(string thumbPrint, StoreName storeName, StoreLocation location, string machineName, string password = null)
|
|
{
|
|
var safeHandle = IntPtr.Zero;
|
|
try
|
|
{
|
|
safeHandle = GetRemoteCertificateStore(storeName, location, machineName, safeHandle);
|
|
|
|
if (safeHandle != IntPtr.Zero)
|
|
{
|
|
var currentCertContext = IntPtr.Zero;
|
|
|
|
do
|
|
{
|
|
currentCertContext = SafeNativeMethods.CertEnumCertificatesInStore(safeHandle,
|
|
currentCertContext);
|
|
|
|
if (currentCertContext == IntPtr.Zero)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(password))
|
|
{
|
|
var cert = new X509Certificate2(currentCertContext);
|
|
|
|
if (string.Equals(cert.Thumbprint, thumbPrint, StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
return cert;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var store = new X509Store("temp");
|
|
|
|
store.Open(OpenFlags.ReadWrite);
|
|
store.Add(new X509Certificate2(currentCertContext));
|
|
|
|
foreach (var certificate in store.Certificates)
|
|
{
|
|
if (string.Equals(certificate.Thumbprint, thumbPrint, StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
return certificate;
|
|
}
|
|
}
|
|
}
|
|
} while (currentCertContext != (IntPtr)0);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
finally
|
|
{
|
|
if (safeHandle != IntPtr.Zero)
|
|
{
|
|
SafeNativeMethods.CertCloseStore(safeHandle, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a Remote Certificate Store
|
|
/// </summary>
|
|
/// <param name="storeName"></param>
|
|
/// <param name="location"></param>
|
|
/// <param name="machineName"></param>
|
|
/// <param name="safeHandle"></param>
|
|
/// <returns>An Integer Pointer (IntPtr)</returns>
|
|
/// <exception cref="System.ArgumentNullException"></exception>
|
|
/// <exception cref="System.FormatException"></exception>
|
|
// ReSharper disable once RedundantAssignment
|
|
private static IntPtr GetRemoteCertificateStore(StoreName storeName, StoreLocation location, string machineName, IntPtr safeHandle)
|
|
{
|
|
if (location == StoreLocation.CurrentUser)
|
|
{
|
|
safeHandle = SafeNativeMethods.CertOpenStore(CertStoreProvSystem, 0, 0,
|
|
CertSystemStoreCurrentUser, $@"\\{machineName}\{storeName}");
|
|
}
|
|
else
|
|
{
|
|
safeHandle = SafeNativeMethods.CertOpenStore(CertStoreProvSystem, 0, 0,
|
|
CertSystemStoreLocalMachine, $@"\\{machineName}\{storeName}");
|
|
}
|
|
|
|
return safeHandle;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds the Keyfile Location on Disk
|
|
/// </summary>
|
|
/// <param name="keyFileName"></param>
|
|
/// <returns></returns>
|
|
/// <exception cref="System.ArgumentNullException"></exception>
|
|
/// <exception cref="System.ArgumentException"></exception>
|
|
/// <exception cref="System.IO.DirectoryNotFoundException"></exception>
|
|
/// <exception cref="System.IO.PathTooLongException"></exception>
|
|
/// <exception cref="System.IO.IOException"></exception>
|
|
/// <exception cref="System.OverflowException"></exception>
|
|
/// <exception cref="System.PlatformNotSupportedException"></exception>
|
|
/// <exception cref="System.UnauthorizedAccessException"></exception>
|
|
public static string FindKeyLocation(string keyFileName)
|
|
{
|
|
var commonAppDataPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
|
|
var machineKeyPath = commonAppDataPath + @"\Microsoft\Crypto\RSA\MachineKeys";
|
|
|
|
var fileList = Directory.EnumerateFiles(machineKeyPath, keyFileName);
|
|
if (fileList.Any())
|
|
{
|
|
return machineKeyPath;
|
|
}
|
|
|
|
var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
|
var rsaKeyPath = appDataPath + @"\Microsoft\Crypto\RSA\";
|
|
|
|
var directoryArray = Directory.GetDirectories(rsaKeyPath);
|
|
if (directoryArray.Length > 0)
|
|
{
|
|
foreach (var directory in directoryArray)
|
|
{
|
|
var matchingDirectories = Directory.EnumerateFiles(directory, keyFileName);
|
|
if (matchingDirectories.Any())
|
|
{
|
|
return directory;
|
|
}
|
|
}
|
|
}
|
|
|
|
var cryptoKeyPath = appDataPath + @"\Microsoft\Crypto\Keys\";
|
|
var keyPath = Path.Combine(cryptoKeyPath, keyFileName);
|
|
if(File.Exists(keyPath))
|
|
{
|
|
return cryptoKeyPath;
|
|
}
|
|
|
|
return string.Empty;
|
|
}
|
|
}
|
|
} |