вторник, 11 сентября 2012 г.

SSL HTTP сервер на Indy 9 (Delphi 7)

HTTP-сервер в Indy 9 представлен компонентом TIdHTTPServer. Чтобы включить использование SSL (HTTPS), нужно у объекта TIdHTTPServer свойство IOHandler связать с объектом типа TIdServerIOHandlerSSL, в котором в поле CertFile прописать путь к файлу сертификата, а в KeyFile - путь к файлу с приватным ключом.

Файл сертификата имеет примерно такое содержимое:

Файл с приватным ключом выглядит примерно так:

Но что делать, если необходимо, чтобы сертификат и ключ были "вшиты" в приложение? Я использую следующую технику - после загрузки и инициализации библиотеки ssleay32.dll подменяю указатели на функции IdSslCtxUseCertificateFile и IdSslCtxUsePrivateKeyFile (они объявлены в IdSSLOpenSSLHeaders) на мои реализации этих функций, которые читают сертификат и ключ не из файлов, а из памяти.

unit uOpenSSL;

(*
Copyright 2012 Coolsoftware. http://blog.coolsoftware.ru/
You can freely use this code for your needs.
Please, don't remove this copyright.
*)

interface

uses Windows, SysUtils, IdSSLOpenSSL, IdSSLOpenSSLHeaders;

function MySslCtxUsePrivateKeyFile(ctx: PSSL_CTX; const _file: PChar; _type: Integer): Integer cdecl;
function MySslCtxUseCertificateFile(ctx: PSSL_CTX; const _file: PChar; _type: Integer): Integer cdecl;

type
  PBIO            = Pointer;
  PPBIO           =^PBIO;
  PBIO_METHOD     = Pointer;
  PRSA         = Pointer;

var
  MySslCtxUsePrivateKey : function(ctx: PSSL_CTX; pkey: PEVP_PKEY): 
    Integer cdecl = nil;
  MySslCtxUseCertificate : function(ctx: PSSL_CTX; x: PX509): 
    Integer cdecl = nil;

  EVP_PKEY_new : function(): PEVP_PKEY cdecl = nil;
  EVP_PKEY_free : procedure(pkey: PEVP_PKEY) cdecl = nil;

  EVP_PKEY_set1_RSA : function(pkey: PEVP_PKEY; key: PRSA) : 
    Integer cdecl = nil;

  BIO_s_mem : function () : PBIO_METHOD cdecl = nil;
  BIO_new : function(t: PBIO_METHOD): PBIO cdecl = nil;
  BIO_free : function(a: PBIO): Integer cdecl = nil;
 BIO_puts : function(bp: PBIO; buf: PChar): Integer cdecl = nil;

  RSA_new: function (): pRSA; cdecl = nil;
  RSA_free: procedure (r: pRSA); cdecl = nil;
  RSA_public_encrypt: function (flen: integer; from: PChar; _to: PChar;
    rsa: pRSA; padding: integer): integer; cdecl = nil;
  RSA_public_decrypt: function (flen: integer; from: PChar; _to: PChar;
    rsa: pRSA; padding: integer): integer; cdecl;

  d2i_RSAPrivateKey : function(a: Pointer; pp: Pointer; length: Integer): 
    PRSA cdecl = nil;

  PEM_ASN1_read_bio : function(d2: Pointer; name: PChar; bp: PBIO; x: Pointer;
    cb: Pointer; u: Pointer): PChar cdecl = nil;
  PEM_read_bio_X509 : function(bp: PBIO; x: Pointer; cb: Pointer; u: Pointer): 
    PX509 cdecl = nil;
  PEM_read_bio_RSAPublicKey: function (bp: pBIO; x: pRSA;
    cb: Pointer; u: pointer): pRSA; cdecl = nil;

implementation

uses SyncObjs;

const
  SSL_DLL_name       = 'ssleay32.dll';  {Do not localize}
  SSLCLIB_DLL_name   = 'libeay32.dll';  {Do not localize}

var
  hIdSSL    : Integer = 0;
  hIdCrypto : Integer = 0;

function LoadFunction(FceName:String):Pointer;
begin
  FceName := FceName+#0;
  Result := GetProcAddress(hIdSSL, @FceName[1]);
//  if (Result = nil) then ShowMessage('Error loading: ' + FceName);  {Do not localize}
end;

function LoadFunctionCLib(FceName:String):Pointer;
begin
  FceName := FceName+#0;
  Result := GetProcAddress(hIdCrypto, @FceName[1]);
//  if (Result = nil) then ShowMessage('Error loading: ' + FceName);  {Do not localize}
end;

