-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[MSYS-543] [MSYS-546] Added feature to list certificates and add new certificate. #4
Changes from all commits
deea2dc
5ac4928
3dda9f7
393454c
f30b3a4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,92 @@ | ||
# win32-certstore | ||
Ruby library for accessing the certificate store on Windows | ||
Ruby library for accessing the certificate store on Microsoft Windows: | ||
|
||
## Subcommands | ||
|
||
This library provides the following features. | ||
|
||
### Open certificate store | ||
|
||
Any valid certificate store can be opened in two ways: | ||
|
||
**Notes: Valid certificate store names: | ||
`CA -> Certification authority certificates.` | ||
`MY -> A certificate store that holds certificates with associated private keys.` | ||
`ROOT -> Root certificates.` | ||
`SPC -> Software Publisher Certificate.`** | ||
|
||
``` | ||
Win32::Certstore.open("Root") do |store| | ||
//your code should be here! | ||
end | ||
``` | ||
or | ||
``` | ||
store = Win32::Certstore.open("Root") | ||
``` | ||
|
||
### List certificates | ||
|
||
Lists certificates of a valid certificate store and returns output in JSON format: | ||
|
||
``` | ||
Win32::Certstore.open("Root") do |store| | ||
store.list | ||
end | ||
``` | ||
or | ||
``` | ||
store = Win32::Certstore.open("Root") | ||
store.list | ||
``` | ||
|
||
### Add certificate | ||
|
||
Add a valid certificate in a certificate store. | ||
|
||
**Notes: The new certificate should be in the following formats `.cer|.crt|.pfx|.der`:** | ||
|
||
``` | ||
Win32::Certstore.open("Root") do |store| | ||
store.add(certificate_file_path) | ||
end | ||
``` | ||
or | ||
``` | ||
store = Win32::Certstore.open("Root") | ||
store.add(certificate_file_path) | ||
``` | ||
|
||
## Requirements / setup | ||
|
||
### Ruby | ||
|
||
Ruby 1.9.3+ is required. | ||
|
||
### Chef version | ||
|
||
This library requires >= Chef 11.0.0. | ||
|
||
## CONTRIBUTING: | ||
|
||
Please file bugs against the WIN32-CERTSTORE project at https://github.com/chef/win32-certstore/issues. | ||
|
||
More information on the contribution process for Chef projects can be found in the [Chef Contributions document](http://docs.chef.io/community_contributions.html). | ||
|
||
# LICENSE: | ||
|
||
Author:: Bryan McLellan (<[email protected]>) | ||
Copyright:: Copyright (c) 2017 Chef Software, Inc. | ||
License:: Apache License, Version 2.0 | ||
|
||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
|
||
http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
$LOAD_PATH.unshift File.expand_path('../../lib/win32', __FILE__) | ||
# | ||
# Author:: Nimisha Sharad (<[email protected]>) | ||
# Copyright:: Copyright (c) 2017 Chef Software, Inc. | ||
|
@@ -15,4 +16,6 @@ | |
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
require 'win32/certstore/certstore' | ||
require "mixin/crypto" | ||
require "certstore/certstore" | ||
require "win32-certstore/version" |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,30 +15,77 @@ | |
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
require "win32/certstore/version" | ||
require 'win32/api/reserved_names' | ||
require 'mixin/crypto' | ||
require 'mixin/assertions' | ||
require_relative 'store_base' | ||
|
||
module Win32 | ||
module Certstore | ||
include Win32::API::ReservedNames | ||
class Certstore | ||
include Win32::Mixin::Crypto | ||
extend Win32::Mixin::Assertions | ||
include Chef::Mixin::WideString | ||
include Win32::Certstore::StoreBase | ||
|
||
def open store_name | ||
certstore_handle = CertOpenSystemStoreW(nil, wstring(store_name)) | ||
unless certstore_handle | ||
attr_reader :store_name | ||
|
||
def initialize(store_name) | ||
@certstore_handler = open(store_name) | ||
end | ||
|
||
def self.open(store_name) | ||
validate_store(store_name) | ||
if block_given? | ||
yield self.new(store_name) | ||
else | ||
self.new(store_name) | ||
end | ||
end | ||
|
||
def list | ||
list = cert_list(@certstore_handler) | ||
close | ||
return list | ||
end | ||
|
||
def add(cert_file_path) | ||
add = cert_add(@certstore_handler, cert_file_path) | ||
close | ||
return add | ||
end | ||
|
||
private | ||
|
||
attr_reader :certstore_handler | ||
|
||
def open(store_name) | ||
certstore_handler = CertOpenSystemStoreW(nil, wstring(store_name)) | ||
unless certstore_handler | ||
last_error = FFI::LastError.error | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. open and close are the private methods of certstore. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense. |
||
raise Chef::Exceptions::Win32APIError, "Unable to open the Certificate Store `#{store_name}` with error: #{last_error}." | ||
end | ||
certstore_handle | ||
add_finalizer(certstore_handler) | ||
certstore_handler | ||
end | ||
|
||
def add_finalizer(certstore_handler) | ||
ObjectSpace.define_finalizer(self, self.class.finalize(certstore_handler)) | ||
end | ||
|
||
def close certstore_handle | ||
closed = CertCloseStore(certstore_handle, CERT_CLOSE_STORE_FORCE_FLAG) | ||
def self.finalize(certstore_handler) | ||
proc { puts "DESTROY OBJECT #{certstore_handler}" } | ||
end | ||
|
||
def close | ||
closed = CertCloseStore(@certstore_handler, CERT_CLOSE_STORE_FORCE_FLAG) | ||
unless closed | ||
last_error = FFI::LastError.error | ||
raise Chef::Exceptions::Win32APIError, "Unable to close the Certificate Store with error: #{last_error}." | ||
end | ||
closed | ||
remove_finalizer | ||
end | ||
|
||
def remove_finalizer | ||
ObjectSpace.undefine_finalizer(self) | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
# | ||
# Author:: Piyush Awasthi (<[email protected]>) | ||
# Copyright:: Copyright (c) 2017 Chef Software, Inc. | ||
# License:: Apache License, Version 2.0 | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
require 'mixin/crypto' | ||
require 'openssl' | ||
|
||
module Win32 | ||
class Certstore | ||
module StoreBase | ||
include Win32::Mixin::Crypto | ||
include Win32::Mixin::Assertions | ||
include Chef::Mixin::WideString | ||
include Chef::Mixin::ShellOut | ||
|
||
def cert_list(store_handler) | ||
cert_name = FFI::MemoryPointer.new(2, 128) | ||
cert_list = [] | ||
begin | ||
while (pCertContext = CertEnumCertificatesInStore(store_handler, pCertContext) and not pCertContext.null? ) do | ||
if (CertGetNameStringW(pCertContext, CERT_NAME_FRIENDLY_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, nil, cert_name, 1024)) | ||
cert_list << cert_name.read_wstring | ||
end | ||
end | ||
CertFreeCertificateContext(pCertContext) | ||
rescue Exception => e | ||
lookup_error("list") | ||
end | ||
cert_list.to_json | ||
end | ||
|
||
def cert_add(store_handler, cert_file_path) | ||
validate_certificate(cert_file_path) | ||
file_content = read_certificate_content(cert_file_path) | ||
pointer_cert = FFI::MemoryPointer.from_string(file_content) | ||
cert_length = file_content.bytesize | ||
begin | ||
if (CertAddEncodedCertificateToStore(store_handler, X509_ASN_ENCODING, pointer_cert, cert_length, 2, nil)) | ||
"Added certificate #{File.basename(cert_file_path)} successfully" | ||
else | ||
lookup_error | ||
end | ||
rescue Exception => e | ||
lookup_error("add") | ||
end | ||
end | ||
|
||
private | ||
|
||
def lookup_error(failed_operation = nil) | ||
last_error = FFI::LastError.error | ||
case last_error | ||
when 1223 | ||
raise Chef::Exceptions::Win32APIError, "The operation was canceled by the user." | ||
when -2146885628 | ||
raise Chef::Exceptions::Win32APIError, "Cannot find object or property." | ||
when -2146885629 | ||
raise Chef::Exceptions::Win32APIError, "An error occurred while reading or writing to a file." | ||
when -2146881269 | ||
raise Chef::Exceptions::Win32APIError, "ASN1 bad tag value met. -- Is the certificate in DER format?" | ||
when -2146881278 | ||
raise Chef::Exceptions::Win32APIError, "ASN1 unexpected end of data." | ||
else | ||
raise Chef::Exceptions::Win32APIError, "Unable to #{failed_operation} certificate with error: #{last_error}." | ||
end | ||
end | ||
|
||
# This is a single public certificate in X509 DER format. | ||
# If your certificate has a header and footer line like "---- BEGIN CERTIFICATE ----" then it is in PEM format, not DER format. | ||
# A certificate can be converted with `openssl x509 -in example.crt -out example.der -outform DER` | ||
def read_certificate_content(cert_path) | ||
unless (File.extname(cert_path) == ".der") | ||
temp_file = shell_out("powershell.exe -Command $env:temp").stdout.strip.concat("\\TempCert.der") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use
|
||
shell_out("powershell.exe -Command openssl x509 -in #{cert_path} -outform DER -out #{temp_file}") | ||
cert_path = temp_file | ||
end | ||
File.read("#{cert_path}") | ||
end | ||
|
||
end | ||
end | ||
end |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder why this is
certstore/certstore
here ?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest being more specific here, i.e. use `require "win32/certstore/certstore" so we don't risk picking up something else in the LOAD_PATH called 'certstore'.