Корневой сертификат Indy 10 SSL

Я пытаюсь проверить сертификат сервера. Я использую Indy 10 и OpenSSL. Указываю Root RootCertFile и VerifyDepth на MaxInt. OnVerifyPeer работает нормально — AOk есть true. Интересно, как загрузить сертификаты из доверенных корневых центров сертификации Windows. Вот мой урезанный код клиента:

uses
  {Delphi}
  IdSSLOpenSSL
  , IdHTTP
  , IdHeaderList
  , System.Classes
  {Project}
  ;

type
  TUnicodeHTTPPoster = class
  strict private
    FidHTTP: TIdHTTP;
    FLastError: string;
    FCertPassword: string;

    procedure OnGetPassword(var Password: string);
    function OnVerifySSLPeer(Certificate: TIdX509;AOk: Boolean; ADepth, AError: Integer): Boolean;
  public
    constructor Create(const ASSLVersion: TIdSSLVersion; const AAccept: string = 'application/xml';
      const ACharSet: string = 'utf-8'; const ACertFile: string = ''; const AKeyFile: string = '';
      const ACertPassword: string = ''); reintroduce;
    destructor Destroy; override;

    function Post(const ACustomHeaders: TIdHeaderList; const ARawBody: TStream;
      const AURL: string; out AResponse: string): integer;
  end;

implementation

uses
  {Delphi}
  System.SysUtils
  , IdURI
  , IdGlobal
  {Project}
  ;

constructor TUnicodeHTTPPoster.Create(const ASSLVersion: TIdSSLVersion; const AAccept: string = 'application/xml';
  const ACharSet: string = 'utf-8'; const ACertFile: string = ''; const AKeyFile: string = '';
  const ACertPassword: string = '');
var
  _IdSSLIOHandlerSocketOpenSSL: TIdSSLIOHandlerSocketOpenSSL;
begin
  inherited Create;

  FidHTTP := TIdHTTP.Create(nil);
  FidHTTP.Request.Accept := 'application/xml';
  if AAccept <> '' then
    FidHTTP.Request.Accept := AAccept;

  FidHTTP.Request.Charset := 'utf-8';
  if ACharSet <> '' then
    FidHTTP.Request.Charset := ACharSet;

  _IdSSLIOHandlerSocketOpenSSL := TIdSSLIOHandlerSocketOpenSSL.Create(FidHTTP);

  if FileExists(ACertFile) then
    _IdSSLIOHandlerSocketOpenSSL.SSLOptions.CertFile := ACertFile;

  if FileExists(AKeyFile) then
    _IdSSLIOHandlerSocketOpenSSL.SSLOptions.KeyFile := AKeyFile;

  FCertPassword := ACertPassword;

  FidHTTP.Request.BasicAuthentication := False;
  _IdSSLIOHandlerSocketOpenSSL.SSLOptions.Mode := sslmClient;
  _IdSSLIOHandlerSocketOpenSSL.SSLOptions.Method := ASSLVersion;
  _IdSSLIOHandlerSocketOpenSSL.OnGetPassword := OnGetPassword;
  _IdSSLIOHandlerSocketOpenSSL.SSLOptions.VerifyMode := [sslvrfPeer];
  _IdSSLIOHandlerSocketOpenSSL.SSLOptions.VerifyDepth := MaxInt;
  _IdSSLIOHandlerSocketOpenSSL.OnVerifyPeer := OnVerifySSLPeer;
  _IdSSLIOHandlerSocketOpenSSL.SSLOptions.RootCertFile := 'C:\Users\ekolesnikovics\Desktop\Projects\nDentity\ndentify\Build\dc_ofisas.nsoft.lt.pem';
  FidHTTP.IOHandler := _IdSSLIOHandlerSocketOpenSSL;
end;

function TUnicodeHTTPPoster.OnVerifySSLPeer(Certificate: TIdX509;AOk: Boolean; ADepth, AError: Integer): Boolean;
begin
  Result := AOk;
end;

procedure TUnicodeHTTPPoster.OnGetPassword(var Password: string);
begin
  Password := FCertPassword;
end;

function TUnicodeHTTPPoster.Post(const ACustomHeaders: TIdHeaderList; const ARawBody: TStream;
  const AURL: string; out AResponse: string): integer;
var
  _URL: string;
  _ResponseStream: TStringStream;
