Skip to content

Commit

Permalink
Delete certificate by thumbprint
Browse files Browse the repository at this point in the history
Signed-off-by: piyushawasthi <[email protected]>
  • Loading branch information
piyushawasthi authored and btm committed Apr 5, 2018
1 parent c3afd9d commit 96430d1
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 14 deletions.
31 changes: 22 additions & 9 deletions lib/win32/certstore/store_base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,19 +84,22 @@ def cert_list(store_handler)
# store_handler => Open certificate store handler
# certificate_thumbprint => thumbprint is a hash. which could be sha1 or md5.
def cert_delete(store_handler, certificate_thumbprint)
validate_thumbprint(certificate_thumbprint)
cert_name = memory_ptr
thumbprint = update_thumbprint(certificate_thumbprint)
cert_pem = format_pem(get_cert_pem(thumbprint))
cert_rdn = get_rdn(build_openssl_obj(cert_pem))
cert_delete_flag = false
begin
if !certificate_name.empty? && (pcert_context = CertFindCertificateInStore(store_handler, X509_ASN_ENCODING, 0, CERT_FIND_ISSUER_STR, certificate_name.to_wstring, nil)) && (not pcert_context.null?)
if CertDeleteCertificateFromStore(CertDuplicateCertificateContext(pcert_context))
return "Deleted certificate #{certificate_name} successfully"
else
lookup_error
end
cert_args = cert_find_args(store_handler, cert_rdn)
if (pcert_context = CertFindCertificateInStore(*cert_args) and !pcert_context.null?)
cert_delete_flag = CertDeleteCertificateFromStore(CertDuplicateCertificateContext(pcert_context)) || lookup_error
end
return "Cannot find certificate with name as `#{certificate_name}`. Please re-verify certificate Issuer name or Friendly name"
CertFreeCertificateContext(pcert_context)
rescue Exception => e
@error = "delete: "
lookup_error
lookup_error("delete")
end
cert_delete_flag
end

private
Expand All @@ -106,6 +109,11 @@ def cert_add_args(store_handler, certificate_obj)
[store_handler, X509_ASN_ENCODING, der_cert(certificate_obj), certificate_obj.to_s.bytesize, 2, nil]
end

# Build arguments for CertFindCertificateInStore
def cert_find_args(store_handler, cert_rdn)
[store_handler, X509_ASN_ENCODING, 0, CERT_FIND_ISSUER_STR, cert_rdn.to_wstring, nil]
end

# Build argument for CertGetNameStringW
def cert_get_name_args(pcert_context, cert_name)
[pcert_context, CERT_NAME_FRIENDLY_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, nil, cert_name, 1024]
Expand All @@ -127,6 +135,11 @@ def get_cert_pem(thumbprint)
get_data.stdout
end

# To get RDN from certificate object
def get_rdn(cert_obj)
cert_obj.issuer.to_s.concat("/").scan(/=(.*?)\//).join(", ")
end

# Format pem
def format_pem(cert_pem)
cert_pem.delete("\r")
Expand Down
92 changes: 87 additions & 5 deletions spec/win32/unit/certstore_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@
let (:store_name) { "root" }
let (:thumbprint) { "b1bc968bd4f49d622aa89a81f2150152a41d829c" }
before(:each) do
allow_any_instance_of(certbase).to receive(:get_cert_pem).and_return([])
allow_any_instance_of(certbase).to receive(:get_cert_pem).and_return("")
end
it "returns nil" do
store = certstore.open(store_name)
Expand All @@ -142,7 +142,7 @@
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])
allow_any_instance_of(certbase).to receive(:get_cert_pem).and_return(cert_pem)
end
it "returns OpenSSL::X509::Certificate Object" do
store = certstore.open(store_name)
Expand All @@ -158,7 +158,7 @@
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])
allow_any_instance_of(certbase).to receive(:get_cert_pem).and_return(cert_pem)
end
it "returns OpenSSL::X509::Certificate Object" do
store = certstore.open(store_name)
Expand All @@ -174,7 +174,7 @@
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])
allow_any_instance_of(certbase).to receive(:get_cert_pem).and_return(cert_pem)
end
it "returns OpenSSL::X509::Certificate Object" do
store = certstore.open(store_name)
Expand All @@ -184,7 +184,88 @@
expect(cert_obj.not_after.to_s).to eql("2028-01-28 12:00:00 UTC")
end
end
end

describe "#cert_delete" 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.delete(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.delete(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(:CertFindCertificateInStore).and_return(false)
allow_any_instance_of(certbase).to receive(:lookup_error).and_return(false)
end
it "returns false" do
store = certstore.open(store_name)
expect(store.delete(thumbprint)).to eql(false)
end
end

context "When passing valid 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)
allow_any_instance_of(certbase).to receive(:CertDeleteCertificateFromStore).and_return(true)
end
it "returns true" do
store = certstore.open(store_name)
expect(store.delete(thumbprint)).to eql(true)
end
end

context "When passing valid 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)
allow_any_instance_of(certbase).to receive(:CertDeleteCertificateFromStore).and_return(true)
end
it "returns true" do
store = certstore.open(store_name)
expect(store.delete(thumbprint)).to eql(true)
end
end

context "When passing valid 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)
allow_any_instance_of(certbase).to receive(:CertDeleteCertificateFromStore).and_return(true)
end
it "returns true" do
store = certstore.open(store_name)
expect(store.delete(thumbprint)).to eql(true)
end
end
end

describe "Perform more than one operations with single certstore object" do
Expand Down Expand Up @@ -214,6 +295,7 @@
let (:cert_file_path) { '.\spec\win32\unit\assets\GlobalSignRootCA.pem' }
let (:certificate_object) { OpenSSL::X509::Certificate.new(File.read cert_file_path) }
let (:certificate_name) { "GlobalSign" }
let (:thumbprint) { "b1bc968bd4f49d622aa89a81f2150152a41d829c" }

it "returns 'The operation was canceled by the user'" do
allow(certstore_handler).to receive(:CertAddEncodedCertificateToStore).and_return(false)
Expand Down Expand Up @@ -260,7 +342,7 @@
allow(certbase).to receive(:CertDeleteCertificateFromStore).and_return(false)
allow(FFI::LastError).to receive(:error).and_return(-2147024891)
store = certstore.open(store_name)
expect { store.delete(certificate_name) }.to raise_error(SystemCallError)
expect { store.delete(thumbprint) }.to raise_error(SystemCallError)
end
end
end
Expand Down

0 comments on commit 96430d1

Please sign in to comment.