Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Delphi Новый топик    Ответить
 WinApi получение отпечатка сертификата из хранилища  [new]
ora0ra
Member

Откуда:
Сообщений: 5
Всем привет!

Встал вопрос! Как получить отпечаток (он же thumbprint) из хранилища сертификатов в винде? Набросал библиотеку для получения серийного номера из crypt32.dll, а куда дальше двигать не понятно. Подскажите как получить отпечаток?

Библиотека:
+
unit CryptoWinUtils;

interface

uses
  Windows, classes, SysUtils;

type
  /////////////////////////////
  // WinApi структуры
  /////////////////////////////

  CRYPTOAPI_BLOB = packed record
    cbData: DWORD;
    pbData: PByte;
  end;

  CRYPT_INTEGER_BLOB = CRYPTOAPI_BLOB;
  CRYPT_OBJID_BLOB = CRYPTOAPI_BLOB;

  CERT_NAME_BLOB = CRYPTOAPI_BLOB;
  PCERT_NAME_BLOB = ^CERT_NAME_BLOB;

  CRYPT_ALGORITHM_IDENTIFIER = packed record
    pszObjId: LPSTR;
    Parameters: CRYPT_OBJID_BLOB;
  end;

  CRYPT_BIT_BLOB = packed record
    cbData: DWORD;
    pbData: PBYTE;
    cUnusedBits: DWORD;
  end;

  CERT_PUBLIC_KEY_INFO = packed record
    Algorithm: CRYPT_ALGORITHM_IDENTIFIER;
    PublicKey: CRYPT_BIT_BLOB;
  end;

  CERT_EXTENSION = packed record
    pszObjId: LPSTR;
    fCritical: BOOL;
    Value: CRYPT_OBJID_BLOB;
  end;
  PCERT_EXTENSION = ^CERT_EXTENSION;
  TARR_CERT_EXTENSION = PCERT_EXTENSION;

  CERT_INFO = packed record
    dwVersion: DWORD;
    SerialNumber: CRYPT_INTEGER_BLOB;
    SignatureAlgorithm: CRYPT_ALGORITHM_IDENTIFIER;
    Issuer: CERT_NAME_BLOB;
    NotBefore: FILETIME;
    NotAfter: FILETIME;
    Subject: CERT_NAME_BLOB;
    SubjectPublicKeyInfo: CERT_PUBLIC_KEY_INFO;
    IssuerUniqueId: CRYPT_BIT_BLOB;
    SubjectUniqueId: CRYPT_BIT_BLOB;
    cExtension: DWORD;
    rgExtension: TARR_CERT_EXTENSION;
  end;
  PCERT_INFO = ^CERT_INFO;

  HCERTSTORE = Pointer;
  HCRYPTPROV = ULONG;

  CERT_CONTEXT = packed record
    dwCertEncodingType: DWORD;
    pbCertEncoded: PBYTE;
    cbCertEncoded: DWORD;
    pCertInfo: PCERT_INFO;
    hCertStore: HCERTSTORE;
  end;
  PCERT_CONTEXT = ^CERT_CONTEXT;
  PCCERT_CONTEXT = ^CERT_CONTEXT;

  /////////////////////////////
  // Другие структуры
  /////////////////////////////

  TCertRow = record
    SerialNumber: string;
    // ...
  end;
  TCertRows = array of TCertRow;

const
  X509_ASN_ENCODING = $00000001;
  PKCS_7_ASN_ENCODING = $00010000;

  CERT_SIMPLE_NAME_STR = 1; // All object identifiers (OIDs) are discarded. CERT_RDN entries are separated by a comma followed by a space (, ). Multiple attributes in a CERT_RDN are separated by a plus sign enclosed within spaces ( + ), for example, Firm, Qwe Asd + Zxc.
  CERT_OID_NAME_STR = 2; // OIDs are included with an equal sign (=) separator from their attribute value. CERT_RDN entries are separated by a comma followed by a space (, ). Multiple attributes in a CERT_RDN are separated by a plus sign followed by a space (+ ).
  CERT_X500_NAME_STR = 3; // OIDs are converted to their X.500 key names; otherwise, they are the same as CERT_OID_NAME_STR. If an OID does not have a corresponding X.500 name, the OID is used with a prefix of OID.
  CERT_NAME_STR_SEMICOLON_FLAG = $40000000; // Replace the comma followed by a space (, ) separator with a semicolon followed by a space (; ) separator.
  CERT_NAME_STR_CRLF_FLAG = $08000000; // Replace the comma followed by a space (, ) separator with a backslash followed by the letter r followed by a backslash followed by the letter n (\r\n) separator.
  CERT_NAME_STR_NO_PLUS_FLAG = $20000000; // Replace the plus sign enclosed within spaces ( + ) separator with a single space separator.
  CERT_NAME_STR_NO_QUOTING_FLAG = $10000000; // Disable quoting.
  CERT_NAME_STR_REVERSE_FLAG = $02000000; // The order of the RDNs in the distinguished name string is reversed after decoding. This flag is not set by default.
  CERT_NAME_STR_DISABLE_IE4_UTF8_FLAG = $00010000; // By default, a CERT_RDN_T61_STRING X.500 key string is decoded as UTF8. If UTF8 decoding fails, the X.500 key is decoded as an 8 bit character. Use CERT_NAME_STR_DISABLE_IE4_UTF8_FLAG to skip the initial attempt to decode as UTF8.
  CERT_NAME_STR_ENABLE_PUNYCODE_FLAG = $00200000; // If the name pointed to by the pName parameter contains an email RDN, and the host name portion of the email address contains a Punycode encoded IA5String, the name is converted to the Unicode equivalent.

  CERT_TYPE_INFO_SERIAL_NUMBER = 1;

  /////////////////////////////
  // WinApi функции
  /////////////////////////////

