win32-certstore 0.2.4 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7b07544eeb10d6d5d218064ac922283b7654a4e6a683999f645792ff27fa45d5
4
- data.tar.gz: c7be876bba7975484a85507e241d9d8f6c590194ea32290edc5189540e647f69
3
+ metadata.gz: 63c8c4f2aaa89a78a8123d98079710df407a3930b0f4b5a6ef3ff2232c11ba05
4
+ data.tar.gz: 8641dfff337fe7b702783becfcfbfbba6b2a66af189eeb511897c2f0cb5e7d6c
5
5
  SHA512:
6
- metadata.gz: 772ca2195c9e4aa5b522d2e1196730cfc3cb777a05779c99d17a770eae954280e5a555986ee8718fbf785a94d9490b55b419515548c0740ed7f08f20839cf43b
7
- data.tar.gz: 167436e45e6ae7b64cd4de5ce95eced68d7b16ed759585b3bd5a682a6f63b6c8b26c31c13c59e448006a6bb298ce89986bb33d987efcbbaee3959f6542ff733f
6
+ metadata.gz: 4f255e439feee57642565bd9fca87f19140b5f95c38eed0a1e8621326749dd5274c0b72c8211bddfbec265820bff4b8d2ad9460ebbf5c30fa1d6607a7e81f204
7
+ data.tar.gz: 4efd363fb264fc8501f0f9e105e79f7537319838aafc0cb751387a73360386186d326dedd51522907aa131fe74c1b3ba07bd7b3f6f16a1283f5f59203d96a6a7
@@ -31,18 +31,21 @@ module Win32
31
31
 
32
32
  attr_accessor :store_name
33
33
 
34
- def initialize(store_name)
34
+ # Initializes a new instance of a certificate store.
35
+ # takes 2 parameters - the store name (My, Root, etc) and the location (CurrentUser or LocalMachine), it defaults to LocalMachine for backwards compatibility
36
+ def initialize(store_name, store_location: CERT_SYSTEM_STORE_LOCAL_MACHINE)
35
37
  @store_name = store_name
36
- @certstore_handler = open(store_name)
38
+ @store_location = store_location
39
+ @certstore_handler = open(store_name, store_location: store_location)
37
40
  end
38
41
 
39
42
  # To open given certificate store
40
- def self.open(store_name)
43
+ def self.open(store_name, store_location: CERT_SYSTEM_STORE_LOCAL_MACHINE)
41
44
  validate_store(store_name)
42
45
  if block_given?
43
- yield new(store_name)
46
+ yield new(store_name, store_location: store_location)
44
47
  else
45
- new(store_name)
48
+ new(store_name, store_location: store_location)
46
49
  end
47
50
  end
48
51
 
@@ -60,11 +63,12 @@ module Win32
60
63
  #
61
64
  # @param path [String] Path of the certificate that should be imported
62
65
  # @param password [String] Password of the certificate if it is protected
66
+ # @param key_properties [Integer] dwFlags used to specify properties of the pfx key, see certstore/store_base.rb cert_add_pfx function
63
67
  #
64
68
  # @return [Boolean]
65
69
  #
66
- def add_pfx(path, password)
67
- cert_add_pfx(certstore_handler, path, password)
70
+ def add_pfx(path, password, key_properties = 0)
71
+ cert_add_pfx(certstore_handler, path, password, key_properties)
68
72
  end
69
73
 
70
74
  # Return `OpenSSL::X509` certificate object
@@ -74,6 +78,18 @@ module Win32
74
78
  cert_get(certificate_thumbprint)
75
79
  end
76
80
 
81
+ # Returns a filepath to a PKCS12 container. The filepath is in a temporary folder so normal housekeeping by the OS should clear it.
82
+ # However, you should delete it yourself anyway.
83
+ # @param certificate_thumbprint [String] Is the thumbprint of the pfx blob you want to capture
84
+ # @param store_location: [String] A location in the Cert store where the pfx is located, typically 'LocalMachine'
85
+ # @param export_password: [String] The password to export with. P12 objects are an encrypted container that have a private key in \
86
+ # them and a password is required to export them.
87
+ # @param output_path: [String] The path where the you want P12 exported to.
88
+ # @return [Object] of certificate set in PKSC12 format at the path specified above
89
+ def get_pfx(certificate_thumbprint, store_location: @store_location, export_password:, output_path: "")
90
+ get_cert_pfx(certificate_thumbprint, store_location: store_location, export_password: export_password, output_path: output_path)
91
+ end
92
+
77
93
  # Returns all the certificates in a store
