win32-certstore 0.2.2 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fdfde36cb468d1ce15e0d5fd317783347f07c96f0282519b56b86b424d89e92e
4
- data.tar.gz: a4ce61aafb784b12bf5e7ee28958221c4318500d78ec2ac23f3d700e7528d466
3
+ metadata.gz: a571cfa5aea1f77ed28c7711fe81982338d7af42a73f886cfbdfe6df3123fc91
4
+ data.tar.gz: cb36e5aba55afe58af9a7770f330f276ad7013fe86f7d51dd1d9d372b7a9a598
5
5
  SHA512:
6
- metadata.gz: 3f1ff03dc3ac803b951179683c48bdef81ea481f12c0a1838e9d236085e4451d2698a3f488da855bbc44bdbf037d6fdc753113309ef0838b2b61fab425cc8040
7
- data.tar.gz: 61ac2a5b3f1cbceb606822c07dfdfec2b5b76e1ba91820f4f7571c094f16c10ffeba292e9772abf3938eba537951426d8c8ef8f09ddcf5b1bc74cbf0367435ce
6
+ metadata.gz: 94138aaef753c7b9830e7374fc80d53ae531c85f14677f3fc72eb4234744ee7c21737b0ee8f41d530deeec0e433196ec1acacc1792468ed0849b995ee0fab754
7
+ data.tar.gz: 5710436ecefe40dc2883375d4317d333189ef0b7896b27dd3c31e0ccf1beab9d321edab6cae714b365492aec5420853a0ec8d53cb18dd006f61ecfa1e85c8a4a
@@ -53,6 +53,21 @@ module Win32
53
53
  cert_add(certstore_handler, certificate_obj)
54
54
  end
55
55
 
56
+ # Adds a PFX certificate to certificate store
57
+ #
58
+ # @note Unlike other certificates, PFX can be password protected and may contain a private key.
59
+ # Therefore we need a different approach to import them.
60
+ #
61
+ # @param path [String] Path of the certificate that should be imported
62
+ # @param password [String] Password of the certificate if it is protected
63
+ # @param key_properties [Integer] dwFlags used to specify properties of the pfx key, see certstore/store_base.rb cert_add_pfx function
64
+ #
65
+ # @return [Boolean]
66
+ #
67
+ def add_pfx(path, password, key_properties = 0)
68
+ cert_add_pfx(certstore_handler, path, password, key_properties)
69
+ end
70
+
56
71
  # Return `OpenSSL::X509` certificate object
57
72
  # @param request [thumbprint<string>] of certificate
58
73
  # @return [Object] of certificates in OpenSSL::X509 format
@@ -90,11 +105,7 @@ module Win32
90
105
 
91
106
  # To close and destroy pointer of open certificate store handler
92
107
  def close
93
- closed = CertCloseStore(@certstore_handler, CERT_CLOSE_STORE_FORCE_FLAG)
94
- unless closed
95
- last_error = FFI::LastError.error
96
- raise SystemCallError.new("Unable to close the Certificate Store.", last_error)
97
- end
108
+ close_cert_store
98
109
  remove_finalizer
99
110
  end
100
111
 
@@ -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
@@ -68,12 +68,17 @@ module Win32
68
68
  CERT_NAME_UPN_TYPE = 8
69
69
 
70
70
  # Retrieve Certificates flag
71
+ CERT_COMPARE_ANY = 0
71
72
  CERT_COMPARE_SHA1_HASH = 1
72
73
  CERT_INFO_SUBJECT_FLAG = 7
73
74
  CERT_COMPARE_NAME_STR_W = 8
74
75
  CERT_COMPARE_SHIFT = 16
75
76
  CERT_FIND_SHA1_HASH = CERT_COMPARE_SHA1_HASH << CERT_COMPARE_SHIFT
76
77
  CERT_FIND_SUBJECT_STR = CERT_COMPARE_NAME_STR_W << CERT_COMPARE_SHIFT | CERT_INFO_SUBJECT_FLAG
78
+ CERT_FIND_ANY = CERT_COMPARE_ANY << CERT_COMPARE_SHIFT
79
+
80
+ CERT_STORE_ADD_USE_EXISTING = 2
81
+ CERT_STORE_ADD_REPLACE_EXISTING = 3
77
82
 
78
83
  # List Certificates Flag
79
84
  CERT_NAME_ISSUER_FLAG = 0x1
@@ -108,17 +113,17 @@ module Win32
108
113
 
109
114
  class FILETIME < FFI::Struct
110
115
  layout :dwLowDateTime, DWORD,
111
- :dwHighDateTime, DWORD
116
+ :dwHighDateTime, DWORD
112
117
  end
113
118
 
114
119
  class CRYPT_INTEGER_BLOB < FFI::Struct
115
120
  layout :cbData, DWORD, # Count, in bytes, of data
116
- :pbData, :pointer # Pointer to data buffer
121
+ :pbData, :pointer # Pointer to data buffer
117
122
  end
118
123
 
119
124
  class CRYPT_NAME_BLOB < FFI::Struct