const
  fn_SSL_CTX_use_RSAPrivateKey = 'SSL_CTX_use_RSAPrivateKey';  {Do not localize}
  fn_SSL_CTX_use_PrivateKey = 'SSL_CTX_use_PrivateKey';  {Do not localize}
  fn_SSL_CTX_use_certificate = 'SSL_CTX_use_certificate';  {Do not localize}

  fn_EVP_PKEY_new = 'EVP_PKEY_new';  {Do not localize}
  fn_EVP_PKEY_free = 'EVP_PKEY_free';  {Do not localize}

  fn_EVP_PKEY_set1_RSA = 'EVP_PKEY_set1_RSA';

  fn_BIO_s_mem = 'BIO_s_mem';  {Do not localize}
  fn_BIO_new = 'BIO_new';  {Do not localize}
  fn_BIO_free = 'BIO_free';  {Do not localize}
  fn_BIO_puts = 'BIO_puts';  {Do not localize}

  fn_RSA_new = 'RSA_new';  {Do not localize}
  fn_RSA_free = 'RSA_free';  {Do not localize}
  fn_RSA_public_encrypt = 'RSA_public_encrypt';  {Do not localize}
  fn_RSA_public_decrypt = 'RSA_public_decrypt';  {Do not localize}

  fn_d2i_RSAPrivateKey = 'd2i_RSAPrivateKey';  {Do not localize}

  fn_PEM_ASN1_read_bio = 'PEM_ASN1_read_bio';  {Do not localize}
  fn_PEM_read_bio_X509 = 'PEM_read_bio_X509';  {Do not localize}
  fn_PEM_read_bio_RSAPublicKey = 'PEM_read_bio_RSAPublicKey';  {Do not localize}

var
  c_PrivateKeyPassword: PChar = 'put the password of your private key here';
  c_PrivateKey: PChar = 'put your private key here';
  c_Certificate: PChar = 'put your certificate here';

  LockPassCB: TCriticalSection;

function MyPasswordCallback(buf:PChar; size:Integer; rwflag:Integer; 
  userdata: Pointer):Integer; cdecl;
begin
  LockPassCB.Acquire;
  try
    size := StrLen(c_PrivateKeyPassword);
    StrCopy(buf, c_PrivateKeyPassword);
    Result := size;
  finally
    LockPassCB.Release;
  end;
end;

function MySslCtxUsePrivateKeyFile(ctx: PSSL_CTX; const _file: PChar; 
  _type: Integer):Integer cdecl;
var
  pk: PEVP_PKEY;
  rsakey: PRSA;
  pMemBIO: PBIO;
begin
    pk := EVP_PKEY_new();
    try
      pMemBIO := BIO_new(BIO_s_mem());
      try
        BIO_puts(pMemBIO, c_PrivateKey);
        rsakey := PEM_ASN1_read_bio(
          @d2i_RSAPrivateKey, OPENSSL_PEM_STRING_RSA, pMemBIO, nil,
          @MyPasswordCallback, nil);
      finally
        BIO_free(pMemBIO);
      end;
      EVP_PKEY_set1_RSA(pk, rsakey);
      Result := MySslCtxUsePrivateKey(ctx, pk);
    finally
      EVP_PKEY_free(pk);
    end;
end;

function MySslCtxUseCertificateFile(ctx: PSSL_CTX; const _file: PChar; 
  _type: Integer):Integer cdecl;
var
  pCert: PX509;
  pMemBIO: PBIO;
begin
    pMemBIO := BIO_new(BIO_s_mem());
    try
      BIO_puts(pMemBIO, c_Certificate);
      pCert := PEM_read_bio_X509(pMemBIO, nil,
        @MyPasswordCallback, nil);
    finally
      BIO_free(pMemBIO);
    end;
    Result := MySslCtxUseCertificate(ctx, pCert);
end;

var
  ctx: TIdSSLContext;

initialization
  LockPassCB := TCriticalSection.Create;
    
  //this will load the SSL library
  ctx := TIdSSLContext.Create;
  try

    hIdCrypto := GetModuleHandle(SSLCLIB_DLL_name);
    hIdSSL := GetModuleHandle(SSL_DLL_name);

    @MySslCtxUsePrivateKey := LoadFunction(fn_SSL_CTX_use_PrivateKey);
    @MySslCtxUseCertificate := LoadFunction(fn_SSL_CTX_use_certificate);

    @EVP_PKEY_new := LoadFunctionCLib(fn_EVP_PKEY_new);
    @EVP_PKEY_free := LoadFunctionCLib(fn_EVP_PKEY_free);

    @EVP_PKEY_set1_RSA := LoadFunctionCLib(fn_EVP_PKEY_set1_RSA);

    @BIO_s_mem := LoadFunctionCLib(fn_BIO_s_mem);
    @BIO_new := LoadFunctionCLib(fn_BIO_new);
    @BIO_free := LoadFunctionCLib(fn_BIO_free);
    @BIO_puts := LoadFunctionCLib(fn_BIO_puts);

    @RSA_new := LoadFunctionCLib(fn_RSA_new);
    @RSA_free := LoadFunctionCLib(fn_RSA_free);
    @RSA_public_encrypt := LoadFunctionCLib(fn_RSA_public_encrypt);
    @RSA_public_decrypt := LoadFunctionCLib(fn_RSA_public_decrypt);

    @d2i_RSAPrivateKey := LoadFunctionCLib(fn_d2i_RSAPrivateKey);

    @PEM_ASN1_read_bio := LoadFunctionCLib(fn_PEM_ASN1_read_bio);
    @PEM_read_bio_X509 := LoadFunctionCLib(fn_PEM_read_bio_X509);
    @PEM_read_bio_RSAPublicKey := LoadFunctionCLib(fn_PEM_read_bio_RSAPublicKey);

    IdSslCtxUsePrivateKeyFile := MySslCtxUsePrivateKeyFile;
    IdSslCtxUseCertificateFile := MySslCtxUseCertificateFile;

  finally
    ctx.Free;
  end;

finalization
  LockPassCB.Free;
end.

===
Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru

Комментариев нет:

Отправить комментарий