Я использую Crypto API в Ruby, чтобы найти сертификат Windows, используя его Thumbprint.Для этого я использую CertFindCertificateInStore функцию, которая требует структуру CRYPT_HASH_BLOB .
Я имею дело с указателями с помощью FFI::MemoryPointer
.Пробовал различные подходы для создания этой структуры, но ни один из них, похоже, не работает.
Может кто-нибудь, пожалуйста, посмотрите и предоставьте мне предложения.
Вот полный код для ссылки:
require "ffi"
module LibC
extend FFI::Library
ffi_lib FFI::Library::LIBC
# memory allocators
attach_function :malloc, [:size_t], :pointer
attach_function :calloc, [:size_t], :pointer
attach_function :free, [:pointer], :void
end
module Crypto
extend LibC
extend FFI::Library
ffi_lib "Crypt32"
HCERTSTORE = FFI::TypeDefs[:pointer]
HCRYPTPROV_LEGACY = FFI::TypeDefs[:pointer]
PCCERT_CONTEXT = FFI::TypeDefs[:pointer]
DWORD = FFI::TypeDefs[:uint32]
BLOB = FFI::TypeDefs[:ulong]
LPCTSTR = FFI::TypeDefs[:pointer]
BOOL = FFI::TypeDefs[:bool]
LPVOID = FFI::TypeDefs[:pointer]
class CRYPT_HASH_BLOB < FFI::Struct
layout :cbData, DWORD, # Count, in bytes, of data
:pbData, :pointer # Pointer to data buffer
def initialize(str)
super(nil)
if str
# Method 1: Simply using thumbprint string
buffer1 = LibC.malloc str.size
buffer1.write_string str
self[:pbData] = buffer1
self[:cbData] = str.size
# Converting thumbpring string into a byte array
# arr = [str].pack('H*').unpack('C*')
# Method 2: Using Byte Array with LibC
# buffer2 = LibC.malloc(arr.first.size * arr.size) # Create the pointer to the array
# buffer2.write_array_of_uint32 arr # Fill the memory location with data
# self[:pbData] = buffer2
# self[:cbData] = arr.size
# # Method 3: Using Byte Array with FFI::MemoryPointer
# buffer3 = FFI::MemoryPointer.new :uint32, arr.size # Create the pointer to the array
# buffer3.put_array_of_uint32 0, arr # Fill the memory location with data
# self[:pbData] = buffer3
# self[:cbData] = arr.size
end
end
end
attach_function :CertOpenStore, [DWORD, DWORD, HCRYPTPROV_LEGACY, DWORD, LPCTSTR], HCERTSTORE
attach_function :CertCloseStore, [HCERTSTORE, DWORD], BOOL
attach_function :CertFindCertificateInStore, [HCERTSTORE, DWORD, DWORD, DWORD, LPVOID, PCCERT_CONTEXT], PCCERT_CONTEXT
attach_function :CertFreeCertificateContext, [PCCERT_CONTEXT], BOOL
=begin
PCCERT_CONTEXT CertFindCertificateInStore(
HCERTSTORE hCertStore,
DWORD dwCertEncodingType,
DWORD dwFindFlags,
DWORD dwFindType,
const void *pvFindPara,
PCCERT_CONTEXT pPrevCertContext
);
=end
end
class CertificateHandler
include Crypto
CERT_STORE_PROV_SYSTEM = 10
CERT_SYSTEM_STORE_LOCAL_MACHINE = 0x00020000
X509_ASN_ENCODING = 0x00000001
PKCS_7_ASN_ENCODING = 0x00010000
ENCODING_TYPE = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING
CERT_CLOSE_STORE_FORCE_FLAG = 1
CERT_COMPARE_SHA1_HASH = 1
CERT_COMPARE_SHIFT = 16
CERT_FIND_SHA1_HASH = CERT_COMPARE_SHA1_HASH << CERT_COMPARE_SHIFT
def self.finalize(certstore_handler)
proc { certstore_handler.to_s }
end
def add_finalizer(certstore_handler)
ObjectSpace.define_finalizer(self, self.class.finalize(certstore_handler))
end
def remove_finalizer
ObjectSpace.undefine_finalizer(self)
end
def utf8_to_wide(ustring)
# ensure it is actually UTF-8
# Ruby likes to mark binary data as ASCII-8BIT
ustring = (ustring + "").force_encoding("UTF-8") if ustring.respond_to?(:force_encoding) && ustring.encoding.name != "UTF-8"
# ensure we have the double-null termination Windows Wide likes
ustring += "\000\000" if ustring.length == 0 || ustring[-1].chr != "\000"
# encode it all as UTF-16LE AKA Windows Wide Character AKA Windows Unicode
ustring = ustring.encode("UTF-16LE") if ustring.respond_to?(:encode)
ustring
end
def cert_find_by_thumbprint(thumbprint = nil)
store_name = "Root"
thumbprint ||= "1D F4 AB B6 13 F2 12 27 1C 04 F8 52 9D DE 38 E4 B7 24 2E 6C" # Assume this is a valid thumbprint in Root
thumbprint.gsub!(/[^A-Za-z0-9]/, "") # Discard WhiteSpaces
certstore_handler = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, nil,
CERT_SYSTEM_STORE_LOCAL_MACHINE, utf8_to_wide(store_name))
add_finalizer(certstore_handler)
pcert_context = CertFindCertificateInStore(certstore_handler, ENCODING_TYPE, 0, CERT_FIND_SHA1_HASH, CRYPT_HASH_BLOB.new(thumbprint), nil)
puts "Certificate Found = #{!pcert_context.null?}" # It should be true
CertFreeCertificateContext(pcert_context)
closed = CertCloseStore(@certstore_handler, CERT_CLOSE_STORE_FORCE_FLAG)
remove_finalizer
end
end
CertificateHandler.new.cert_find_by_thumbprint