win32-certstore 0.1.0 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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