Невозможно создать действительный CRYPT_HASH_BLOB в рубине - PullRequest
0 голосов
/ 26 декабря 2018

Я использую 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

1 Ответ

0 голосов
/ 26 декабря 2018

Я совсем не знаком с Ruby, но могу сделать некоторые предположения.Поправьте меня, если я ошибаюсь.

Вы конвертируете отпечаток исходного кода (значение SHA1) в строку без пробелов между октетами:

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

Это приведет к чему-то вроде этого: 1DF4ABB613F212271C04F8529DDE38E4B7242E6Cи затем вы передаете эту строку в конструктор CRYPT_HASH_BLOB в этой строке:

pcert_context = CertFindCertificateInStore(certstore_handler, ENCODING_TYPE, 0, CERT_FIND_SHA1_HASH, CRYPT_HASH_BLOB.new(thumbprint), nil)

В CRYPT_HASH_BLOB.initialize(str) вы распаковываете строку utf8 в байтовый массив.Первый метод неверен, потому что вы рассматриваете входную строку как массив символов.Вместо этого это шестнадцатеричный байтовый массив, поэтому должна быть задействована строка arr = [str].pack('H*').unpack('C*').По крайней мере, Google предполагает, что эта строка преобразует серию октетов в соответствующий байтовый массив.Таким образом, метод 3 хорош, за исключением того, что вы должны выделить * байт * массив (не целочисленный массив) и записать символы вместо целых чисел.Правильный код конструктора должен выглядеть следующим образом:

def initialize(str)
    super(nil)
    if str
        # Converting thumbpring string into a byte array
        arr = [str].pack('H*').unpack('C*')

        # Method 3: Using Byte Array with FFI::MemoryPointer
        buffer3 = FFI::MemoryPointer.new(:char, arr.size) # Create the pointer to the array
        buffer3.write_array_of_char(arr) # Fill the memory location with data
        self[:pbData] = buffer3
        self[:cbData] = arr.size
    end
end

Полагаю, это сработает.

...