begin
  Result := 500;
  FLastError := '';

  try
    if Trim(AURL) = '' then
      raise EArgumentException.Create('URL is not provided.');

    _URL := TIdURI.URLEncode(AURL, IndyTextEncoding_UTF8);
    _ResponseStream := TStringStream.Create('', TEncoding.UTF8);
    try
      if Assigned(FidHTTP.Request.CustomHeaders) then
        FidHTTP.Request.CustomHeaders.Clear;

      if Assigned(ACustomHeaders) then
        FidHTTP.Request.CustomHeaders := ACustomHeaders;

      FidHTTP.Post(_URL, ARawBody, _ResponseStream);
      _ResponseStream.Position := 0;
      AResponse := _ResponseStream.DataString;
    finally
      FreeAndNil(_ResponseStream);
    end;

    Result := 200;
  except
    on E: EIdHTTPProtocolException do
    begin
      Result := E.ErrorCode;
      FLastError := E.ErrorMessage;
      FidHTTP.Disconnect;
    end;

    on E: Exception do
    begin
      FLastError := E.Message;
      FidHTTP.Disconnect;
    end;
  end;
end;

«Интересно, как загрузить сертификаты из доверенных корневых центров сертификации Windows» — OpenSSL не поддерживает хранилища сертификатов Windows, а только собственное хранилище. Итак, вам придется экспортировать сертификаты Windows и импортировать их в OpenSSL. Это можно сделать, но это не тривиально. Indy не предоставляет функциональность, позволяющую сделать это за вас, вам придется напрямую использовать OpenSSL API.

Remy Lebeau 11.12.2020 18:16
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
В предыдущей статье мы завершили установку базы данных, для тех, кто не знает.
3
1
487
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В итоге я использовал бесплатные https://github.com/magicxor/WinCryptographyAPI

procedure TUnicodeHTTPPoster.ExportWindowsCertificateStoreToFile(const ACertFile: string);
var
  _hStore: HCERTSTORE;
  _CertContext: PCertContext;
  _pchString: Cardinal;
  _szString: string;
  _CertList: TStringList;
begin
  _hStore := CertOpenSystemStore(0, PChar('ROOT'));
  if (_hStore = nil) then
    RaiseLastOSError;

  _CertList := TStringList.Create;
  try
    _CertContext := CertEnumCertificatesInStore(_hStore, nil);
    if (_CertContext = nil) then
      RaiseLastOSError;
    while _CertContext <> nil do
    begin
      _pchString := 0;
      if not CryptBinaryToString(_CertContext.pbCertEncoded,
        _CertContext.cbCertEncoded, CRYPT_STRING_BASE64, nil, _pchString) then
        RaiseLastOSError;

      SetLength(_szString, 0);
      SetLength(_szString, _pchString - 1);
      if not CryptBinaryToString(_CertContext.pbCertEncoded,
        _CertContext.cbCertEncoded, CRYPT_STRING_BASE64, PWideChar(_szString),
        _pchString) then
        RaiseLastOSError;

      _CertList.Add('-----BEGIN CERTIFICATE-----');
      _CertList.Add(Trim(StrPas(PWideChar(_szString))));
      _CertList.Add('-----END CERTIFICATE-----');

      _CertContext := CertEnumCertificatesInStore(_hStore, _CertContext);
    end;

    _CertList.SaveToFile(ACertFile);
  finally
    FreeAndNil(_CertList);
    CertCloseStore(_hStore, 0);
  end;
end;

_RootCertFileName := TPath.Combine(ExtractFilePath(ParamStr(0)), 'windows_cert.pem');
ExportWindowsCertificateStoreToFile(_RootCertFileName);
_IdSSLIOHandlerSocketOpenSSL.SSLOptions.RootCertFile := _RootCertFileName;

Другие вопросы по теме

Похожие вопросы

Приложения эмулятора Android не подключены к сети после импорта сертификата SSL
Как создать сертификаты Java (хранилище ключей и хранилище доверенных сертификатов) для двустороннего SSL, начиная с сертификатов X509
Почему я не могу подключить приложение Quarkus к Keycloak с помощью пользовательского сертификата?
Запрос Python с ошибкой сертификатов. Вызвано SSLError(SSLError(9, '[SSL] PEM lib (_ssl.c:4027)
Не удалось добавить SSL-сертификат, ошибка: 183, невозможно создать файл, если этот файл уже существует
Каковы новые требования к сертификатам в Chrome?
Невозможно защитить веб-запрос с помощью самозаверяющего сертификата из основного приложения .NET внутри AWS Lambda
Зашифровано ли приветственное сообщение сервера в протоколе рукопожатия TLS?
Клиент извлекает сертификат SSL/TLS из соединения с реактором-netty
Ошибка urllib.requiest.urlopen: сбой проверки сертификата в виртуальной среде python