120
125
  layout :cbData, DWORD, # Count, in bytes, of data
121
- :pbData, :pointer # Pointer to data buffer
126
+ :pbData, :pointer # Pointer to data buffer
122
127
  def initialize(str = nil)
123
128
  super(nil)
124
129
  if str
@@ -129,7 +134,7 @@ module Win32
129
134
 
130
135
  class CRYPT_HASH_BLOB < FFI::Struct
131
136
  layout :cbData, DWORD, # Count, in bytes, of data
132
- :pbData, :pointer # Pointer to data buffer
137
+ :pbData, :pointer # Pointer to data buffer
133
138
 
134
139
  def initialize(str = nil)
135
140
  super(nil)
@@ -144,49 +149,62 @@ module Win32
144
149
  end
145
150
  end
146
151
 
152
+ class CRYPT_DATA_BLOB < FFI::Struct
153
+ layout :cbData, DWORD, # Count, in bytes, of data
154
+ :pbData, :pointer # Pointer to data buffer
155
+
156
+ def initialize(str = nil)
157
+ super(nil)
158
+ if str
159
+ self[:pbData] = FFI::MemoryPointer.from_string(str)
160
+ self[:cbData] = str.size
161
+ end
162
+ end
163
+ end
164
+
147
165
  class CERT_EXTENSION < FFI::Struct
148
166
  layout :pszObjId, LPTSTR,
149
- :fCritical, BOOL,
150
- :Value, CRYPT_INTEGER_BLOB
167
+ :fCritical, BOOL,
168
+ :Value, CRYPT_INTEGER_BLOB
151
169
  end
152
170
 
153
171
  class CRYPT_BIT_BLOB < FFI::Struct
154
172
  layout :cbData, DWORD,
155
- :pbData, BYTE,
156
- :cUnusedBits, DWORD
173
+ :pbData, BYTE,
174
+ :cUnusedBits, DWORD
157
175
  end
158
176
 
159
177
  class CRYPT_ALGORITHM_IDENTIFIER < FFI::Struct
160
178
  layout :pszObjId, LPSTR,
161
- :Parameters, CRYPT_INTEGER_BLOB
179
+ :Parameters, CRYPT_INTEGER_BLOB
162
180
  end
163
181
 
164
182
  class CERT_PUBLIC_KEY_INFO < FFI::Struct
165
183
  layout :Algorithm, CRYPT_ALGORITHM_IDENTIFIER,
166
- :PublicKey, CRYPT_BIT_BLOB
184
+ :PublicKey, CRYPT_BIT_BLOB
167
185
  end
168
186
 
169
187
  class CERT_INFO < FFI::Struct
170
188
  layout :dwVersion, DWORD,
171
- :SerialNumber, CRYPT_INTEGER_BLOB,
172
- :SignatureAlgorithm, CRYPT_ALGORITHM_IDENTIFIER,
173
- :Issuer, CRYPT_NAME_BLOB,
174
- :NotBefore, FILETIME,
175
- :NotAfter, FILETIME,
176
- :Subject, CRYPT_NAME_BLOB,
177
- :SubjectPublicKeyInfo, CERT_PUBLIC_KEY_INFO,
178
- :IssuerUniqueId, CRYPT_BIT_BLOB,
179
- :SubjectUniqueId, CRYPT_BIT_BLOB,
180
- :cExtension, DWORD,
181
- :rgExtension, CERT_EXTENSION
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
182
200
  end
183
201
 
184
202
  class CERT_CONTEXT < FFI::Struct
185
203
  layout :dwCertEncodingType, DWORD,
186
- :pbCertEncoded, BYTE,
187
- :cbCertEncoded, DWORD,
188
- :pCertInfo, CERT_INFO,
189
- :hCertStore, HCERTSTORE
204
+ :pbCertEncoded, BYTE,
205
+ :cbCertEncoded, DWORD,
206
+ :pCertInfo, CERT_INFO,
207
+ :hCertStore, HCERTSTORE
190
208
  end
191
209
 
192
210
  ###############################################################################
@@ -224,6 +242,10 @@ module Win32
224
242
  safe_attach_function :CertFindCertificateInStore, [HCERTSTORE, DWORD, DWORD, DWORD, LPVOID, PCCERT_CONTEXT], PCCERT_CONTEXT
225
243
 
226
244
  safe_attach_function :PFXExportCertStoreEx, [HCERTSTORE, CRYPT_INTEGER_BLOB, LPCTSTR, LPVOID, DWORD], BOOL
245
+
246
+ # Fetches store handler of a PFX certificate
247
+ attach_function :PFXImportCertStore, [CRYPT_DATA_BLOB, LPCTSTR, DWORD], HCERTSTORE
248
+ attach_function :CertAddCertificateContextToStore, [HCERTSTORE, PCCERT_CONTEXT, DWORD, PCCERT_CONTEXT], BOOL
227
249
  end
228
250
  end
229
251
  end
@@ -15,7 +15,7 @@
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
@@ -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
 