78
94
  # @param [nil]
79
95
  # @return [Array] array of certificates list
@@ -117,8 +133,9 @@ module Win32
117
133
  attr_reader :certstore_handler
118
134
 
119
135
  # To open certstore and return open certificate store pointer
120
- def open(store_name)
121
- certstore_handler = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, nil, CERT_SYSTEM_STORE_LOCAL_MACHINE, wstring(store_name))
136
+
137
+ def open(store_name, store_location: CERT_SYSTEM_STORE_LOCAL_MACHINE)
138
+ certstore_handler = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, nil, store_location, wstring(store_name))
122
139
  unless certstore_handler
123
140
  last_error = FFI::LastError.error
124
141
  raise SystemCallError.new("Unable to open the Certificate Store `#{store_name}`.", last_error)
@@ -14,7 +14,7 @@
14
14
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
15
  # See the License for the specific language governing permissions and
16
16
  # limitations under the License.
17
- require "openssl"
17
+ require "openssl" unless defined?(OpenSSL)
18
18
 
19
19
  module Win32
20
20
  class Certstore
@@ -16,7 +16,7 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
- require "ffi"
19
+ require "ffi" unless defined?(FFI)
20
20
 
21
21
  module Win32
22
22
  class Certstore
@@ -88,6 +88,9 @@ module Win32
88
88
 
89
89
  CERT_STORE_PROV_SYSTEM = 10
90
90
  CERT_SYSTEM_STORE_LOCAL_MACHINE = 0x00020000
91
+ CERT_SYSTEM_STORE_CURRENT_USER = 0x00010000
92
+ CERT_SYSTEM_STORE_SERVICES = 0x00050000
93
+ CERT_SYSTEM_STORE_USERS = 0x00060000
91
94
 
92
95
  # Define ffi pointer
93
96
  HCERTSTORE = FFI::TypeDefs[:pointer]
@@ -113,17 +116,17 @@ module Win32
113
116
 
114
117
  class FILETIME < FFI::Struct
115
118
  layout :dwLowDateTime, DWORD,
116
- :dwHighDateTime, DWORD
119
+ :dwHighDateTime, DWORD
117
120
  end
118
121
 
119
122
  class CRYPT_INTEGER_BLOB < FFI::Struct
120
123
  layout :cbData, DWORD, # Count, in bytes, of data
121
- :pbData, :pointer # Pointer to data buffer
124
+ :pbData, :pointer # Pointer to data buffer
122
125
  end
123
126
 
124
127
  class CRYPT_NAME_BLOB < FFI::Struct
125
128
  layout :cbData, DWORD, # Count, in bytes, of data
126
- :pbData, :pointer # Pointer to data buffer
129
+ :pbData, :pointer # Pointer to data buffer
127
130
  def initialize(str = nil)
128
131
  super(nil)
129
132
  if str
@@ -134,7 +137,7 @@ module Win32
134
137
 
135
138
  class CRYPT_HASH_BLOB < FFI::Struct
136
139
  layout :cbData, DWORD, # Count, in bytes, of data
137
- :pbData, :pointer # Pointer to data buffer
140
+ :pbData, :pointer # Pointer to data buffer
138
141
 
139
142
  def initialize(str = nil)
140
143
  super(nil)
@@ -151,7 +154,7 @@ module Win32
151
154
 
152
155
  class CRYPT_DATA_BLOB < FFI::Struct
153
156
  layout :cbData, DWORD, # Count, in bytes, of data
154
- :pbData, :pointer # Pointer to data buffer
157
+ :pbData, :pointer # Pointer to data buffer
155
158
 
156
159
  def initialize(str = nil)
