Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
KANSoftWare

Использование PEM сертификата с NetHTTPClient в Delphi: решение проблем и обходные пути.

Delphi , Синтаксис , Шифрование

 

Введение

При работе с HTTPS-запросами в Delphi с использованием компонента TNetHTTPClient разработчики часто сталкиваются с необходимостью валидации серверных сертификатов. В этой статье мы разберем проблему, описанную пользователем MMSoft, и предложим несколько решений для корректной работы с PEM-сертификатами в Delphi, особенно на платформе Android.

Понимание проблемы

Пользователь пытается использовать PEM-сертификат с TNetHTTPClient в Delphi 12.1 для Android. В текущей реализации он просто отключает проверку сертификата через событие OnValidateServerCertificate, что не является безопасным решением.

Как правильно отметил Kas Ob., такая реализация имеет несколько недостатков:
- Отсутствие проверки сертификата делает соединение уязвимым к MITM-атакам
- Нет гарантии, что клиент соединяется именно с нужным сервером
- При изменении сертификата на сервере клиент продолжит работать, что может быть нежелательно

Решение 1: Валидация сертификата по отпечатку

Наиболее простой и достаточно надежный способ - проверять отпечаток (fingerprint) сертификата:

procedure TForm1.NetHTTPClient1ValidateServerCertificate(const Sender: TObject;
  const ARequest: TURLRequest; const Certificate: TCertificate;
  var Accepted: Boolean);
const
  VALID_CERT_THUMBPRINT = 'A1B2C3D4E5F6...'; // Замените на реальный отпечаток
begin
  Accepted := Certificate.Thumbprint = VALID_CERT_THUMBPRINT;
end;

Чтобы получить отпечаток сертификата, можно использовать OpenSSL:

openssl x509 -noout -fingerprint -sha256 -inform pem -in certificate.pem

Решение 2: Загрузка PEM-сертификата и полная проверка

Для более полной проверки можно загрузить PEM-сертификат и сравнивать его с полученным от сервера:

uses
  System.Classes, System.SysUtils, System.NetEncoding;

procedure TForm1.LoadCertificate;
var
  CertStream: TStringStream;
begin
  CertStream := TStringStream.Create;
  try
    // Загружаем PEM-файл из ресурсов или файловой системы
    CertStream.LoadFromFile(TPath.Combine(TPath.GetDocumentsPath, 'certificate.pem'));
    FCertificateData := CertStream.DataString;
  finally
    CertStream.Free;
  end;
end;

procedure TForm1.NetHTTPClient1ValidateServerCertificate(const Sender: TObject;
  const ARequest: TURLRequest; const Certificate: TCertificate;
  var Accepted: Boolean);
var
  ServerCert: string;
begin
  ServerCert := '-----BEGIN CERTIFICATE-----' + sLineBreak +
                TNetEncoding.Base64.EncodeBytesToString(Certificate.Certificate) + 
                sLineBreak + '-----END CERTIFICATE-----';

  Accepted := ServerCert = FCertificateData;
end;

Решение 3: Использование хранилища сертификатов

Более правильным подходом будет использование хранилища доверенных сертификатов. На Android можно использовать встроенное хранилище или добавить свой сертификат:

procedure TForm1.AddCustomCertificateToStore;
var
  CertFile: string;
  X509Cert: JX509Certificate;
  CertFactory: JCertificateFactory;
  Input: JInputStream;
begin
  CertFile := TPath.Combine(TPath.GetDocumentsPath, 'certificate.pem');

  if TFile.Exists(CertFile) then
  begin
    Input := TJFileInputStream.JavaClass.init(StringToJString(CertFile));
    try
      CertFactory := TJCertificateFactory.JavaClass.getInstance(StringToJString('X.509'));
      X509Cert := TJX509Certificate.Wrap(CertFactory.generateCertificate(Input));

      // Создаем KeyStore и добавляем сертификат
      FKeyStore := TJKeyStore.JavaClass.getInstance(TJKeyStore.JavaClass.getDefaultType);
      FKeyStore.load(nil, nil);
      FKeyStore.setCertificateEntry('my_cert', X509Cert);
    finally
      Input.close;
    end;
  end;
end;

Решение для Android

На платформе Android работа с сертификатами имеет свои особенности. Вот пример настройки TNetHTTPClient для Android:

procedure TForm1.ConfigureAndroidHTTPClient;
var
  SSLContext: JSSLContext;
  TrustManagerFactory: JTrustManagerFactory;
  KeyManagerFactory: JKeyManagerFactory;
  KeyStore: JKeyStore;
  TrustManagers: TJavaObjectArray<JTrustManager>;
  KeyManagers: TJavaObjectArray<JKeyManager>;
  SSLSocketFactory: JSSLSocketFactory;