function CertFreeCertificateContext(pCertContext:
  PCCERT_CONTEXT): BOOL; stdcall external 'crypt32.dll';

function CertCreateCertificateContext(dwCertEncodingType: DWORD;
  pbCertEncoded: PBYTE; cbCertEncoded: DWORD): PCCERT_CONTEXT; stdcall
external 'crypt32.dll';

function CertOpenSystemStoreA(hProv: HCRYPTPROV;
  szSubsystemProtocol: LPCSTR): HCERTSTORE; stdcall
external 'crypt32.dll';

function CertCloseStore(hCertStore: HCERTSTORE;
  dwFlags: DWORD = 0): BOOL; stdcall
external 'crypt32.dll';

function CertEnumCertificatesInStore(hCertStore: HCERTSTORE;
  pPrevCertContext: PCCERT_CONTEXT): PCCERT_CONTEXT; stdcall
external 'crypt32.dll';

function CertNameToStrA(
  dwCertEncodingType: DWORD;
  pName: PCERT_NAME_BLOB;
  dwStrType: DWORD;
  psz: LPSTR;
  csz: DWORD): DWORD; stdcall
external 'crypt32.dll';

function CertNameToStrW(
  dwCertEncodingType: DWORD;
  pName: PCERT_NAME_BLOB;
  dwStrType: DWORD;
  psz: LPWSTR;
  csz: DWORD): DWORD; stdcall
external 'crypt32.dll';

/////////////////////////////
// Другие функции
/////////////////////////////

// ByteArrayToStr
function ByteArrayToStr(pbData: PByte; cbData: DWORD): string;

// Получить серийный номер сертификата
function CertGetSerialNumber(aCertInfo: PCCERT_CONTEXT): string;

// Получить список сертификатов из хранилища
function CertGetList(const aCertStoreName: string): TCertRows;

// Поиск сертификата
function CertFind(const aCertRows: TCertRows; const aCertData: string; const aCertTypeInfo: Integer): Integer;

implementation

// ByteArrayToStr

function ByteArrayToStr(pbData: PByte; cbData: DWORD): string;
var
  I, J: Integer;
  S: string;
begin
  Result := '';
  if not Assigned(pbData) or (cbData <= 0) then
    Exit;
  for I := 0 to cbData - 1 do
  begin
    J := PByteArray(pbData)^[i];
    S := IntToHex(J, 2);
    if (I > 0) and (I and 1 = 0) then
      S := S + ' ';
    Result := S + Result;
  end;
end;

// Получить серийный номер сертификата

function CertGetSerialNumber(aCertInfo: PCCERT_CONTEXT): string;
begin
  Result := ByteArrayToStr(
    aCertInfo.pCertInfo.SerialNumber.pbData
    , aCertInfo.pCertInfo.SerialNumber.cbData);
end;

// Получить список сертификатов из хранилища

function CertGetList(const aCertStoreName: string): TCertRows;
var
  certStoreName: PChar;
  hwndStore: HCERTSTORE;
  cert: PCCERT_CONTEXT;
  cryptoProvider: HCRYPTPROV;
  id: Integer;