157
160
  super(nil)
@@ -164,47 +167,47 @@ module Win32
164
167
 
165
168
  class CERT_EXTENSION < FFI::Struct
166
169
  layout :pszObjId, LPTSTR,
167
- :fCritical, BOOL,
168
- :Value, CRYPT_INTEGER_BLOB
170
+ :fCritical, BOOL,
171
+ :Value, CRYPT_INTEGER_BLOB
169
172
  end
170
173
 
171
174
  class CRYPT_BIT_BLOB < FFI::Struct
172
175
  layout :cbData, DWORD,
173
- :pbData, BYTE,
174
- :cUnusedBits, DWORD
176
+ :pbData, BYTE,
177
+ :cUnusedBits, DWORD
175
178
  end
176
179
 
177
180
  class CRYPT_ALGORITHM_IDENTIFIER < FFI::Struct
178
181
  layout :pszObjId, LPSTR,
179
- :Parameters, CRYPT_INTEGER_BLOB
182
+ :Parameters, CRYPT_INTEGER_BLOB
180
183
  end
181
184
 
182
185
  class CERT_PUBLIC_KEY_INFO < FFI::Struct
183
186
  layout :Algorithm, CRYPT_ALGORITHM_IDENTIFIER,
184
- :PublicKey, CRYPT_BIT_BLOB
187
+ :PublicKey, CRYPT_BIT_BLOB
185
188
  end
186
189
 
187
190
  class CERT_INFO < FFI::Struct
188
191
  layout :dwVersion, DWORD,
189
- :SerialNumber, CRYPT_INTEGER_BLOB,
190
- :SignatureAlgorithm, CRYPT_ALGORITHM_IDENTIFIER,
191
- :Issuer, CRYPT_NAME_BLOB,
192
- :NotBefore, FILETIME,
193
- :NotAfter, FILETIME,
194
- :Subject, CRYPT_NAME_BLOB,
195
- :SubjectPublicKeyInfo, CERT_PUBLIC_KEY_INFO,
196
- :IssuerUniqueId, CRYPT_BIT_BLOB,
197
- :SubjectUniqueId, CRYPT_BIT_BLOB,
198
- :cExtension, DWORD,
199
- :rgExtension, CERT_EXTENSION
192
+ :SerialNumber, CRYPT_INTEGER_BLOB,
193
+ :SignatureAlgorithm, CRYPT_ALGORITHM_IDENTIFIER,
194
+ :Issuer, CRYPT_NAME_BLOB,
195
+ :NotBefore, FILETIME,
196
+ :NotAfter, FILETIME,
197
+ :Subject, CRYPT_NAME_BLOB,
198
+ :SubjectPublicKeyInfo, CERT_PUBLIC_KEY_INFO,
199
+ :IssuerUniqueId, CRYPT_BIT_BLOB,
200
+ :SubjectUniqueId, CRYPT_BIT_BLOB,
201
+ :cExtension, DWORD,
202
+ :rgExtension, CERT_EXTENSION
200
203
  end
201
204
 
202
205
  class CERT_CONTEXT < FFI::Struct
203
206
  layout :dwCertEncodingType, DWORD,
204
- :pbCertEncoded, BYTE,
205
- :cbCertEncoded, DWORD,
206
- :pCertInfo, CERT_INFO,
207
- :hCertStore, HCERTSTORE
207
+ :pbCertEncoded, BYTE,
208
+ :cbCertEncoded, DWORD,
209
+ :pCertInfo, CERT_INFO,
210
+ :hCertStore, HCERTSTORE
208
211
  end
209
212
 
210
213
  ###############################################################################
@@ -21,21 +21,52 @@ module Win32
21
21
  class Certstore
22
22
  module Mixin
23
23
  module Helper
24
-
25
- # PSCommand to search certificate from thumbprint and convert in pem
26
- def cert_ps_cmd(thumbprint, store_name)
24
+ # PSCommand to search certificate from thumbprint and either turn it into a pem or return a path to a pfx object
25
+ def cert_ps_cmd(thumbprint, store_location: "LocalMachine", export_password: "1234", output_path: "")
27
26
  <<-EOH
