-
Notifications
You must be signed in to change notification settings - Fork 110
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remove as much of the WINDOWS_CERT compiler directives as possible #1352
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using Calamari.Integration.Certificates.WindowsNative; | ||
#if WINDOWS_CERTIFICATE_STORE_SUPPORT | ||
using System.Linq; | ||
using System.Runtime.InteropServices; | ||
using System.Security.AccessControl; | ||
using System.Security.Cryptography; | ||
using System.Security.Cryptography.X509Certificates; | ||
using static Calamari.Integration.Certificates.WindowsNative.WindowsX509Native; | ||
#endif | ||
|
||
namespace Calamari.Integration.Certificates | ||
{ | ||
#if WINDOWS_CERTIFICATE_STORE_SUPPORT | ||
public static class CryptoKeySecurityAccessRules | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This class is just the extraction of a bunch of CryptoKey logic that was moved out of |
||
{ | ||
|
||
internal static void AddPrivateKeyAccessRules(ICollection<PrivateKeyAccessRule> accessRules, SafeCertContextHandle certificate) | ||
{ | ||
try | ||
{ | ||
var keyProvInfo = certificate.GetCertificateProperty<KeyProviderInfo>(CertificateProperty.KeyProviderInfo); | ||
|
||
// If it is a CNG key | ||
if (keyProvInfo.dwProvType == 0) | ||
{ | ||
SetCngPrivateKeySecurity(certificate, accessRules); | ||
} | ||
else | ||
{ | ||
SetCspPrivateKeySecurity(certificate, accessRules); | ||
} | ||
} | ||
catch (Exception ex) | ||
{ | ||
throw new Exception("Could not set security on private-key", ex); | ||
} | ||
} | ||
|
||
static void SetCngPrivateKeySecurity(SafeCertContextHandle certificate, ICollection<PrivateKeyAccessRule> accessRules) | ||
{ | ||
using (var key = CertificatePal.GetCngPrivateKey(certificate)) | ||
{ | ||
var security = GetCngPrivateKeySecurity(certificate); | ||
|
||
foreach (var cryptoKeyAccessRule in accessRules.Select(ToCryptoKeyAccessRule)) | ||
{ | ||
security.AddAccessRule(cryptoKeyAccessRule); | ||
} | ||
|
||
var securityDescriptorBytes = security.GetSecurityDescriptorBinaryForm(); | ||
var gcHandle = GCHandle.Alloc(securityDescriptorBytes, GCHandleType.Pinned); | ||
|
||
var errorCode = NCryptSetProperty(key, | ||
WindowsX509Native.NCryptProperties.SecurityDescriptor, | ||
gcHandle.AddrOfPinnedObject(), | ||
securityDescriptorBytes.Length, | ||
(int)WindowsX509Native.NCryptFlags.Silent | (int)WindowsX509Native.SecurityDesciptorParts.DACL_SECURITY_INFORMATION); | ||
|
||
gcHandle.Free(); | ||
|
||
if (errorCode != 0) | ||
{ | ||
throw new CryptographicException(errorCode); | ||
} | ||
} | ||
} | ||
|
||
static void SetCspPrivateKeySecurity(SafeCertContextHandle certificate, ICollection<PrivateKeyAccessRule> accessRules) | ||
{ | ||
using (var cspHandle = CertificatePal.GetCspPrivateKey(certificate)) | ||
{ | ||
var security = GetCspPrivateKeySecurity(certificate); | ||
|
||
foreach (var cryptoKeyAccessRule in accessRules.Select(ToCryptoKeyAccessRule)) | ||
{ | ||
security.AddAccessRule(cryptoKeyAccessRule); | ||
} | ||
|
||
var securityDescriptorBytes = security.GetSecurityDescriptorBinaryForm(); | ||
|
||
if (!CryptSetProvParam(cspHandle, | ||
WindowsX509Native.CspProperties.SecurityDescriptor, | ||
securityDescriptorBytes, | ||
WindowsX509Native.SecurityDesciptorParts.DACL_SECURITY_INFORMATION)) | ||
{ | ||
throw new CryptographicException(Marshal.GetLastWin32Error()); | ||
} | ||
} | ||
} | ||
|
||
static CryptoKeyAccessRule ToCryptoKeyAccessRule(PrivateKeyAccessRule privateKeyAccessRule) | ||
{ | ||
switch (privateKeyAccessRule.Access) | ||
{ | ||
case PrivateKeyAccess.ReadOnly: | ||
return new CryptoKeyAccessRule(privateKeyAccessRule.GetIdentityReference(), CryptoKeyRights.GenericRead, AccessControlType.Allow); | ||
|
||
case PrivateKeyAccess.FullControl: | ||
// We use 'GenericAll' here rather than 'FullControl' as 'FullControl' doesn't correctly set the access for CNG keys | ||
return new CryptoKeyAccessRule(privateKeyAccessRule.GetIdentityReference(), CryptoKeyRights.GenericAll, AccessControlType.Allow); | ||
|
||
default: | ||
throw new ArgumentOutOfRangeException(nameof(privateKeyAccessRule.Access)); | ||
} | ||
} | ||
|
||
static CryptoKeySecurity GetCngPrivateKeySecurity(SafeCertContextHandle certificate) | ||
{ | ||
using (var key = CertificatePal.GetCngPrivateKey(certificate)) | ||
{ | ||
var security = new CryptoKeySecurity(); | ||
security.SetSecurityDescriptorBinaryForm(CertificatePal.GetCngPrivateKeySecurity(key), | ||
AccessControlSections.Access); | ||
return security; | ||
} | ||
} | ||
|
||
static CryptoKeySecurity GetCspPrivateKeySecurity(SafeCertContextHandle certificate) | ||
{ | ||
using (var cspHandle = CertificatePal.GetCspPrivateKey(certificate)) | ||
{ | ||
var security = new CryptoKeySecurity(); | ||
security.SetSecurityDescriptorBinaryForm(CertificatePal.GetCspPrivateKeySecurity(cspHandle), AccessControlSections.Access); | ||
return security; | ||
} | ||
} | ||
|
||
|
||
public static CryptoKeySecurity GetPrivateKeySecurity(string thumbprint, StoreLocation storeLocation, string storeName) | ||
{ | ||
var store = new X509Store(storeName, storeLocation); | ||
store.Open(OpenFlags.ReadOnly); | ||
|
||
var found = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false); | ||
store.Close(); | ||
|
||
if (found.Count == 0) | ||
throw new Exception( | ||
$"Could not find certificate with thumbprint '{thumbprint}' in store Cert:\\{storeLocation}\\{storeName}"); | ||
|
||
var certificate = new SafeCertContextHandle(found[0].Handle, false); | ||
|
||
if (!certificate.HasPrivateKey()) | ||
throw new Exception("Certificate does not have a private-key"); | ||
|
||
var keyProvInfo = | ||
certificate.GetCertificateProperty<WindowsX509Native.KeyProviderInfo>(WindowsX509Native.CertificateProperty.KeyProviderInfo); | ||
|
||
// If it is a CNG key | ||
return keyProvInfo.dwProvType == 0 | ||
? GetCngPrivateKeySecurity(certificate) | ||
: GetCspPrivateKeySecurity(certificate); | ||
} | ||
} | ||
#else | ||
public static class CryptoKeySecurityAccessRules | ||
{ | ||
internal static void AddPrivateKeyAccessRules(ICollection<PrivateKeyAccessRule> accessRules, SafeCertContextHandle certificate) | ||
{ | ||
throw new NotImplementedException("Not Yet Available For NetCore"); | ||
} | ||
} | ||
#endif | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,4 @@ | ||
#if WINDOWS_CERTIFICATE_STORE_SUPPORT | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A bunch of the file changes are just this, removing the |
||
|
||
using System; | ||
using System; | ||
using System.Security.Principal; | ||
|
||
namespace Calamari.Integration.Certificates | ||
|
@@ -20,5 +18,4 @@ public static IdentityReference GetIdentityReference(this PrivateKeyAccessRule p | |
} | ||
} | ||
} | ||
} | ||
#endif | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,4 @@ | ||
#if WINDOWS_CERTIFICATE_STORE_SUPPORT | ||
using System; | ||
using System; | ||
using System.Runtime.InteropServices; | ||
using System.Security.Cryptography; | ||
using System.Text; | ||
|
@@ -115,6 +114,8 @@ public static byte[] GetCspPrivateKeySecurity(SafeCspHandle cspHandle) | |
return buffer; | ||
} | ||
|
||
// At the moment these models are not in the Calamari Solution. This is the next step. | ||
#if WINDOWS_CERTIFICATE_STORE_SUPPORT | ||
public static byte[] GetCngPrivateKeySecurity(SafeNCryptKeyHandle hObject) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The To keep the changes in multiple stages, this will be introduced in a followup PR. |
||
{ | ||
int bufferSize = 0; | ||
|
@@ -179,6 +180,7 @@ public static void DeleteCngKey(SafeNCryptKeyHandle key) | |
throw new CryptographicException(errorCode); | ||
} | ||
|
||
#endif | ||
public static string GetSubjectName(SafeCertContextHandle certificate) | ||
{ | ||
var flags = CertNameFlags.None; | ||
|
@@ -194,5 +196,4 @@ public static string GetSubjectName(SafeCertContextHandle certificate) | |
return sb.ToString(); | ||
} | ||
} | ||
} | ||
#endif | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,4 @@ | ||
#if WINDOWS_CERTIFICATE_STORE_SUPPORT | ||
#nullable disable | ||
#nullable disable | ||
using System; | ||
using System.Runtime.InteropServices; | ||
using System.Text; | ||
|
@@ -69,14 +68,7 @@ internal static extern bool CryptAcquireCertificatePrivateKey(SafeCertContextHan | |
[Out] out int dwKeySpec, | ||
[Out, MarshalAs(UnmanagedType.Bool)] out bool pfCallerFreeProvOrNCryptKey); | ||
|
||
[DllImport("Crypt32.dll", SetLastError = true)] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shuffled down to put between the other unmanaged invocations that are currently only available in netfx for this PR |
||
[return: MarshalAs(UnmanagedType.Bool)] | ||
internal static extern bool CryptAcquireCertificatePrivateKey(SafeCertContextHandle pCert, | ||
AcquireCertificateKeyOptions dwFlags, | ||
IntPtr pvReserved, // void * | ||
[Out] out SafeNCryptKeyHandle phCryptProvOrNCryptKey, | ||
[Out] out int dwKeySpec, | ||
[Out, MarshalAs(UnmanagedType.Bool)] out bool pfCallerFreeProvOrNCryptKey); | ||
|
||
|
||
[DllImport("Crypt32.dll", SetLastError = true, CharSet = CharSet.Unicode)] | ||
internal static extern | ||
|
@@ -91,6 +83,7 @@ bool CertEnumSystemStoreCallBackProto( | |
[MarshalAs(UnmanagedType.LPWStr)] string storeName, uint dwFlagsNotUsed, IntPtr notUsed1, | ||
IntPtr notUsed2, IntPtr notUsed3); | ||
|
||
#if WINDOWS_CERTIFICATE_STORE_SUPPORT | ||
[DllImport("Ncrypt.dll", SetLastError = true, ExactSpelling = true)] | ||
internal static extern int NCryptGetProperty(SafeNCryptHandle hObject, [MarshalAs(UnmanagedType.LPWStr)] string szProperty, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput, int cbOutput, ref int pcbResult, int flags); | ||
|
||
|
@@ -100,6 +93,16 @@ bool CertEnumSystemStoreCallBackProto( | |
[DllImport("Ncrypt.dll")] | ||
internal static extern int NCryptDeleteKey(SafeNCryptKeyHandle hKey, int flags); | ||
|
||
[DllImport("Crypt32.dll", SetLastError = true)] | ||
[return: MarshalAs(UnmanagedType.Bool)] | ||
internal static extern bool CryptAcquireCertificatePrivateKey(SafeCertContextHandle pCert, | ||
AcquireCertificateKeyOptions dwFlags, | ||
IntPtr pvReserved, // void * | ||
[Out] out SafeNCryptKeyHandle phCryptProvOrNCryptKey, | ||
[Out] out int dwKeySpec, | ||
[Out, MarshalAs(UnmanagedType.Bool)] out bool pfCallerFreeProvOrNCryptKey); | ||
#endif | ||
|
||
[Flags] | ||
internal enum CertStoreProviders | ||
{ | ||
|
@@ -333,5 +336,4 @@ internal struct CRYPT_BIT_BLOB | |
public int cUnusedBits; | ||
} | ||
} | ||
} | ||
#endif | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change allows us to capture the output properly when debugging tests