Skip to content

Commit

Permalink
Add the ability to override the certificate provider, when installing…
Browse files Browse the repository at this point in the history
… a certificate.

When creating certificates using ssl_certificates it will what ever the system default is set to.
  • Loading branch information
Taliesin Sisson committed Jul 10, 2017
1 parent 9b1bf2f commit 4898419
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 28 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ Installs a certificate into the Windows certificate store from a file, and grant
- `private_key_acl` - array of 'domain\account' entries to be granted read-only access to the certificate's private key. This is not idempotent.
- `store_name` - the certificate store to manipulate. One of MY (default : personal store), CA (trusted intermediate store) or ROOT (trusted root store).
- `user_store` - if false (default) then use the local machine store; if true then use the current user's store.
- `provider_name` - if `Default` (default) then use pfx provided provider name. Valid provider names are `Default`, `Microsoft Enhanced RSA and AES Cryptographic Provider`, `Microsoft RSA SChannel Cryptographic Provider`, `Microsoft Strong Cryptographic Provider` or `Microsoft Software Key Storage Provider`

#### Examples

Expand Down
179 changes: 151 additions & 28 deletions resources/certificate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,31 +25,40 @@
property :private_key_acl, Array
property :store_name, String, default: 'MY', regex: /^(?:MY|CA|ROOT|TrustedPublisher|TRUSTEDPEOPLE)$/
property :user_store, [true, false], default: false
property :provider_name, String, default: 'Default', regex: /^(?:Microsoft Enhanced RSA and AES Cryptographic Provider|Microsoft RSA SChannel Cryptographic Provider|Microsoft Strong Cryptographic Provider|Microsoft Software Key Storage Provider|Default)$/