28
- $content = $null
29
- $cert = Get-ChildItem Cert:\\LocalMachine\\'#{store_name}' -Recurse | Where { $_.Thumbprint -eq '#{thumbprint}' }
30
- if($cert -ne $null)
31
- {
32
- $content = @(
33
- '-----BEGIN CERTIFICATE-----'
34
- [System.Convert]::ToBase64String($cert.RawData, 'InsertLineBreaks')
35
- '-----END CERTIFICATE-----'
36
- )
27
+ $cert = Get-ChildItem Cert:\'#{store_location}' -Recurse | Where { $_.Thumbprint -eq '#{thumbprint}' }
28
+
29
+ # The function and the code below test to see if a) the cert has a private key and b) it has a
30
+ # Enhanced Usage of Client Auth. Those 2 attributes would mean this is a pfx-able object
31
+ function test_cert_values{
32
+ $usagelist = ($cert).EnhancedKeyUsageList
33
+ foreach($use in $usagelist){
34
+ if($use.FriendlyName -like "Client Authentication" ){
35
+ return $true
36
+ }
37
+ }
38
+ return $false
39
+ }
40
+
41
+ $result = test_cert_values
42
+
43
+ $output_path = "#{output_path}"
44
+ if([string]::IsNullOrEmpty($output_path)){
45
+ $temproot = [System.IO.Path]::GetTempPath()
46
+ }
47
+ else{
48
+ $temproot = $output_path
49
+ }
50
+
51
+ if((($cert).HasPrivateKey) -and ($result -eq $true)){
52
+ $file_name = '#{thumbprint}'
53
+ $file_path = $(Join-Path -Path $temproot -ChildPath "$file_name.pfx")
54
+ $mypwd = ConvertTo-SecureString -String '#{export_password}' -Force -AsPlainText
55
+ $cert | Export-PfxCertificate -FilePath $file_path -Password $mypwd | Out-Null
56
+ $file_path
57
+ }
58
+ else {
59
+ $content = $null
60
+ if($cert -ne $null)
61
+ {
62
+ $content = @(
63
+ '-----BEGIN CERTIFICATE-----'
64
+ [System.Convert]::ToBase64String($cert.RawData, 'InsertLineBreaks')
65
+ '-----END CERTIFICATE-----'
66
+ )
67
+ }
68
+ $content
37
69
  }
38
- $content
39
70
  EOH
40
71
  end
41
72
 
@@ -43,7 +74,6 @@ module Win32
43
74
  def valid_duration?(cert_obj)
44
75
  cert_obj.not_before < Time.now.utc && cert_obj.not_after > Time.now.utc
45
76
  end
46
-
47
77
  end
48
78
  end
49
79
  end
@@ -15,12 +15,12 @@
15
15
  # See the License for the specific language governing permissions and
16
16
  # limitations under the License.
17
17
 
18
- require "mixlib/shellout"
18
+ require "mixlib/shellout" unless defined?(Mixlib::ShellOut)
19
19
 
20
20
  module Win32
21
21
  class Certstore
22
22
  module Mixin
23
- module ShellOut
23
+ module ShellExec
24
24
  def shell_out_command(*command_args)
25
25
  cmd = Mixlib::ShellOut.new(*command_args)
26
26
  cmd.live_stream
@@ -28,6 +28,7 @@ module Win32
28
28
  if cmd.error!
29
29
  raise Mixlib::ShellOut::ShellCommandFailed, cmd.error!
30
30
  end
31
+
31
32
  cmd
32
33
  end
33
34
 
@@ -38,7 +39,7 @@ module Win32
38
39
  # @param script [String] script to run
39
40
  # @param options [Hash] options hash
40
41
  # @return [Mixlib::Shellout] mixlib-shellout object
41
- def powershell_out(*command_args)
42
+ def powershell_exec(*command_args)
42
43
  script = command_args.first
43
44
  options = command_args.last.is_a?(Hash) ? command_args.last : nil
44
45
 
@@ -51,8 +52,8 @@ module Win32
51
52
  # @param script [String] script to run
52
53
  # @param options [Hash] options hash
53
54
  # @return [Mixlib::Shellout] mixlib-shellout object
54
- def powershell_out!(*command_args)
55
- cmd = powershell_out(*command_args)
55
+ def powershell_exec!(*command_args)
56
+ cmd = powershell_exec(*command_args)
56
57
  cmd.error!
57
58
  cmd
58
59
  end
@@ -96,7 +97,7 @@ module Win32
96
97
  "-InputFormat None",
97
98
  ]