@@ -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
@@ -19,8 +19,8 @@ require_relative "mixin/crypto"
19
19
  require_relative "mixin/string"
20
20
  require_relative "mixin/shell_out"
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
@@ -49,6 +49,42 @@ module Win32
49
49
  end
50
50
  end
51
51
 
52
+ # Adds a PFX certificate to certificate store
53
+ #
54
+ # @see https://docs.microsoft.com/en-us/windows/desktop/api/wincrypt/nf-wincrypt-pfximportcertstore PFXImportCertStore function
55
+ # @see https://docs.microsoft.com/en-us/windows/desktop/api/wincrypt/nf-wincrypt-certaddcertificatecontexttostore CertAddCertificateContextToStore
56
+ #
57
+ # @param certstore_handler [FFI::Pointer] Handle of the store where certificate should be imported
58
+ # @param path [String] Path of the certificate that should be imported
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
61
+ #
62
+ # @return [Boolean]
63
+ #
64
+ # @raise [SystemCallError] when Crypt API would not be able to perform some action
65
+ #
66
+ def cert_add_pfx(certstore_handler, path, password = "", key_properties = 0)
67
+ cert_added = false
68
+ # Imports a PFX BLOB and returns the handle of a store
69
+ pfx_cert_store = PFXImportCertStore(CRYPT_DATA_BLOB.new(File.binread(path)), wstring(password), key_properties)
70
+ raise if pfx_cert_store.null?
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
79
+ cert_added
80
+ rescue
81
+ lookup_error("Add a PFX")
82
+ ensure
83
+ if pfx_cert_store && !pfx_cert_store.null?
84
+ close_cert_store(pfx_cert_store)
85
+ end
86
+ end
87
+
52
88
  # Get certificate from open certificate store and return certificate object
53
89
  # certificate_thumbprint => thumbprint is a hash. which could be sha1 or md5.
54
90
  def cert_get(certificate_thumbprint)
@@ -89,7 +125,7 @@ module Win32
89
125
  begin
90
126
  cert_args = cert_find_args(store_handler, thumbprint)
91
127
  pcert_context = CertFindCertificateInStore(*cert_args)
92
- if !pcert_context.null?
128
+ unless pcert_context.null?
93
129
  cert_delete_flag = CertDeleteCertificateFromStore(CertDuplicateCertificateContext(pcert_context)) || lookup_error
94
130
  end
95
131
  CertFreeCertificateContext(pcert_context)
@@ -115,6 +151,7 @@ module Win32
115
151
  # search_token => CN, RDN or any certificate attribute
116
152
  def cert_search(store_handler, search_token)
117
153
  raise ArgumentError, "Invalid search token" if !search_token || search_token.strip.empty?
154
+
118
155
  certificate_list = []
119
156
  begin
120
157
  while (pcert_context = CertEnumCertificatesInStore(store_handler, pcert_context)) && !pcert_context.null?
@@ -130,6 +167,12 @@ module Win32
130
167
  certificate_list
131
168
  end
132
169
 
170
+ # To close and destroy pointer of open certificate store handler
171
+ def close_cert_store(certstore_handler = @certstore_handler)
172
+ closed = CertCloseStore(certstore_handler, CERT_CLOSE_STORE_FORCE_FLAG)
173
+ lookup_error("close") unless closed
174
+ end
175
+
133
176
  private
134
177
 
135
178
  # Build arguments for CertAddEncodedCertificateToStore
@@ -142,6 +185,11 @@ module Win32
142
185
  [store_handler, ENCODING_TYPE, 0, CERT_FIND_SHA1_HASH, CRYPT_HASH_BLOB.new(thumbprint), nil]
143
186
  end
144
187
 
188
+ # Build arguments for CertAddCertificateContextToStore
189
+ def add_certcontxt_args(certstore_handler, cert_context)
190
+ [certstore_handler, cert_context, CERT_STORE_ADD_REPLACE_EXISTING, nil]
191
+ end
192
+
145
193
  # Match certificate CN exist in cert_rdn
146
194
  def is_cn_match?(cert_rdn, certificate_name)
147
195
  cert_rdn.read_wstring.match(/(^|\W)#{certificate_name}($|\W)/i)
@@ -172,6 +220,7 @@ module Win32
172
220
  # Verify OpenSSL::X509::Certificate object
173
221
  def verify_certificate(cert_pem)
174
222
  return "Certificate not found" if cert_pem.empty?
223
+
175
224
  valid_duration?(build_openssl_obj(cert_pem))
176
225
  end
177
226
 
@@ -1,6 +1,6 @@
1
1
  module Win32
2
2
  class Certstore
3
- VERSION = "0.2.2".freeze
3
+ VERSION = "0.4.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.2
4
+ version: 0.4.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-01-16 00:00:00.000000000 Z
11
+ date: 2020-08-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -104,8 +104,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
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.0.3
109
108
  signing_key:
110
109
  specification_version: 4
111
110
  summary: Ruby library for accessing the certificate store on Windows.