diff --git a/lib/win32/certstore.rb b/lib/win32/certstore.rb index e5188e1..328d071 100644 --- a/lib/win32/certstore.rb +++ b/lib/win32/certstore.rb @@ -70,6 +70,11 @@ def search(certificate_name) cert_search(certstore_handler, certificate_name) end + # Validate certificate from open certificate store and return boolean + def verify(certificate_name) + cert_verify(certstore_handler, certificate_name) + end + # To close and destroy pointer of open certificate store handler def close closed = CertCloseStore(@certstore_handler, CERT_CLOSE_STORE_FORCE_FLAG) diff --git a/lib/win32/certstore/mixin/helper.rb b/lib/win32/certstore/mixin/helper.rb index d248f87..f2b0671 100644 --- a/lib/win32/certstore/mixin/helper.rb +++ b/lib/win32/certstore/mixin/helper.rb @@ -36,6 +36,11 @@ def cert_ps_cmd(thumbprint) $content EOH end + + # validate certificate not_before and not_after date in UTC + def valid_duration(cert_obj) + cert_obj.not_before < Time.now.utc && cert_obj.not_after > Time.now.utc + end end end end diff --git a/lib/win32/certstore/store_base.rb b/lib/win32/certstore/store_base.rb index 0424225..7dc9936 100644 --- a/lib/win32/certstore/store_base.rb +++ b/lib/win32/certstore/store_base.rb @@ -102,6 +102,17 @@ def cert_delete(store_handler, certificate_thumbprint) cert_delete_flag end + # Verify certificate from open certificate store and return boolean or exceptions + # store_handler => Open certificate store handler + # certificate_thumbprint => thumbprint is a hash. which could be sha1 or md5. + def cert_verify(store_handler, certificate_thumbprint) + validate_thumbprint(certificate_thumbprint) + thumbprint = update_thumbprint(certificate_thumbprint) + cert_pem = get_cert_pem(thumbprint) + cert_pem = format_pem(cert_pem) + verify_certificate(cert_pem) + end + private # Build arguments for CertAddEncodedCertificateToStore @@ -140,6 +151,12 @@ def get_rdn(cert_obj) cert_obj.issuer.to_s.concat("/").scan(/=(.*?)\//).join(", ") end + # Verify OpenSSL::X509::Certificate object + def verify_certificate(cert_pem) + return "Certificate not found" if cert_pem.empty? + valid_duration(build_openssl_obj(cert_pem)) + end + # Format pem def format_pem(cert_pem) cert_pem.delete("\r") diff --git a/spec/win32/unit/certstore_spec.rb b/spec/win32/unit/certstore_spec.rb index bbb9e7b..1a6adaa 100644 --- a/spec/win32/unit/certstore_spec.rb +++ b/spec/win32/unit/certstore_spec.rb @@ -268,6 +268,84 @@ end end + describe "#cert_validate" do + context "When passing empty certificate store name" do + let (:store_name) { "" } + it "raises ArgumentError" do + expect { certstore.open(store_name) }.to raise_error(ArgumentError, "Invalid Certificate Store.") + end + end + + context "When passing empty thumbprint" do + let (:store_name) { "root" } + let (:thumbprint) { " " } + it "raises ArgumentError" do + store = certstore.open(store_name) + expect { store.valid?(thumbprint) }.to raise_error(ArgumentError, "Invalid certificate thumbprint.") + end + end + + context "When passing thumbprint is nil" do + let (:store_name) { "root" } + let (:thumbprint) { nil } + it "raises ArgumentError" do + store = certstore.open(store_name) + expect { store.valid?(thumbprint) }.to raise_error(ArgumentError, "Invalid certificate thumbprint.") + end + end + + context "When passing invalid thumbprint" do + let (:store_name) { "root" } + let (:thumbprint) { "b1bc968bd4f49d622aa89a81f2150152a41d829c" } + before(:each) do + allow_any_instance_of(certbase).to receive(:get_cert_pem).and_return("") + end + it "returns Certificate not found" do + store = certstore.open(store_name) + expect(store.valid?(thumbprint)).to eql("Certificate not found") + end + end + + context "When passing valid certificate's thumbprint" do + let (:store_name) { "root" } + let (:thumbprint) { "b1bc968bd4f49d622aa89a81f2150152a41d829909c" } + let (:cert_pem) { File.read('.\spec\win32\unit\assets\GlobalSignRootCA.pem') } + before(:each) do + allow_any_instance_of(certbase).to receive(:get_cert_pem).and_return(cert_pem) + end + it "returns true" do + store = certstore.open(store_name) + expect(store.valid?(thumbprint)).to eql(true) + end + end + + context "When passing valid certificate's thumbprint with spaces" do + let (:store_name) { "root" } + let (:thumbprint) { "b1 bc 96 8b d4 f4 9d 62 2a a8 9a 81 f2 15 01 52 a4 1d 82 9c" } + let (:cert_pem) { File.read('.\spec\win32\unit\assets\GlobalSignRootCA.pem') } + before(:each) do + allow_any_instance_of(certbase).to receive(:get_cert_pem).and_return(cert_pem) + end + it "returns true" do + store = certstore.open(store_name) + expect(store.valid?(thumbprint)).to eql(true) + end + end + + context "When passing valid certificate's thumbprint with :" do + let (:store_name) { "root" } + let (:thumbprint) { "b1:bc:96:8b:d4:f4:9d:62:2a:a8:9a:81:f2:15:01:52:a4:1d:82:9c" } + let (:cert_pem) { File.read('.\spec\win32\unit\assets\GlobalSignRootCA.pem') } + before(:each) do + allow_any_instance_of(certbase).to receive(:get_cert_pem).and_return(cert_pem) + end + it "returns true" do + store = certstore.open(store_name) + expect(store.valid?(thumbprint)).to eql(true) + end + end + end + describe "Perform more than one operations with single certstore object" do context "Perform add and list with single certstore object" do let (:store_name) { "root" }