win32-certstore 0.1.0 → 0.1.3

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.
@@ -1,50 +1,50 @@
1
- #
2
- # Author:: John Keiser (<jkeiser@chef.io>)
3
- # Author:: Seth Chisamore (<schisamo@chef.io>)
4
- # Copyright:: Copyright (c) 2017 Chef Software, Inc.
5
- # License:: Apache License, Version 2.0
6
- #
7
- # Licensed under the Apache License, Version 2.0 (the "License");
8
- # you may not use this file except in compliance with the License.
9
- # You may obtain a copy of the License at
10
- #
11
- # http://www.apache.org/licenses/LICENSE-2.0
12
- #
13
- # Unless required by applicable law or agreed to in writing, software
14
- # distributed under the License is distributed on an "AS IS" BASIS,
15
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
- # See the License for the specific language governing permissions and
17
- # limitations under the License.
18
-
19
- require_relative "string"
20
-
21
- module Win32::Certstore::Mixin::Unicode
22
- end
23
-
24
- module FFI
25
- class Pointer
26
- include Win32::Certstore::Mixin::String
27
- def read_wstring(num_wchars = nil)
28
- if num_wchars.nil?
29
- # Find the length of the string
30
- length = 0
31
- last_char = nil
32
- while last_char != "\000\000"
33
- length += 1
34
- last_char = get_bytes(0, length * 2)[-2..-1]
35
- end
36
-
37
- num_wchars = length
38
- end
39
- wide_to_utf8(get_bytes(0, num_wchars * 2))
40
- end
41
- end
42
- end
43
-
44
- class String
45
- include Win32::Certstore::Mixin::String
46
-
47
- def to_wstring
48
- utf8_to_wide(self)
49
- end
50
- end
1
+ #
2
+ # Author:: John Keiser (<jkeiser@chef.io>)
3
+ # Author:: Seth Chisamore (<schisamo@chef.io>)
4
+ # Copyright:: Copyright (c) 2017 Chef Software, Inc.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require_relative "string"
20
+
21
+ module Win32::Certstore::Mixin::Unicode
22
+ end
23
+
24
+ module FFI
25
+ class Pointer
26
+ include Win32::Certstore::Mixin::String
27
+ def read_wstring(num_wchars = nil)
28
+ if num_wchars.nil?
29
+ # Find the length of the string
30
+ length = 0
31
+ last_char = nil
32
+ while last_char != "\000\000"
33
+ length += 1
34
+ last_char = get_bytes(0, length * 2)[-2..-1]
35
+ end
36
+
37
+ num_wchars = length
38
+ end
39
+ wide_to_utf8(get_bytes(0, num_wchars * 2))
40
+ end
41
+ end
42
+ end
43
+
44
+ class String
45
+ include Win32::Certstore::Mixin::String
46
+
47
+ def to_wstring
48
+ utf8_to_wide(self)
49
+ end
50
+ end
@@ -1,214 +1,215 @@
1
- #
2
- # Author:: Piyush Awasthi (<piyush.awasthi@msystechnologies.com>)
3
- # Copyright:: Copyright (c) 2017 Chef Software, Inc.
4
- # License:: Apache License, Version 2.0
5
- #
6
- # Licensed under the Apache License, Version 2.0 (the "License");
7
- # you may not use this file except in compliance with the License.
8
- # You may obtain a copy of the License at
9
- #
10
- # http://www.apache.org/licenses/LICENSE-2.0
11
- #
12
- # Unless required by applicable law or agreed to in writing, software
13
- # distributed under the License is distributed on an "AS IS" BASIS,
14
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- # See the License for the specific language governing permissions and
16
- # limitations under the License.
17
-
18
- require_relative "mixin/crypto"
19
- require_relative "mixin/string"
20
- require_relative "mixin/shell_out"
21
- require_relative "mixin/unicode"
22
- require "openssl"
23
- require "json"
24
-
25
- module Win32
26
- class Certstore
27
- module StoreBase
28
- include Win32::Certstore::Mixin::Crypto
29
- include Win32::Certstore::Mixin::Assertions
30
- include Win32::Certstore::Mixin::String
31
- include Win32::Certstore::Mixin::ShellOut
32
- include Win32::Certstore::Mixin::Unicode
33
- include Win32::Certstore::Mixin::Helper
34
-
35
- # Adding new certification in open certificate and return boolean
36
- # store_handler => Open certificate store handler
37
- # certificate_obj => certificate object must be in OpenSSL::X509
38
- def cert_add(store_handler, certificate_obj)
39
- validate_certificate_obj(certificate_obj)
40
- begin
41
- cert_args = cert_add_args(store_handler, certificate_obj)
42
- if CertAddEncodedCertificateToStore(*cert_args)
43
- true
44
- else
45
- lookup_error
46
- end
47
- rescue Exception => e
48
- lookup_error("add")
49
- end
50
- end
51
-
52
- # Get certificate from open certificate store and return certificate object
53
- # store_handler => Open certificate store handler
54
- # certificate_thumbprint => thumbprint is a hash. which could be sha1 or md5.
55
- def cert_get(certificate_thumbprint)
56
- validate_thumbprint(certificate_thumbprint)
57
- thumbprint = update_thumbprint(certificate_thumbprint)
58
- cert_pem = get_cert_pem(thumbprint)
59
- cert_pem = format_pem(cert_pem)
60
- unless cert_pem.empty?
61
- build_openssl_obj(cert_pem)
62
- end
63
- end
64
-
65
- # Listing certificate of open certstore and return list in json
66
- def cert_list(store_handler)
67
- cert_name = memory_ptr
68
- cert_list = []
69
- begin
70
- while (pcert_context = CertEnumCertificatesInStore(store_handler, pcert_context)) && (not pcert_context.null?) do
71
- cert_args = cert_get_name_args(pcert_context, cert_name, CERT_NAME_FRIENDLY_DISPLAY_TYPE)
72
- if CertGetNameStringW(*cert_args)
73
- cert_list << cert_name.read_wstring
74
- end
75
- end
76
- CertFreeCertificateContext(pcert_context)
77
- rescue Exception => e
78
- lookup_error("list")
79
- end
80
- cert_list.to_json
81
- end
82
-
83
- # Deleting certificate from open certificate store and return boolean
84
- # store_handler => Open certificate store handler
85
- # certificate_thumbprint => thumbprint is a hash. which could be sha1 or md5.
86
- def cert_delete(store_handler, certificate_thumbprint)
87
- validate_thumbprint(certificate_thumbprint)
88
- cert_name = memory_ptr
89
- thumbprint = update_thumbprint(certificate_thumbprint)
90
- cert_pem = format_pem(get_cert_pem(thumbprint))
91
- cert_rdn = get_rdn(build_openssl_obj(cert_pem))
92
- cert_delete_flag = false
93
- begin
94
- cert_args = cert_find_args(store_handler, cert_rdn)
95
- if (pcert_context = CertFindCertificateInStore(*cert_args) and !pcert_context.null?)
96
- cert_delete_flag = CertDeleteCertificateFromStore(CertDuplicateCertificateContext(pcert_context)) || lookup_error
97
- end
98
- CertFreeCertificateContext(pcert_context)
99
- rescue Exception => e
100
- lookup_error("delete")
101
- end
102
- cert_delete_flag
103
- end
104
-
105
- # Verify certificate from open certificate store and return boolean or exceptions
106
- # store_handler => Open certificate store handler
107
- # certificate_thumbprint => thumbprint is a hash. which could be sha1 or md5.
108
- def cert_validate(certificate_thumbprint)
109
- validate_thumbprint(certificate_thumbprint)
110
- thumbprint = update_thumbprint(certificate_thumbprint)
111
- cert_pem = get_cert_pem(thumbprint)
112
- cert_pem = format_pem(cert_pem)
113
- verify_certificate(cert_pem)
114
- end
115
-
116
- # Search certificate from open certificate store and return list
117
- # store_handler => Open certificate store handler
118
- # search_token => CN, RDN or any certificate attribute
119
- def cert_search(store_handler, search_token)
120
- raise ArgumentError, "Invalid search token" if !search_token || search_token.strip.empty?
121
- cert_rdn = memory_ptr
122
- certificate_list =[]
123
- counter = 0
124
- begin
125
- while (pcert_context = CertEnumCertificatesInStore(store_handler, pcert_context) and !pcert_context.null?)
126
- cert_property = get_cert_property(pcert_context)
127
- if cert_property.include?(search_token)
128
- certificate_list << [cert_property[CERT_NAME_FRIENDLY_DISPLAY_TYPE], cert_property[CERT_NAME_RDN_TYPE]]
129
- end
130
- end
131
- CertFreeCertificateContext(pcert_context)
132
- rescue Exception => e
133
- lookup_error
134
- end
135
- certificate_list
136
- end
137
-
138
- private
139
-
140
- # Build arguments for CertAddEncodedCertificateToStore
141
- def cert_add_args(store_handler, certificate_obj)
142
- [store_handler, X509_ASN_ENCODING, der_cert(certificate_obj), certificate_obj.to_s.bytesize, 2, nil]
143
- end
144
-
145
- # Build arguments for CertFindCertificateInStore
146
- def cert_find_args(store_handler, cert_rdn)
147
- [store_handler, X509_ASN_ENCODING, 0, CERT_FIND_ISSUER_STR, cert_rdn.to_wstring, nil]
148
- end
149
-
150
- # Match certificate CN exist in cert_rdn
151
- def is_cn_match?(cert_rdn, certificate_name)
152
- cert_rdn.read_wstring.match(/(^|\W)#{certificate_name}($|\W)/i)
153
- end
154
-
155
- # Get Certificate all properties
156
- def get_cert_property(pcert_context)
157
- property_value = memory_ptr
158
- property_list = []
159
- property_list[0] = ""
160
- (1..8).to_a.each do |property_type|
161
- CertGetNameStringW(pcert_context, property_type, CERT_NAME_ISSUER_FLAG, nil, property_value, 1024)
162
- property_list << property_value.read_wstring
163
- end
164
- property_list
165
- end
166
-
167
- # Build argument for CertGetNameStringW
168
- def cert_get_name_args(pcert_context, cert_name, search_type)
169
- [pcert_context, search_type, CERT_NAME_ISSUER_FLAG, nil, cert_name, 1024]
170
- end
171
-
172
- # Remove extra space and : from thumbprint
173
- def update_thumbprint(certificate_thumbprint)
174
- certificate_thumbprint.gsub(/[^A-Za-z0-9]/, '')
175
- end
176
-
177
- # Verify OpenSSL::X509::Certificate object
178
- def verify_certificate(cert_pem)
179
- return "Certificate not found" if cert_pem.empty?
180
- valid_duration?(build_openssl_obj(cert_pem))
181
- end
182
-
183
- # Convert OpenSSL::X509::Certificate object in .der formate
184
- def der_cert(cert_obj)
185
- FFI::MemoryPointer.from_string(cert_obj.to_der)
186
- end
187
-
188
- # Get certificate pem
189
- def get_cert_pem(thumbprint)
190
- get_data = powershell_out!(cert_ps_cmd(thumbprint))
191
- get_data.stdout
192
- end
193
-
194
- # To get RDN from certificate object
195
- def get_rdn(cert_obj)
196
- cert_obj.issuer.to_s.concat("/").scan(/=(.*?)\//).join(", ")
197
- end
198
-
199
- # Format pem
200
- def format_pem(cert_pem)
201
- cert_pem.delete("\r")
202
- end
203
-
204
- # Build pem to OpenSSL::X509::Certificate object
205
- def build_openssl_obj(cert_pem)
206
- OpenSSL::X509::Certificate.new(cert_pem)
207
- end
208
- # Create empty memory pointer
209
- def memory_ptr
210
- FFI::MemoryPointer.new(2, 256)
211
- end
212
- end
213
- end
214
- end
1
+ #
2
+ # Author:: Piyush Awasthi (<piyush.awasthi@msystechnologies.com>)
3
+ # Copyright:: Copyright (c) 2017 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ require_relative "mixin/crypto"
19
+ require_relative "mixin/string"
20
+ require_relative "mixin/shell_out"
21
+ require_relative "mixin/unicode"
22
+ require "openssl"
23
+ require "json"
24
+
25
+ module Win32
26
+ class Certstore
27
+ module StoreBase
28
+ include Win32::Certstore::Mixin::Crypto
29
+ include Win32::Certstore::Mixin::Assertions
30
+ include Win32::Certstore::Mixin::String
31
+ include Win32::Certstore::Mixin::ShellOut
32
+ include Win32::Certstore::Mixin::Unicode
33
+ include Win32::Certstore::Mixin::Helper
34
+
35
+ # Adding new certification in open certificate and return boolean
36
+ # store_handler => Open certificate store handler
37
+ # certificate_obj => certificate object must be in OpenSSL::X509
38
+ def cert_add(store_handler, certificate_obj)
39
+ validate_certificate_obj(certificate_obj)
40
+ begin
41
+ cert_args = cert_add_args(store_handler, certificate_obj)
42
+ if CertAddEncodedCertificateToStore(*cert_args)
43
+ true
44
+ else
45
+ lookup_error
46
+ end
47
+ rescue Exception => e
48
+ lookup_error("add")
49
+ end
50
+ end
51
+
52
+ # Get certificate from open certificate store and return certificate object
53
+ # store_handler => Open certificate store handler
54
+ # certificate_thumbprint => thumbprint is a hash. which could be sha1 or md5.
55
+ def cert_get(certificate_thumbprint)
56
+ validate_thumbprint(certificate_thumbprint)
57
+ thumbprint = update_thumbprint(certificate_thumbprint)
58
+ cert_pem = get_cert_pem(thumbprint)
59
+ cert_pem = format_pem(cert_pem)
60
+ unless cert_pem.empty?
61
+ build_openssl_obj(cert_pem)
62
+ end
63
+ end
64
+
65
+ # Listing certificate of open certstore and return list in json
66
+ def cert_list(store_handler)
67
+ cert_name = memory_ptr
68
+ cert_list = []
69
+ begin
70
+ while (pcert_context = CertEnumCertificatesInStore(store_handler, pcert_context)) && (not pcert_context.null?)
71
+ cert_args = cert_get_name_args(pcert_context, cert_name, CERT_NAME_FRIENDLY_DISPLAY_TYPE)
72
+ if CertGetNameStringW(*cert_args)
73
+ cert_list << cert_name.read_wstring
74
+ end
75
+ end
76
+ CertFreeCertificateContext(pcert_context)
77
+ rescue Exception => e
78
+ lookup_error("list")
79
+ end
80
+ cert_list.to_json
81
+ end
82
+
83
+ # Deleting certificate from open certificate store and return boolean
84
+ # store_handler => Open certificate store handler
85
+ # certificate_thumbprint => thumbprint is a hash. which could be sha1 or md5.
86
+ def cert_delete(store_handler, certificate_thumbprint)
87
+ validate_thumbprint(certificate_thumbprint)
88
+ cert_name = memory_ptr
89
+ thumbprint = update_thumbprint(certificate_thumbprint)
90
+ cert_pem = format_pem(get_cert_pem(thumbprint))
91
+ cert_rdn = get_rdn(build_openssl_obj(cert_pem))
92
+ cert_delete_flag = false
93
+ begin
94
+ cert_args = cert_find_args(store_handler, cert_rdn)
95
+ if (pcert_context = CertFindCertificateInStore(*cert_args)) && !pcert_context.null?
96
+ cert_delete_flag = CertDeleteCertificateFromStore(CertDuplicateCertificateContext(pcert_context)) || lookup_error
97
+ end
98
+ CertFreeCertificateContext(pcert_context)
99
+ rescue Exception => e
100
+ lookup_error("delete")
101
+ end
102
+ cert_delete_flag
103
+ end
104
+
105
+ # Verify certificate from open certificate store and return boolean or exceptions
106
+ # store_handler => Open certificate store handler
107
+ # certificate_thumbprint => thumbprint is a hash. which could be sha1 or md5.
108
+ def cert_validate(certificate_thumbprint)
109
+ validate_thumbprint(certificate_thumbprint)
110
+ thumbprint = update_thumbprint(certificate_thumbprint)
111
+ cert_pem = get_cert_pem(thumbprint)
112
+ cert_pem = format_pem(cert_pem)
113
+ verify_certificate(cert_pem)
114
+ end
115
+
116
+ # Search certificate from open certificate store and return list
117
+ # store_handler => Open certificate store handler
118
+ # search_token => CN, RDN or any certificate attribute
119
+ def cert_search(store_handler, search_token)
120
+ raise ArgumentError, "Invalid search token" if !search_token || search_token.strip.empty?
121
+ cert_rdn = memory_ptr
122
+ certificate_list = []
123
+ counter = 0
124
+ begin
125
+ while (pcert_context = CertEnumCertificatesInStore(store_handler, pcert_context)) && !pcert_context.null?
126
+ cert_property = get_cert_property(pcert_context)
127
+ if cert_property.include?(search_token)
128
+ certificate_list << [cert_property[CERT_NAME_FRIENDLY_DISPLAY_TYPE], cert_property[CERT_NAME_RDN_TYPE]]
129
+ end
130
+ end
131
+ CertFreeCertificateContext(pcert_context)
132
+ rescue Exception => e
133
+ lookup_error
134
+ end
135
+ certificate_list
136
+ end
137
+
138
+ private
139
+
140
+ # Build arguments for CertAddEncodedCertificateToStore
141
+ def cert_add_args(store_handler, certificate_obj)
142
+ [store_handler, X509_ASN_ENCODING, der_cert(certificate_obj), certificate_obj.to_s.bytesize, 2, nil]
143
+ end
144
+
145
+ # Build arguments for CertFindCertificateInStore
146
+ def cert_find_args(store_handler, cert_rdn)
147
+ [store_handler, X509_ASN_ENCODING, 0, CERT_FIND_ISSUER_STR, cert_rdn.to_wstring, nil]
148
+ end
149
+
150
+ # Match certificate CN exist in cert_rdn
151
+ def is_cn_match?(cert_rdn, certificate_name)
152
+ cert_rdn.read_wstring.match(/(^|\W)#{certificate_name}($|\W)/i)
153
+ end
154
+
155
+ # Get Certificate all properties
156
+ def get_cert_property(pcert_context)
157
+ property_value = memory_ptr
158
+ property_list = []
159
+ property_list[0] = ""
160
+ (1..8).to_a.each do |property_type|
161
+ CertGetNameStringW(pcert_context, property_type, CERT_NAME_ISSUER_FLAG, nil, property_value, 1024)
162
+ property_list << property_value.read_wstring
163
+ end
164
+ property_list
165
+ end
166
+
167
+ # Build argument for CertGetNameStringW
168
+ def cert_get_name_args(pcert_context, cert_name, search_type)
169
+ [pcert_context, search_type, CERT_NAME_ISSUER_FLAG, nil, cert_name, 1024]
170
+ end
171
+
172
+ # Remove extra space and : from thumbprint
173
+ def update_thumbprint(certificate_thumbprint)
174
+ certificate_thumbprint.gsub(/[^A-Za-z0-9]/, "")
175
+ end
176
+
177
+ # Verify OpenSSL::X509::Certificate object
178
+ def verify_certificate(cert_pem)
179
+ return "Certificate not found" if cert_pem.empty?
180
+ valid_duration?(build_openssl_obj(cert_pem))
181
+ end
182
+
183
+ # Convert OpenSSL::X509::Certificate object in .der formate
184
+ def der_cert(cert_obj)
185
+ FFI::MemoryPointer.from_string(cert_obj.to_der)
186
+ end
187
+
188
+ # Get certificate pem
189
+ def get_cert_pem(thumbprint)
190
+ get_data = powershell_out!(cert_ps_cmd(thumbprint))
191
+ get_data.stdout
192
+ end
193
+
194
+ # To get RDN from certificate object
195
+ def get_rdn(cert_obj)
196
+ cert_obj.issuer.to_s.concat("/").scan(/=(.*?)\//).join(", ")
197
+ end
198
+
199
+ # Format pem
200
+ def format_pem(cert_pem)
201
+ cert_pem.delete("\r")
202
+ end
203
+
204
+ # Build pem to OpenSSL::X509::Certificate object
205
+ def build_openssl_obj(cert_pem)
206
+ OpenSSL::X509::Certificate.new(cert_pem)
207
+ end
208
+
209
+ # Create empty memory pointer
210
+ def memory_ptr
211
+ FFI::MemoryPointer.new(2, 256)
212
+ end
213
+ end
214
+ end
215
+ end