diff options
author | John McCrae <mccrae@progress.com> | 2023-02-05 08:20:57 +0600 |
---|---|---|
committer | John <john.mccrae@progress.com> | 2023-02-28 18:27:19 -0800 |
commit | 9f8c667ae2b0ccdd9fa0a77a7d215aaf2df98c5f (patch) | |
tree | 2b923e5db2ba206ead8717eb6be03795b6fa7691 | |
parent | 625558f029d1503d03e7cc42ab9c69d210fa7fa3 (diff) | |
download | chef-9f8c667ae2b0ccdd9fa0a77a7d215aaf2df98c5f.tar.gz |
Correcting cert retrieval issues for multiple user scenarios
Signed-off-by: John McCrae <mccrae@progress.com>
-rw-r--r-- | .expeditor/scripts/prep_and_run_tests.ps1 | 2 | ||||
-rw-r--r-- | chef-config/lib/chef-config/config.rb | 10 | ||||
-rw-r--r-- | lib/chef/client.rb | 29 | ||||
-rw-r--r-- | lib/chef/http/authenticator.rb | 151 | ||||
-rw-r--r-- | spec/integration/client/client_spec.rb | 38 | ||||
-rw-r--r-- | spec/unit/client_spec.rb | 28 | ||||
-rw-r--r-- | spec/unit/http/authenticator_spec.rb | 75 |
7 files changed, 263 insertions, 70 deletions
diff --git a/.expeditor/scripts/prep_and_run_tests.ps1 b/.expeditor/scripts/prep_and_run_tests.ps1 index 76e475cd8a..bbf798cbd2 100644 --- a/.expeditor/scripts/prep_and_run_tests.ps1 +++ b/.expeditor/scripts/prep_and_run_tests.ps1 @@ -11,7 +11,7 @@ if ($TestType -eq 'Functional') { } Write-Output "--- Running Chef bundle install" -bundle install --jobs=3 --retry=3 +bundle install --jobs=3 --retry=3 switch ($TestType) { "Unit" {[string[]]$RakeTest = 'spec:unit','component_specs'; break} diff --git a/chef-config/lib/chef-config/config.rb b/chef-config/lib/chef-config/config.rb index 2932fdc82f..6b6d9de427 100644 --- a/chef-config/lib/chef-config/config.rb +++ b/chef-config/lib/chef-config/config.rb @@ -778,6 +778,16 @@ module ChefConfig # Then the public key from the new cert is pushed to Chef Server for authentication default :migrate_key_to_keystore, false + # When we move certs into the certstore, we need to manage multi-user scenarios. + # This flag is used in conjunction with 'migrate_key_to_keystore'. If 2 users, Bob and Terri, are using + # this node, we need a means to separate the private keys of each user. Alternately, if an Admin + # configures the node and then relies on a system account to run chef afterward, we need a second + # method for that. Setting this in the client.rb file with a "user" flag will cause chef to create + # and manage separate private keys. We look for this flag to be set to "user" to manage distinct users keys with, + # However, if that key is set to anything else, we assume that keys are to be stored in the LocalMachine store. + # Leaving this key omitted has the same effect as setting it to anything other than "user" + default :auth_key_registry_type, nil + # When registering the client, should we allow the client key location to # be a symlink? eg: /etc/chef/client.pem -> /etc/chef/prod-client.pem # If the path of the key goes through a directory like /tmp this should diff --git a/lib/chef/client.rb b/lib/chef/client.rb index e5fcd56eb0..743b5ec473 100644 --- a/lib/chef/client.rb +++ b/lib/chef/client.rb @@ -66,6 +66,15 @@ class Chef class Client CRYPT_EXPORTABLE = 0x00000001 + # adding these + # certstore 65536 == 0x00010000 == CurrentUser + # certstore 131072 == 0x00020000 == LocalMachine + # Reference: https://github.com/chef/win32-certstore/blob/main/lib/win32/certstore/mixin/crypto.rb#L90 + CERT_SYSTEM_STORE_LOCAL_MACHINE = 0x00020000 + CERT_SYSTEM_STORE_CURRENT_USER = 0x00010000 + CERT_SYSTEM_STORE_SERVICES = 0x00050000 + CERT_SYSTEM_STORE_USERS = 0x00060000 + attr_reader :local_context extend Chef::Mixin::Deprecation @@ -674,9 +683,15 @@ class Chef # In the brave new world of No Certs On Disk, we want to put the pem file into Keychain or the Certstore # But is it already there? + # We're solving the multi-user scenario where both a system/admin user can run on the box but also someone without + # admin rights can also run correctly locally. def check_certstore_for_key(cert_name) require "win32-certstore" - win32certstore = ::Win32::Certstore.open("MY") + if Chef::Config[:auth_key_registry_type] == "user" + win32certstore = ::Win32::Certstore.open("MY", store_location: CERT_SYSTEM_STORE_CURRENT_USER) + else + win32certstore = ::Win32::Certstore.open("MY") + end win32certstore.search("#{cert_name}") end @@ -783,8 +798,6 @@ class Chef require "time" unless defined?(Time) autoload :URI, "uri" - # KeyMigration.instance.key_migrated = true - node = Chef::Config[:node_name] d = Time.now if d.month == 10 || d.month == 11 || d.month == 12 @@ -818,9 +831,13 @@ class Chef require "win32-certstore" tempfile = Tempfile.new("#{Chef::Config[:node_name]}.pfx") File.open(tempfile, "wb") { |f| f.print new_pfx.to_der } - - store = ::Win32::Certstore.open("MY") - store.add_pfx(tempfile, password, CRYPT_EXPORTABLE) + # Need to determine where to store the key + if Chef::Config[:auth_key_registry_type] == "user" + win32certstore = ::Win32::Certstore.open("MY", store_location: CERT_SYSTEM_STORE_CURRENT_USER) + else + win32certstore = ::Win32::Certstore.open("MY") + end + win32certstore.add_pfx(tempfile, password, CRYPT_EXPORTABLE) tempfile.unlink end diff --git a/lib/chef/http/authenticator.rb b/lib/chef/http/authenticator.rb index c795297397..10d85db4e6 100644 --- a/lib/chef/http/authenticator.rb +++ b/lib/chef/http/authenticator.rb @@ -102,12 +102,12 @@ class Chef self.class.get_cert_password end - def encrypt_pfx_pass - self.class.encrypt_pfx_pass + def encrypt_pfx_pass_with_vector + self.class.encrypt_pfx_pass_with_vector end - def decrypt_pfx_pass - self.class.decrypt_pfx_pass + def decrypt_pfx_pass_with_vector + self.class.decrypt_pfx_pass_with_vector end # Detects if a private key exists in a certificate repository like Keychain (macOS) or Certificate Store (Windows) @@ -123,9 +123,18 @@ class Chef end end + def self.get_cert_user + Chef::Config[:auth_key_registry_type] == "user" ? store = "CurrentUser" : store = "LocalMachine" + end + + def self.get_registry_user + Chef::Config[:auth_key_registry_type] == "user" ? store = "HKEY_CURRENT_USER" : store = "HKEY_LOCAL_MACHINE" + end + def self.check_certstore_for_key(client_name) + store = get_cert_user powershell_code = <<~CODE - $cert = Get-ChildItem -path cert:\\LocalMachine\\My -Recurse -Force | Where-Object { $_.Subject -Match "chef-#{client_name}" } -ErrorAction Stop + $cert = Get-ChildItem -path cert:\\#{store}\\My -Recurse -Force | Where-Object { $_.Subject -Match "chef-#{client_name}" } -ErrorAction Stop if (($cert.HasPrivateKey -eq $true) -and ($cert.PrivateKey.Key.ExportPolicy -ne "NonExportable")) { return $true } @@ -164,49 +173,86 @@ class Chef end def self.get_cert_password + store = get_registry_user @win32registry = Chef::Win32::Registry.new - path = "HKEY_LOCAL_MACHINE\\Software\\Progress\\Authentication" + path = "#{store}\\Software\\Progress\\Authentication" # does the registry key even exist? - present = @win32registry.get_values(path) - if present.nil? || present.empty? + # password_blob should be an array of hashes + password_blob = @win32registry.get_values(path) + if password_blob.nil? || password_blob.empty? raise Chef::Exceptions::Win32RegKeyMissing end - present.each do |secret| - if secret[:name] == "PfxPass" - password = decrypt_pfx_pass(secret[:data]) - return password + # Did someone have just the password stored in the registry? + raw_data = password_blob.map { |x| x[:data] } + vector = raw_data[2] + if !!vector + decrypted_password = decrypt_pfx_pass_with_vector(password_blob) + else + decrypted_password = decrypt_pfx_pass_with_password(password_blob) + if !!decrypted_password + migrate_pass_to_use_vector(decrypted_password) + else + Chef::Log.error("Failed to retrieve certificate password") end end - - raise Chef::Exceptions::Win32RegKeyMissing - + decrypted_password rescue Chef::Exceptions::Win32RegKeyMissing # if we don't have a password, log that and generate one - Chef::Log.warn "Authentication Hive and values not present in registry, creating them now" - new_path = "HKEY_LOCAL_MACHINE\\Software\\Progress\\Authentication" + store = get_registry_user + new_path = "#{store}\\Software\\Progress\\Authentication" unless @win32registry.key_exists?(new_path) @win32registry.create_key(new_path, true) end - require "securerandom" unless defined?(SecureRandom) - size = 14 - password = SecureRandom.alphanumeric(size) - encrypted_pass = encrypt_pfx_pass(password) - values = { name: "PfxPass", type: :string, data: encrypted_pass } - @win32registry.set_value(new_path, values) - password + create_and_store_new_password end - def self.encrypt_pfx_pass(password) + def self.encrypt_pfx_pass_with_vector(password) powershell_code = <<~CODE - $encrypted_string = ConvertTo-SecureString "#{password}" -AsPlainText -Force - $secure_string = ConvertFrom-SecureString $encrypted_string - return $secure_string + $AES = [System.Security.Cryptography.Aes]::Create() + $key_temp = [System.Convert]::ToBase64String($AES.Key) + $iv_temp = [System.Convert]::ToBase64String($AES.IV) + $encryptor = $AES.CreateEncryptor() + [System.Byte[]]$Bytes = [System.Text.Encoding]::Unicode.GetBytes("#{password}") + $EncryptedBytes = $encryptor.TransformFinalBlock($Bytes,0,$Bytes.Length) + $EncryptedBase64String = [System.Convert]::ToBase64String($EncryptedBytes) + # create array of encrypted pass, key, iv + $password_blob = @($EncryptedBase64String, $key_temp, $iv_temp) + return $password_blob CODE powershell_exec!(powershell_code).result end - def self.decrypt_pfx_pass(password) + def self.decrypt_pfx_pass_with_vector(password_blob) + raw_data = password_blob.map { |x| x[:data] } + password = raw_data[0] + key = raw_data[1] + vector = raw_data[2] + + powershell_code = <<~CODE + $KeyBytes = [System.Convert]::FromBase64String("#{key}") + $IVBytes = [System.Convert]::FromBase64String("#{vector}") + $aes = [System.Security.Cryptography.Aes]::Create() + $aes.Key = $KeyBytes + $aes.IV = $IVBytes + $EncryptedBytes = [System.Convert]::FromBase64String("#{password}") + $Decryptor = $aes.CreateDecryptor() + $DecryptedBytes = $Decryptor.TransformFinalBlock($EncryptedBytes,0,$EncryptedBytes.Length) + $DecryptedString = [System.Text.Encoding]::Unicode.GetString($DecryptedBytes) + return $DecryptedString + CODE + results = powershell_exec!(powershell_code).result + end + + def self.decrypt_pfx_pass_with_password(password_blob) + password = "" + password_blob.each do |secret| + if secret[:name] == "PfxPass" + password = secret[:data] + else + Chef::Log.error("Failed to retrieve a password for the private key") + end + end powershell_code = <<~CODE $secure_string = "#{password}" | ConvertTo-SecureString $string = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR((($secure_string)))) @@ -215,14 +261,49 @@ class Chef powershell_exec!(powershell_code).result end - def self.retrieve_certificate_key(client_name) - require "openssl" unless defined?(OpenSSL) + def self.migrate_pass_to_use_vector(password) + store = get_cert_user + corrected_store = (store == "CurrentUser" ? "HKCU" : "HKLM") + powershell_code = <<~CODE + Remove-ItemProperty -Path "#{corrected_store}:\\Software\\Progress\\Authentication" -Name "PfXPass" + CODE + powershell_exec!(powershell_code) + create_and_store_new_password(password) + end + # This method name is a bit of a misnomer. We call it to legit create a new password using the vector format. + # But we also call it with a password that needs to be migrated to use the vector format too. + def self.create_and_store_new_password(password = nil) + @win32registry = Chef::Win32::Registry.new + store = get_registry_user + path = "#{store}\\Software\\Progress\\Authentication" + if password.nil? + require "securerandom" unless defined?(SecureRandom) + size = 14 + password = SecureRandom.alphanumeric(size) + end + encrypted_blob = encrypt_pfx_pass_with_vector(password) + encrypted_password = encrypted_blob[0] + key = encrypted_blob[1] + vector = encrypted_blob[2] + values = [ + { name: "PfxPass", type: :string, data: encrypted_password }, + { name: "PfxKey", type: :string, data: key }, + { name: "PfxIV", type: :string, data: vector }, + ] + values.each do |i| + @win32registry.set_value(path, i) + end + password + end + + def self.retrieve_certificate_key(client_name) if ChefUtils.windows? + require "openssl" unless defined?(OpenSSL) password = get_cert_password return false unless password - if check_certstore_for_key(client_name) + if !!check_certstore_for_key(client_name) ps_blob = powershell_exec!(get_the_key_ps(client_name, password)).result file_path = ps_blob["PSPath"].split("::")[1] pkcs = OpenSSL::PKCS12.new(File.binread(file_path), password) @@ -252,10 +333,11 @@ class Chef end def self.get_the_key_ps(client_name, password) + store = get_cert_user powershell_code = <<~CODE Try { $my_pwd = ConvertTo-SecureString -String "#{password}" -Force -AsPlainText; - $cert = Get-ChildItem -path cert:\\LocalMachine\\My -Recurse | Where-Object { $_.Subject -match "chef-#{client_name}$" } -ErrorAction Stop; + $cert = Get-ChildItem -path cert:\\#{store}\\My -Recurse | Where-Object { $_.Subject -match "chef-#{client_name}$" } -ErrorAction Stop; $tempfile = [System.IO.Path]::GetTempPath() + "export_pfx.pfx"; Export-PfxCertificate -Cert $cert -Password $my_pwd -FilePath $tempfile; } @@ -266,8 +348,9 @@ class Chef end def self.delete_old_key_ps(client_name) + store = get_cert_user powershell_code = <<~CODE - Get-ChildItem -path cert:\\LocalMachine\\My -Recurse | Where-Object { $_.Subject -match "chef-#{client_name}$" } | Remove-Item -ErrorAction Stop; + Get-ChildItem -path cert:\\#{store}\\My -Recurse | Where-Object { $_.Subject -match "chef-#{client_name}$" } | Remove-Item -ErrorAction Stop; CODE end diff --git a/spec/integration/client/client_spec.rb b/spec/integration/client/client_spec.rb index 1bd84fa940..38d414701a 100644 --- a/spec/integration/client/client_spec.rb +++ b/spec/integration/client/client_spec.rb @@ -35,14 +35,14 @@ describe "chef-client" do @server = @api = nil end - def install_certificate_in_store(client_name) + def install_certificate_in_store(client_name, store_location) if ChefUtils.windows? powershell_exec! <<~EOH if (-not (($PSVersionTable.PSVersion.Major -ge 5) -and ($PSVersionTable.PSVersion.Build -ge 22000)) ) { - New-SelfSignedCertificate -CertStoreLocation Cert:\\LocalMachine\\My -DnsName "#{client_name}" + New-SelfSignedCertificate -CertStoreLocation Cert:\\#{store_location}\\My -DnsName "#{client_name}" } else { - New-SelfSignedCertificate -CertStoreLocation Cert:\\LocalMachine\\My -Subject "#{client_name}" -FriendlyName "#{client_name}" -KeyExportPolicy Exportable + New-SelfSignedCertificate -CertStoreLocation Cert:\\#{store_location}\\My -Subject "#{client_name}" -FriendlyName "#{client_name}" -KeyExportPolicy Exportable } EOH end @@ -50,14 +50,6 @@ describe "chef-client" do def create_registry_key ::Chef::HTTP::Authenticator.get_cert_password - # @win32registry = Chef::Win32::Registry.new - # path = "HKEY_LOCAL_MACHINE\\Software\\Progress\\Authentication" - # unless @win32registry.key_exists?(path) - # @win32registry.create_key(path, true) - # end - # password = SOME_CHARS.sample(1 + rand(SOME_CHARS.count)).join[0...14] - # values = { name: "PfxPass", type: :string, data: password } - # @win32registry.set_value(path, values) end def remove_certificate_from_store @@ -111,6 +103,9 @@ describe "chef-client" do tempfile.close @path = tempfile.path Chef::Config.validation_key = @path + if ChefUtils.windows? + create_registry_key + end file "config/client.rb", <<~EOM local_mode true @@ -201,17 +196,27 @@ describe "chef-client" do if ChefUtils.windows? context "and the private key is in the Windows CertStore" do - before do - install_certificate_in_store(client_name) + + it "should verify that the cert is loaded in the \\LocalMachine\\My store" do + Chef::Config[:auth_key_registry_type] = "machine" + install_certificate_in_store(client_name, "LocalMachine") create_registry_key + expect(Chef::HTTP::Authenticator.check_certstore_for_key(hostname)).to eq(true) end - after do + it "should verify that the export password for the pfx is loaded in the Registry" do + expect(verify_export_password_exists.result).to eq(true) + end + + it "should verify that a private key is returned to me" do + expect(Chef::HTTP::Authenticator.retrieve_certificate_key(client_name)).not_to be nil remove_certificate_from_store - remove_registry_key end - it "should verify that the cert is loaded in the LocalMachine\\My" do + it "should verify that the cert is loaded in the \\CurrentUser\\My store" do + Chef::Config[:auth_key_registry_type] = "user" + install_certificate_in_store(client_name, "CurrentUser") + create_registry_key expect(Chef::HTTP::Authenticator.check_certstore_for_key(hostname)).to eq(true) end @@ -221,6 +226,7 @@ describe "chef-client" do it "should verify that a private key is returned to me" do expect(Chef::HTTP::Authenticator.retrieve_certificate_key(client_name)).not_to be nil + remove_certificate_from_store end end end diff --git a/spec/unit/client_spec.rb b/spec/unit/client_spec.rb index 7f02eacb39..ae11fd9793 100644 --- a/spec/unit/client_spec.rb +++ b/spec/unit/client_spec.rb @@ -310,25 +310,49 @@ describe Chef::Client, :windows_only do end context "when the client intially boots the first time" do - it "verfies that a certificate was correctly created and exists in the Cert Store" do + it "verfies that a certificate was correctly created and exists in the LocalMachine Cert Store" do + Chef::Config[:node_name] = "test" new_pfx = my_client.generate_pfx_package(cert_name, end_date) my_client.import_pfx_to_store(new_pfx) expect(my_client.check_certstore_for_key(cert_name)).not_to be false + delete_certificate(cert_name) end it "correctly returns a new Publc Key" do new_pfx = my_client.generate_pfx_package(cert_name, end_date) cert_object = new_pfx.certificate.public_key.to_pem expect(cert_object.to_s).to match(/PUBLIC KEY/) + delete_certificate(cert_name) + end + + end + + context "when the client intially boots the first time and auth_key_registry_type is set to 'user' " do + it "verfies that a certificate was correctly created and exists in the CurrentUser Cert Store" do + Chef::Config[:node_name] = "test" + Chef::Config[:auth_key_registry_type] = "user" + new_pfx = my_client.generate_pfx_package(cert_name, end_date) + my_client.import_pfx_to_store(new_pfx) + expect(my_client.check_certstore_for_key(cert_name)).not_to be false + delete_certificate(cert_name) + end + + it "correctly returns a new Publc Key" do + Chef::Config[:auth_key_registry_type] = "user" + new_pfx = my_client.generate_pfx_package(cert_name, end_date) + cert_object = new_pfx.certificate.public_key.to_pem + expect(cert_object.to_s).to match(/PUBLIC KEY/) + delete_certificate(cert_name) end end def delete_certificate(cert_name) + Chef::Config[:auth_key_registry_type] == "user" ? store = "CurrentUser" : store = "LocalMachine" require "chef/mixin/powershell_exec" extend Chef::Mixin::PowershellExec powershell_code = <<~CODE - Get-ChildItem -path cert:\\LocalMachine\\My -Recurse -Force | Where-Object { $_.Subject -Match "#{cert_name}" } | Remove-item + Get-ChildItem -path cert:\\#{store}\\My -Recurse -Force | Where-Object { $_.Subject -Match "#{cert_name}" } | Remove-item CODE powershell_exec!(powershell_code) end diff --git a/spec/unit/http/authenticator_spec.rb b/spec/unit/http/authenticator_spec.rb index bbd952b436..a088a1f4f9 100644 --- a/spec/unit/http/authenticator_spec.rb +++ b/spec/unit/http/authenticator_spec.rb @@ -18,6 +18,9 @@ require "spec_helper" require "chef/http/authenticator" +require "chef/mixin/powershell_exec" + +require_relative "../../../lib/chef/win32/registry" describe Chef::HTTP::Authenticator, :windows_only do let(:class_instance) { Chef::HTTP::Authenticator.new(client_name: "test") } @@ -28,7 +31,7 @@ describe Chef::HTTP::Authenticator, :windows_only do let(:node_name) { "test" } let(:passwrd) { "some_insecure_password" } - before do + before(:each) do Chef::Config[:node_name] = node_name cert_name = "chef-#{node_name}" d = Time.now @@ -36,6 +39,7 @@ describe Chef::HTTP::Authenticator, :windows_only do end_date = end_date.utc.iso8601 my_client = Chef::Client.new + class_instance.get_cert_password pfx = my_client.generate_pfx_package(cert_name, end_date) my_client.import_pfx_to_store(pfx) end @@ -47,10 +51,21 @@ describe Chef::HTTP::Authenticator, :windows_only do delete_certificate(cert_name) end - context "when retrieving a certificate from the certificate store" do + context "when retrieving a certificate from the certificate store it" do + it "properly creates the password hive in the registry when it doesn't exist" do + delete_registry_hive + class_instance.get_cert_password + win32registry = Chef::Win32::Registry.new + expected_path = "HKEY_LOCAL_MACHINE\\Software\\Progress\\Authentication" + path_created = win32registry.key_exists?(expected_path) + expect(path_created).to be(true) + end + it "retrieves a certificate password from the registry when the hive does not already exist" do delete_registry_hive + password = class_instance.get_cert_password expect { class_instance.get_cert_password }.not_to raise_error + expect(password).not_to be(nil) end it "should return a password of at least 14 characters in length" do @@ -58,7 +73,27 @@ describe Chef::HTTP::Authenticator, :windows_only do expect(password.length).to eql(14) end - it "correctly retrieves a valid certificate in pem format from the certstore" do + it "will retrieve a password from a partial registry hive and upgrades it while using the old decryptor" do + delete_registry_hive + load_partial_registry_hive + password = class_instance.get_cert_password + expect(password).to eql(passwrd) + end + + it "verifies that the new password is now using a vector" do + win32registry = Chef::Win32::Registry.new + path = "HKEY_LOCAL_MACHINE\\Software\\Progress\\Authentication" + password_blob = win32registry.get_values(path) + if password_blob.nil? || password_blob.empty? + raise Chef::Exceptions::Win32RegKeyMissing + end + + raw_data = password_blob.map { |x| x[:data] } + vector = raw_data[2] + expect(vector).not_to be(nil) + end + + it "correctly retrieves a valid certificate in pem format from the LocalMachine certstore" do require "openssl" certificate = class_instance.retrieve_certificate_key(node_name) cert_object = OpenSSL::PKey::RSA.new(certificate) @@ -66,21 +101,39 @@ describe Chef::HTTP::Authenticator, :windows_only do end end - def delete_certificate(cert_name) + def load_partial_registry_hive + extend Chef::Mixin::PowershellExec + password = "some_insecure_password" powershell_code = <<~CODE - Get-ChildItem -path cert:\\LocalMachine\\My -Recurse -Force | Where-Object { $_.Subject -Match "#{cert_name}" } | Remove-item + $encrypted_string = ConvertTo-SecureString "#{password}" -AsPlainText -Force + $secure_string = ConvertFrom-SecureString $encrypted_string + return $secure_string CODE - powershell_exec!(powershell_code) + encrypted_pass = powershell_exec!(powershell_code).result + Chef::Config[:auth_key_registry_type] == "user" ? store = "HKEY_CURRENT_USER" : store = "HKEY_LOCAL_MACHINE" + hive_path = "#{store}\\Software\\Progress\\Authentication" + win32registry = Chef::Win32::Registry.new + unless win32registry.key_exists?(hive_path) + win32registry.create_key(hive_path, true) + end + values = { name: "PfxPass", type: :string, data: encrypted_pass } + win32registry.set_value(hive_path, values) end def delete_registry_hive - @win32registry = Chef::Win32::Registry.new - path = "HKEY_LOCAL_MACHINE\\Software\\Progress\\Authentication" - present = @win32registry.get_values(path) - unless present.nil? || present.empty? - @win32registry.delete_key(path, true) + win32registry = Chef::Win32::Registry.new + hive_path = "HKEY_LOCAL_MACHINE\\Software\\Progress" + if win32registry.key_exists?(hive_path) + win32registry.delete_key(hive_path, true) end end + + def delete_certificate(cert_name) + powershell_code = <<~CODE + Get-ChildItem -path cert:\\LocalMachine\\My -Recurse -Force | Where-Object { $_.Subject -Match "#{cert_name}" } | Remove-item + CODE + powershell_exec!(powershell_code) + end end describe Chef::HTTP::Authenticator do |