begin
  // Загружаем наш KeyStore с сертификатом
  KeyStore := TJKeyStore.JavaClass.getInstance('BKS');
  KeyStore.load(nil, nil);

  // Настраиваем TrustManager
  TrustManagerFactory := TJTrustManagerFactory.JavaClass.getInstance(
    TJTrustManagerFactory.JavaClass.getDefaultAlgorithm);
  TrustManagerFactory.init(KeyStore);

  // Настраиваем KeyManager (если нужен клиентский сертификат)
  KeyManagerFactory := TJKeyManagerFactory.JavaClass.getInstance(
    TJKeyManagerFactory.JavaClass.getDefaultAlgorithm);
  KeyManagerFactory.init(KeyStore, nil);

  // Создаем SSLContext
  SSLContext := TJSSLContext.JavaClass.getInstance('TLS');
  SSLContext.init(
    KeyManagerFactory.getKeyManagers,
    TrustManagerFactory.getTrustManagers,
    nil);

  // Устанавливаем SocketFactory для NetHTTPClient
  SSLSocketFactory := SSLContext.getSocketFactory;
  NetHTTPClient1.SecureProtocols := [THTTPSecureProtocol.TLS12];
  NetHTTPClient1.ConnectionFactory.SecureSocketSettings.SSLSocketFactory := 
    TAndroidHelper.JObjectToID(SSLSocketFactory);
end;

Альтернативное решение: использование Indy

Если стандартный TNetHTTPClient вызывает сложности, можно рассмотреть альтернативу в виде компонентов Indy:

uses
  IdHTTP, IdSSL, IdSSLOpenSSL;

procedure TForm1.ConfigureIdHTTP;
var
  SSLIOHandler: TIdSSLIOHandlerSocketOpenSSL;
begin
  SSLIOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
  try
    SSLIOHandler.SSLOptions.Method := sslvTLSv1_2;
    SSLIOHandler.SSLOptions.VerifyDepth := 2;

    // Загружаем PEM-сертификат
    SSLIOHandler.SSLOptions.RootCertFile := 'certificate.pem';
    SSLIOHandler.SSLOptions.VerifyMode := [sslvrfPeer];

    IdHTTP1.IOHandler := SSLIOHandler;
    IdHTTP1.HandleRedirects := True;
  except
    SSLIOHandler.Free;
    raise;
  end;
end;

Обработка ошибок и отладка

При работе с сертификатами важно правильно обрабатывать ошибки:

procedure TForm1.NetHTTPClient1RequestError(const Sender: TObject;
  const AError: string);
begin
  TThread.Synchronize(nil,
    procedure
    begin
      Memo1.Lines.Add('Ошибка: ' + AError);
    end);
end;

procedure TForm1.NetHTTPClient1RequestException(const Sender: TObject;
  const AError: Exception);
begin
  TThread.Synchronize(nil,
    procedure
    begin
      Memo1.Lines.Add('Исключение: ' + AError.Message);
      if AError is ENetHTTPCertificateException then
        Memo1.Lines.Add('Проблема с сертификатом: ' + 
          ENetHTTPCertificateException(AError).Certificate.Thumbprint);
    end);
end;

Рекомендации по безопасности

  1. Не отключайте проверку сертификата в продакшен-коде
  2. Используйте актуальные версии протоколов (TLS 1.2 или выше)
  3. Обновляйте сертификаты при их изменении на сервере
  4. Для критических приложений реализуйте отзыв сертификатов
  5. Логируйте ошибки валидации для последующего анализа

Заключение

Работа с PEM-сертификатами в Delphi требует понимания принципов TLS-аутентификации. В статье представлено несколько подходов - от простой проверки отпечатка до полной настройки хранилища сертификатов на Android. Выбор метода зависит от требований безопасности и специфики вашего приложения.

Помните, что простое отключение проверки сертификатов (Accepted := True) - это не решение, а создание потенциальной уязвимости. Используйте представленные в статье методы для безопасной работы с HTTPS-соединениями в ваших Delphi-приложениях.

Создано по материалам из источника по ссылке.

Статья описывает методы работы с PEM-сертификатами в Delphi, включая проверку отпечатка, загрузку сертификата и использование хранилища доверенных сертификатов, а также альтернативные решения с Indy, с акцентом на безопасность и особенности работы на And


Комментарии и вопросы

Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.


:: Главная :: Шифрование ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-12-22 20:14:06
2025-06-20 06:51:49/0.006540060043335/0