Как я могу получить доступ к сертификатам корневых центров сертификации Windows с помощью Delphi?

Вопрос, связанный с Lazarus или Delphi. Есть ли способ программно получить доступ к доверенным сертификатам корневых центров сертификации в Windows. Я знаю, что в Windows есть инструмент на основе графического интерфейса, который называется «mmc.exe», но мне нужно получить доступ к файлам сертификатов (таким как .crt или .cer или .pem и т. д.) с использованием синтаксиса Object Pascal. Может ли кто-нибудь помочь мне с этим?


person whoosaa    schedule 17.11.2011    source источник


Ответы (3)


В качестве альтернативы есть CAPICOM, который вы можете просто импортировать как библиотеку типов ActiveX, но есть и старый добрый Windows API криптографии.

В качестве примера, вот мой очень старый тестовый проект (в последнее время я его не пробовал). Вам понадобится модуль WinCrypt или JwaWinCrypt с соответствующими переводами API, которые должны быть доступны на JEDI. :

program lstore;

{$APPTYPE CONSOLE}

uses
  Windows,
  SysUtils,
  WinCrypt;

var
  StoreName: array[0..255] of Char;
  hStore: HCERTSTORE;
  CertContext: PCertContext;
  CertPropId: DWORD;
  Data: array[0..511] of Char;
  DataLen: DWORD;
  i: Integer;

procedure DisplayCertContext(Cert: PCertContext);
var
  CertName: array[0..255] of Char;
begin
  if CertGetNameString(CertContext, CERT_NAME_EMAIL_TYPE, 0, nil,
    CertName, 256) = 0 then
    RaiseLastWin32Error;
  Writeln('Subject CERT_NAME_EMAIL_TYPE: ', CertName);
  if CertGetNameString(CertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, nil,
    CertName, 256) = 0 then
    RaiseLastWin32Error;
  Writeln('Subject CERT_NAME_SIMPLE_DISPLAY_TYPE: ', CertName);
  if CertGetNameString(CertContext, CERT_NAME_FRIENDLY_DISPLAY_TYPE, 0, nil,
    CertName, 256) = 0 then
    RaiseLastWin32Error;
  Writeln('Subject CERT_NAME_FRIENDLY_DISPLAY_TYPE: ', CertName);

  if CertGetNameString(CertContext, CERT_NAME_EMAIL_TYPE, CERT_NAME_ISSUER_FLAG, nil,
    CertName, 256) = 0 then
    RaiseLastWin32Error;
  Writeln('Issuer CERT_NAME_EMAIL_TYPE: ', CertName);
  if CertGetNameString(CertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, nil,
    CertName, 256) = 0 then
    RaiseLastWin32Error;
  Writeln('Issuer CERT_NAME_SIMPLE_DISPLAY_TYPE: ', CertName);
  if CertGetNameString(CertContext, CERT_NAME_FRIENDLY_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, nil,
    CertName, 256) = 0 then
    RaiseLastWin32Error;
  Writeln('Issuer CERT_NAME_FRIENDLY_DISPLAY_TYPE: ', CertName);
end;