98
99
 
99
- "powershell.exe #{flags.join(' ')} -Command \"#{script.gsub('"', '\"')}\""
100
+ "powershell.exe #{flags.join(" ")} -Command \"#{script.gsub('"', '\"')}\""
100
101
  end
101
102
  end
102
103
  end
@@ -17,10 +17,10 @@
17
17
 
18
18
  require_relative "mixin/crypto"
19
19
  require_relative "mixin/string"
20
- require_relative "mixin/shell_out"
20
+ require_relative "mixin/shell_exec"
21
21
  require_relative "mixin/unicode"
22
- require "openssl"
23
- require "json"
22
+ require "openssl" unless defined?(OpenSSL)
23
+ require "json" unless defined?(JSON)
24
24
 
25
25
  module Win32
26
26
  class Certstore
@@ -28,7 +28,7 @@ module Win32
28
28
  include Win32::Certstore::Mixin::Crypto
29
29
  include Win32::Certstore::Mixin::Assertions
30
30
  include Win32::Certstore::Mixin::String
31
- include Win32::Certstore::Mixin::ShellOut
31
+ include Win32::Certstore::Mixin::ShellExec
32
32
  include Win32::Certstore::Mixin::Unicode
33
33
  include Win32::Certstore::Mixin::Helper
34
34
 
@@ -57,22 +57,25 @@ module Win32
57
57
  # @param certstore_handler [FFI::Pointer] Handle of the store where certificate should be imported
58
58
  # @param path [String] Path of the certificate that should be imported
59
59
  # @param password [String] Password of the certificate
60
+ # @param key_properties [Integer] dwFlags used to specify properties of the pfx key, see link above
60
61
  #
61
62
  # @return [Boolean]
62
63
  #
63
64
  # @raise [SystemCallError] when Crypt API would not be able to perform some action
64
65
  #
65
- def cert_add_pfx(certstore_handler, path, password = "")
66
+ def cert_add_pfx(certstore_handler, path, password = "", key_properties = 0)
67
+ cert_added = false
66
68
  # Imports a PFX BLOB and returns the handle of a store
67
- pfx_cert_store = PFXImportCertStore(CRYPT_DATA_BLOB.new(File.binread(path)), wstring(password), 0)
69
+ pfx_cert_store = PFXImportCertStore(CRYPT_DATA_BLOB.new(File.binread(path)), wstring(password), key_properties)
68
70
  raise if pfx_cert_store.null?
69
- # Find the certificate context in certificate store
70
- cert_context = CertFindCertificateInStore(pfx_cert_store, ENCODING_TYPE, 0, CERT_FIND_ANY, nil, nil)
71
- raise if cert_context.null?
72
- # Add certificate context to the certificate store
73
- args = add_certcontxt_args(certstore_handler, cert_context)
74
- cert_added = CertAddCertificateContextToStore(*args)
75
- raise unless cert_added
71
+
72
+ # Find all the certificate contexts in certificate store and add them ino the store
73
+ while (cert_context = CertEnumCertificatesInStore(pfx_cert_store, cert_context)) && (not cert_context.null?)
74
+ # Add certificate context to the certificate store
75
+ args = add_certcontxt_args(certstore_handler, cert_context)
76
+ cert_added = CertAddCertificateContextToStore(*args)
77
+ raise unless cert_added
78
+ end
76
79
  cert_added
77
80
  rescue
78
81
  lookup_error("Add a PFX")
@@ -122,7 +125,7 @@ module Win32
122
125
  begin
123
126
  cert_args = cert_find_args(store_handler, thumbprint)