action :create do
hash = '$cert.GetCertHashString()'
code_script = <<-EOH
#{library_script}
#{cert_script(true)}
if (Get-Command Get-PfxCertificate -ErrorAction SilentlyContinue) {
$filePath = '#{win_friendly_path(new_resource.source)}'
$filePathExtension = [IO.Path]::GetExtension($filePath).ToLower()
$certStoreLocation = 'Cert:\\#{cert_location}\\#{new_resource.store_name}'
if ($filePathExtension -eq '.pfx') {
$password = '#{new_resource.pfx_password}'
if ($password)
{
$securePassword = ConvertTo-SecureString $password -AsPlainText -Force
} else {
$securePassword = $null
}
Import-PfxCertificate -FilePath $filePath -CertStoreLocation $certStoreLocation -Password $securePassword
$providerName = '#{provider_name}'
if ($providerName -ne '' -and $providerName -ne 'Default'){
$cspParameters = [Contoso.CryptoTriage]::GetCspParameters($cert)
$cspParameters.pwszProvName = $providerName
$cspParameters.dwKeySpec = [Contoso.CryptoTriage+ProviderKeySpecifier]::AT_KEYEXCHANGE -bor [Contoso.CryptoTriage+ProviderKeySpecifier]::AT_SIGNATURE
$cspParameters.cProvParam = 0
$cspParameters.rgProvParam = 0
if ($providerName -eq 'Microsoft Enhanced RSA and AES Cryptographic Provider'){
$cspParameters.dwProvType = [Contoso.CryptoTriage+ProviderType]::PROV_RSA_AES
} else if ($providerName -eq 'Microsoft RSA SChannel Cryptographic Provider'){
$cspParameters.dwProvType = [Contoso.CryptoTriage+ProviderType]::PROV_RSA_SCHANNEL
} else if ($providerName -eq 'Microsoft Strong Cryptographic Provider'){
$cspParameters.dwProvType = [Contoso.CryptoTriage+ProviderType]::PROV_RSA_FULL
} else if ($providerName -eq 'Microsoft Software Key Storage Provider'){
$cspParameters.dwProvType = [Contoso.CryptoTriage+ProviderType]::CNG
$cspParameters.dwKeySpec = 0
$cspParameters.dwKeySpec = [Contoso.CryptoTriage+ProviderKeySpecifier]::CERT_NCRYPT_KEY_SPEC
} else {
Import-Certificate -FilePath $filePath -CertStoreLocation $certStoreLocation
throw "Unknown provider name - $providerName"
}
} else {
#{within_store_script { |store| store + '.Add($cert)' }}
[Contoso.CryptoTriage]::SetCspParameters($cert, $cspParameters)
}
#{within_store_script { |store| store + '.Add($cert)' }}
#{acl_script(hash)}
EOH

Expand All @@ -68,15 +77,16 @@

# acl_add is a modify-if-exists operation : not idempotent
action :acl_add do
code_script = library_script
guard_script = library_script

if ::File.exist?(new_resource.source)
hash = '$cert.GetCertHashString()'
code_script = cert_script(false)
guard_script = cert_script(false)
code_script << cert_script(false)
guard_script << cert_script(false)
else
# make sure we have no spaces in the hash string
hash = "\"#{new_resource.source.gsub(/\s/, '')}\""
code_script = ''
guard_script = ''
end
code_script << acl_script(hash)
guard_script << cert_exists_script(hash)
Expand Down Expand Up @@ -158,11 +168,10 @@ def within_store_script
EOH
end

def acl_script(hash)
return '' if new_resource.private_key_acl.nil? || new_resource.private_key_acl.empty?
# this PS came from https://www.powershellgallery.com/packages/GuardedFabricTools/0.2.0/Content/CertificateManagement.psm1
set_acl_script = <<-EOH
def library_script
# Most of this PS came from https://www.powershellgallery.com/packages/GuardedFabricTools/0.2.0/Content/CertificateManagement.psm1

set_library_script = <<-EOH
function Test-Nano
{
$EditionId = (Get-ItemProperty -Path 'HKLM:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion' -Name 'EditionID').EditionId
Expand Down Expand Up @@ -274,6 +283,7 @@ def acl_script(hash)
NotSupported = unchecked((int)0x80090029) // NTE_NOT_SUPPORTED
}
[Flags]
public enum KeySpec : uint
{
NONE = 0x0,
Expand Down Expand Up @@ -389,7 +399,115 @@ def acl_script(hash)
ProvParam dwParam,
[MarshalAs(UnmanagedType.LPArray)] byte[] pbData,
SECURITY_INFORMATION dwFlags);
public enum ProviderType : uint
{
CNG = 0,
PROV_RSA_FULL = 1,
PROV_RSA_SIG = 2,
PROV_DSS = 3,
PROV_FORTEZZA = 4,
PROV_MS_EXCHANGE = 5,
PROV_SSL = 6,
PROV_RSA_SCHANNEL = 12,
PROV_DSS_DH = 13,
PROV_EC_ECDSA_SIG = 14,
PROV_EC_ECNRA_SIG = 15,
PROV_EC_ECDSA_FULL = 16,
PROV_EC_ECNRA_FULL = 17,
PROV_DH_SCHANNEL = 18,
PROV_SPYRUS_LYNKS = 20,
PROV_RNG = 21,
PROV_INTEL_SEC = 22,
PROV_REPLACE_OWF = 23,
PROV_RSA_AES = 24,
}
[Flags]
public enum ProviderKeyFlags : uint
{
CERT_SET_KEY_PROV_HANDLE_PROP_ID_OR_CERT_SET_KEY_CONTEXT_PROP_ID = 1,
CRYPT_MACHINE_KEYSET_OR_NCRYPT_MACHINE_KEY_FLAG = 32,
CRYPT_SILENT_OR_NCRYPT_SILENT_FLAG = 64,
}
[Flags]
public enum ProviderKeySpecifier : uint
{
NONE = 0x0,
AT_KEYEXCHANGE = 0x1,
AT_SIGNATURE = 2,
CERT_NCRYPT_KEY_SPEC = 0xFFFFFFFF
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CRYPT_KEY_PROV_INFO
{
[MarshalAs(UnmanagedType.LPWStr)]
public string pwszContainerName;
[MarshalAs(UnmanagedType.LPWStr)]
public string pwszProvName;
public ProviderType dwProvType;
public ProviderKeyFlags dwFlags;
public uint cProvParam;
public IntPtr rgProvParam;
public ProviderKeySpecifier dwKeySpec;
}
[DllImport(CRYPT32, CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool CertGetCertificateContextProperty(
IntPtr pCertContext,
uint dwPropId,
IntPtr pvData,
ref uint pcbData);
[DllImport(CRYPT32, EntryPoint = "CertSetCertificateContextProperty", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern Boolean CertSetCertificateContextProperty(
IntPtr pCertContext,
Int32 dwPropId,
Int32 dwFlags,
IntPtr pvData);
public static CRYPT_KEY_PROV_INFO GetCspParameters(X509Certificate2 certificate) {
const int CERT_KEY_PROV_INFO_PROP_ID = 2;
uint pcbData = 0;
if (!CertGetCertificateContextProperty(certificate.Handle, CERT_KEY_PROV_INFO_PROP_ID, IntPtr.Zero, ref pcbData))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
IntPtr pvData = Marshal.AllocHGlobal((int)pcbData);
try{
if (!CertGetCertificateContextProperty(certificate.Handle, CERT_KEY_PROV_INFO_PROP_ID, pvData, ref pcbData))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
var cryptoKeyProvInfo = (CRYPT_KEY_PROV_INFO)Marshal.PtrToStructure(pvData, typeof(CRYPT_KEY_PROV_INFO));
return cryptoKeyProvInfo;
} finally {
Marshal.FreeHGlobal(pvData);
}
}
public static bool SetCspParameters(X509Certificate2 certificate, CRYPT_KEY_PROV_INFO cryptKeyProvInfo)
{
const int CERT_KEY_PROV_INFO_PROP_ID = 2;
IntPtr pvData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CRYPT_KEY_PROV_INFO)));
try
{
Marshal.StructureToPtr(cryptKeyProvInfo, pvData, false);
return CertSetCertificateContextProperty(certificate.Handle, CERT_KEY_PROV_INFO_PROP_ID, 0, pvData);
} finally {
Marshal.FreeHGlobal(pvData);
}
}
public static TriageHandle TriageAcquireKeyHandle(X509Certificate2 certificate)
{
SafeNCryptKeyHandle ncryptKeyHandle = null;
Expand Down Expand Up @@ -871,13 +989,18 @@ def acl_script(hash)
# add the new rule
$newSD.AddAccessRule($Rule)
Write-Output $newSD
}
}
EOH
set_library_script
end

def acl_script(hash)
return '' if new_resource.private_key_acl.nil? || new_resource.private_key_acl.empty?
set_acl_script = <<-EOH
$hash = #{hash}
$Location = '#{cert_location}'
$StoreName = '#{new_resource.store_name}'
$CertificatePath = "cert:\\$($Location)\\$($StoreName)\\$($Thumbprint)"
$Certificate = Get-ChildItem $CertificatePath
if ($Certificate -eq $null) {
Expand Down

0 comments on commit 4898419

Please sign in to comment.