begin
  // Преобразовываю имя хранилища
  certStoreName := StrAlloc(length(aCertStoreName) + 1);
  StrPCopy(certStoreName, aCertStoreName);

  // Получаю доступ к хранилищу
  hwndStore := CertOpenSystemStoreA(cryptoProvider, certStoreName);

  // Фокус на первый сертификат
  cert := CertEnumCertificatesInStore(hwndStore, nil);

  // Инициализация переменных
  SetLength(Result, 0);

  // Перебор сертификатов
  while cert <> nil do
  begin
    id := Length(Result);
    SetLength(Result, id + 1);

    // Серийный номер
    Result[id].SerialNumber := CertGetSerialNumber(cert);

    // Следующий сертификат
    cert := CertEnumCertificatesInStore(hwndStore, cert);
  end;

  // Завершить работу с хранилищем
  CertCloseStore(hwndStore);
end;

// Поиск сертификата

function CertFind(const aCertRows: TCertRows; const aCertData: string; const aCertTypeInfo: Integer): Integer;
var
  i, len: Integer;
  certData, certRowData: string;
begin
  Result := -1;
  len := Length(aCertRows);

  // Форматирование поискового текста
  case aCertTypeInfo of
    // Серийный номер
    CERT_TYPE_INFO_SERIAL_NUMBER:
      certData := AnsiUpperCase(StringReplace(aCertData, ' ', '', [rfReplaceAll]));
  end;

  // Поиск текста
  for i := 0 to len - 1 do
  begin
    // Форматирование переменной поиска из массива
    case aCertTypeInfo of
      // Серийный номер
      CERT_TYPE_INFO_SERIAL_NUMBER:
        certRowData := AnsiUpperCase(StringReplace(aCertRows[i].SerialNumber, ' ', '', [rfReplaceAll]));
    else
      Break;
    end;

    // Поиск
    if certRowData = certData then
    begin
      Result := i;
      Break;
    end;
  end;
end;

end.


Поиск сертификата по серийному номеру:
procedure TForm1.Button1Click(Sender: TObject);
var
  r: TCertRows;
  i: integer;
begin
  r := CertGetList('My');

  i := CertFind(r, '01e3fbf60031a9579540ca4145db0c17yu', CERT_TYPE_INFO_SERIAL_NUMBER);

  ShowMessage( IntToStr(i) );
end;


Модератор: Как мне оформить свое сообщение?
5 сен 19, 08:54    [21964190]     Ответить | Цитировать Сообщить модератору
 Re: WinApi получение отпечатка сертификата из хранилища  [new]
sql2012
Member

Откуда: РФ
Сообщений: 687
ora0ra,

CertGetCertificateContextProperty(Certificate, CERT_SHA1_HASH_PROP_ID, nil, pcbData)
5 сен 19, 09:29    [21964224]     Ответить | Цитировать Сообщить модератору
 Re: WinApi получение отпечатка сертификата из хранилища  [new]
sql2012
Member

Откуда: РФ
Сообщений: 687
выделяешь память размером pcbData и вызываешь повторно с указателем уже, вместо nil.

Или, зная что это отпечаток, его размерность и предварительно выделив память, можно одним вызовом.
5 сен 19, 09:32    [21964229]     Ответить | Цитировать Сообщить модератору
 Re: WinApi получение отпечатка сертификата из хранилища  [new]
sql2012
Member

Откуда: РФ
Сообщений: 687
ora0ra,

JwaWinCrypt.pas в помощь
5 сен 19, 09:34    [21964234]     Ответить | Цитировать Сообщить модератору
 Re: WinApi получение отпечатка сертификата из хранилища  [new]
ora0ra
Member

Откуда:
Сообщений: 5
sql2012, да благодарю, нашёл.

Написал такой код, если кому надо:
function CertGetCertificateContextProperty(
  pCertContext: PCCERT_CONTEXT;
  dwPropId: DWORD;
  pvData: Pointer;
  pcbData: PDWORD
  ): BOOL; stdcall external 'crypt32.dll';


// Получить отпечаток сертификата

function CertGetThumbPrint(aCertInfo: PCCERT_CONTEXT): string;
const
  DataLen = 20;
var
  Data: array[0..DataLen - 1] of Byte;
  DataLenTmp: PDWORD;
begin
  DataLenTmp^ := DataLen;

  if CertGetCertificateContextProperty(
    aCertInfo,
    CERT_SHA1_HASH_PROP_ID,
    @Data,
    DataLenTmp) then
  begin
    SetLength(Result, DataLen * 2);
    BinToHex(@Data, PChar(Result), DataLen);
  end
  else
    Result := '';
end;

Иногда может ничего не выводить, тогда нужно настроить DataLen (длина хэша).
11 сен 19, 11:49    [21968396]     Ответить | Цитировать Сообщить модератору
Все форумы / Delphi Ответить