124
127
  pcert_context = CertFindCertificateInStore(*cert_args)
125
- if !pcert_context.null?
128
+ unless pcert_context.null?
126
129
  cert_delete_flag = CertDeleteCertificateFromStore(CertDuplicateCertificateContext(pcert_context)) || lookup_error
127
130
  end
128
131
  CertFreeCertificateContext(pcert_context)
@@ -148,6 +151,7 @@ module Win32
148
151
  # search_token => CN, RDN or any certificate attribute
149
152
  def cert_search(store_handler, search_token)
150
153
  raise ArgumentError, "Invalid search token" if !search_token || search_token.strip.empty?
154
+
151
155
  certificate_list = []
152
156
  begin
153
157
  while (pcert_context = CertEnumCertificatesInStore(store_handler, pcert_context)) && !pcert_context.null?
@@ -216,6 +220,7 @@ module Win32
216
220
  # Verify OpenSSL::X509::Certificate object
217
221
  def verify_certificate(cert_pem)
218
222
  return "Certificate not found" if cert_pem.empty?
223
+
219
224
  valid_duration?(build_openssl_obj(cert_pem))
220
225
  end
221
226
 
@@ -226,7 +231,23 @@ module Win32
226
231
 
227
232
  # Get certificate pem
228
233
  def get_cert_pem(thumbprint)
229
- get_data = powershell_out!(cert_ps_cmd(thumbprint, store_name))
234
+ converted_store = if @store_location == CERT_SYSTEM_STORE_LOCAL_MACHINE
235
+ "LocalMachine"
236
+ else
237
+ "CurrentUser"
238
+ end
239
+ get_data = powershell_exec!(cert_ps_cmd(thumbprint, store_location: converted_store))
240
+ get_data.stdout
241
+ end
242
+
243
+ # Get PFX object
244
+ def get_cert_pfx(thumbprint, store_location: CERT_SYSTEM_STORE_LOCAL_MACHINE, export_password:, output_path: )
245
+ converted_store = if store_location == CERT_SYSTEM_STORE_LOCAL_MACHINE
246
+ "LocalMachine"
247
+ else
248
+ "CurrentUser"
249
+ end
250
+ get_data = powershell_exec!(cert_ps_cmd(thumbprint, export_password: export_password, store_location: converted_store, output_path: output_path))
230
251
  get_data.stdout
231
252
  end
232
253
 
@@ -1,6 +1,6 @@
1
1
  module Win32
2
2
  class Certstore
3
- VERSION = "0.2.4".freeze
3
+ VERSION = "0.6.1".freeze
4
4
  MAJOR, MINOR, TINY = VERSION.split(".")
5
5
  end
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: win32-certstore
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chef Software
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-02-04 00:00:00.000000000 Z
11
+ date: 2021-03-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -79,7 +79,7 @@ files:
79
79
  - lib/win32/certstore/mixin/assertions.rb
80
80
  - lib/win32/certstore/mixin/crypto.rb
81
81
  - lib/win32/certstore/mixin/helper.rb
82
- - lib/win32/certstore/mixin/shell_out.rb
82
+ - lib/win32/certstore/mixin/shell_exec.rb
83
83
  - lib/win32/certstore/mixin/string.rb
84
84
  - lib/win32/certstore/mixin/unicode.rb
85
85
  - lib/win32/certstore/store_base.rb
@@ -97,16 +97,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
97
97
  requirements:
98
98
  - - ">="
99
99
  - !ruby/object:Gem::Version
100
- version: '2.3'
100
+ version: '2.5'
101
101
  required_rubygems_version: !ruby/object:Gem::Requirement
102
102
  requirements:
103
103
  - - ">="
104
104
  - !ruby/object:Gem::Version
105
105
  version: '0'
106
106
  requirements: []
107
- rubyforge_project:
108
- rubygems_version: 2.7.6
107
+ rubygems_version: 3.1.4
109
108
  signing_key:
110
109
  specification_version: 4
111
- summary: Ruby library for accessing the certificate store on Windows.
110
+ summary: Ruby library for accessing the certificate stores on Windows.
112
111
  test_files: []