begin
  try
    Write('Enter name of store to be listed: ');
    Readln(StoreName);
    hStore := CertOpenSystemStore(0, StoreName);
    if hStore = nil then
      RaiseLastWin32Error;
    try
      CertContext := CertEnumCertificatesInStore(hStore, nil);
      while CertContext <> nil do
      begin
        DisplayCertContext(CertContext);
        CertPropId := CertEnumCertificateContextProperties(CertContext, 0);
        while CertPropId <> 0 do
        begin
          DataLen := 512;
  //        Writeln(Format('CertPropId: %d', [CertPropId]));
          case CertPropId of
          CERT_KEY_PROV_HANDLE_PROP_ID:
          begin
            CertGetCertificateContextProperty(CertContext, CertPropId,
              @Data[0], DataLen);
            Writeln(Format('KEY_PROV_HANDLE: $%.8x', [PDWORD(@Data[0])^]));
          end;
          CERT_KEY_PROV_INFO_PROP_ID:
          begin
            CertGetCertificateContextProperty(CertContext, CertPropId,
              @Data[0], DataLen);
            with PCryptKeyProvInfo(@Data[0])^ do
            begin
              Writeln(Format('pwszContainerName = %s', [pwszContainerName]));
              Writeln(Format('pwszProvName = %s', [pwszProvName]));
              Writeln(Format('dwFlags = %d', [dwFlags]));
              Writeln(Format('cProvParams = %d', [cProvParams]));
              //Writeln(Format('rgProvParam', [rgProvParam]));
              Writeln(Format('dwKeySpec = %d', [dwKeySpec]));
            end;
            Writeln(Format('KEY_PROV_INFO: %d', [@Data[0]]));
          end;
          CERT_FRIENDLY_NAME_PROP_ID:
          begin
            CertGetCertificateContextProperty(CertContext, CertPropId,
              @Data[0], DataLen);
            Writeln(Format('FRIENDLY_NAME: %s', [PChar(@Data[0])]));
          end;
          CERT_KEY_IDENTIFIER_PROP_ID:
          begin
            CertGetCertificateContextProperty(CertContext, CertPropId,
              @Data[0], DataLen);
            Write('KEY_IDENTIFIER: ');
            for i := 1 to DataLen do
              Write(Format('%.2x ', [PBYTE(@Data[i - 1])^]));
            Writeln;
          end;
          CERT_SHA1_HASH_PROP_ID:
          begin
            CertGetCertificateContextProperty(CertContext, CertPropId,
              @Data[0], DataLen);
            Write('SHA1_HASH: ');
            for i := 1 to DataLen do
              Write(Format('%.2x ', [PBYTE(@Data[i - 1])^]));
            Writeln;
          end;
          CERT_MD5_HASH_PROP_ID:
          begin
            CertGetCertificateContextProperty(CertContext, CertPropId,
              @Data[0], DataLen);
            Write('MD5_HASH: ');
            for i := 1 to DataLen do
              Write(Format('%.2x ', [PBYTE(@Data[i - 1])^]));
            Writeln;
          end;
          else
          end;
          CertPropId := CertEnumCertificateContextProperties(CertContext,
            CertPropId);
        end;
        CertContext := CertEnumCertificatesInStore(hStore, CertContext);
      end;
//      if GetLastError <> CRYPT_E_NOT_FOUND then
//        RaiseLastWin32Error;
    finally
      CertCloseStore(hStore, 0);
    end;
  except
    on E: Exception do
    begin
      ExitCode := 1;
      Writeln(Format('[%s] %s', [E.ClassName, E.Message]));
    end;
  end;
end.
person Ondrej Kelle    schedule 17.11.2011

Вы можете делать что угодно с SecureBlackBox от EldoS. http://www.eldos.com/sbb/

Это программное обеспечение делает его легким. Это не легко.

person Chris Thornton    schedule 17.11.2011
comment
Я согласен с Крисом, я имел честь работать с более ранними версиями, и это было очень приятно. Однако у меня больше нет к нему доступа. Если ваш бюджет позволяет, купите это и сделайте свою жизнь проще. Это также усложнилось из-за их нескольких пакетов, поэтому поговорите с ними, чтобы узнать, какой именно вам нужен. Он поставляется с версией fpc. - person Marcus Adams; 17.11.2011
comment
Пакет PKIBlackBox — это то, что вам нужно. Тем не менее, вы должны просто пойти дальше и получить полный гуакамоле SecureBlackBox Professional. Это недешево (более тысячи долларов), но стоит каждой копейки. - person Leonardo Herrera; 17.11.2011

Я рекомендую Eldos SecureBlackBox, но есть и бесплатная альтернатива.

Вы также можете использовать библиотеки OpenSSL. Вам понадобится полный порт OpenSSL, так как заголовки Indy OpenSSL неполные. Вам также потребуется скомпилировать базу данных общедоступных сертификатов от центров сертификации, поскольку она не включена.

С OpenSSL у вас не будет прямого доступа к хранилищу сертификатов Windows. Вы можете экспортировать сертификаты из Windows или Firefox и импортировать их.

Eldos SecureBlackBox предоставляет доступ к хранилищу сертификатов Windows, включая методы проверки подписи кода программы.

Вы можете включить двоичные файлы OpenSSL в свой продукт Windows, а OpenSSL уже установлен и доступен в Mac OS. Итак, OpenSSL подходит для FireMonkey или Lazarus.

OpenSSL довольно хорошо задокументирован, и вы можете найти множество примеров в Интернете, хотя в основном на C или C++. Мне повезло реализовать с ним все, что мне было нужно. Он имеет множество функций для чтения и записи в файлы PEM.

У SecureBlackBox еще нет версии для FireMonkey, но они говорят, что она будет у них в следующем году (согласно их форуму). У них есть версия fpc (бесплатный паскаль).

person Marcus Adams    schedule 17.11.2011