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