Can Indy load SSL certificates from memory?
I have a client application that needs to authenticate to the server using certificates. Communication is SSL over TCP (not related to http / https). It works for me when I use the private key file and set it to an I / O handler etc.
I wish I would not make the private key file on disk on the client side. Instead, I would like to somehow expose the Indy secret key via memory.
I haven't been able to find a way to do this, so now I'm wondering if this is possible at all?
Clarification: My plan is to include the private key in the encrypted form as a ressource in the .exe file. Decrypt it in memory and then pass it to Indy via a stream or buffer. So the question really is that Indy supports this in some way.
source to share
At this time, Indy does not provide a function for loading certificates from memory. There is an open ticket for this function request:
Support for loading OpenSSL certificates / data from custom memory http://code.google.com/p/indyproject/issues/detail?id=196 http://indy.codeplex.com/workitem/21143
However, OpenSSL does support it, and under D2009 + on Windows Indy use this feature to load certificate files using UTF-16 filenames which OpenSSL does not support (on * Nix systems it does support UTF-8 filenames, though). Indy loads files into memory and uses the same parsing functions that OpenSSL uses. So what you are asking may just not be straight forward.
Here's an example. These are Indy
and (based on UTF-16) functions
cause the download of the private key file specified in the property
function TIdSSLContext.LoadKey: Boolean; begin if PosInStrArray(ExtractFileExt(KeyFile), ['.p12', '.pfx'], False) <> -1 then begin Result := IndySSL_CTX_use_PrivateKey_file_PKCS12(fContext, KeyFile) > 0; end else begin Result := IndySSL_CTX_use_PrivateKey_file(fContext, KeyFile, SSL_FILETYPE_PEM) > 0; end; if Result then begin Result := SSL_CTX_check_private_key(fContext) > 0; end; end; function IndySSL_CTX_use_PrivateKey_file_PKCS12(ctx: PSSL_CTX; const AFileName: String): TIdC_INT; var LM: TMemoryStream; B: PBIO; LKey: PEVP_PKEY; LCert: PX509; P12: PPKCS12; CertChain: PSTACK_OF_X509; LPassword: array of TIdAnsiChar; LPasswordPtr: PIdAnsiChar; begin Result := 0; LM := nil; try LM := TMemoryStream.Create; LM.LoadFromFile(AFileName); except // Surpress exception here since it going to be called by the OpenSSL .DLL // Follow the OpenSSL .DLL Error conventions. SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE, ERR_R_SYS_LIB); LM.Free; Exit; end; try B := BIO_new_mem_buf(LM.Memory, LM.Size); if not Assigned(B) then begin SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE, ERR_R_BUF_LIB); Exit; end; try SetLength(LPassword, MAX_SSL_PASSWORD_LENGTH+1); LPassword[MAX_SSL_PASSWORD_LENGTH] := TIdAnsiChar(0); LPasswordPtr := PIdAnsiChar(LPassword); if Assigned(ctx^.default_passwd_callback) then begin ctx^.default_passwd_callback(LPasswordPtr, MAX_SSL_PASSWORD_LENGTH, 0, ctx^.default_passwd_callback_userdata); // TODO: check return value for failure end else begin // TODO: call PEM_def_callback(), like PEM_read_bio_X509() does // when default_passwd_callback is nil end; P12 := d2i_PKCS12_bio(B, nil); if not Assigned(P12) then begin SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE, ERR_R_PKCS12_LIB); Exit; end; try CertChain := nil; if PKCS12_parse(P12, LPasswordPtr, LKey, LCert, @CertChain) <> 1 then begin SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_PKCS12_LIB); Exit; end; try Result := SSL_CTX_use_PrivateKey(ctx, LKey); finally sk_pop_free(CertChain, @X509_free); X509_free(LCert); EVP_PKEY_free(LKey); end; finally PKCS12_free(P12); end; finally BIO_free(B); end; finally FreeAndNil(LM); end; end; function IndySSL_CTX_use_PrivateKey_file(ctx: PSSL_CTX; const AFileName: String; AType: Integer): TIdC_INT; var LM: TMemoryStream; B: PBIO; LKey: PEVP_PKEY; j: TIdC_INT; begin Result := 0; LM := nil; try LM := TMemoryStream.Create; LM.LoadFromFile(AFileName); except // Surpress exception here since it going to be called by the OpenSSL .DLL // Follow the OpenSSL .DLL Error conventions. SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE, ERR_R_SYS_LIB); LM.Free; Exit; end; try B := BIO_new_mem_buf(LM.Memory, LM.Size); if not Assigned(B) then begin SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE, ERR_R_BUF_LIB); Exit; end; try case AType of SSL_FILETYPE_PEM: begin j := ERR_R_PEM_LIB; LKey := PEM_read_bio_PrivateKey(B, nil, ctx^.default_passwd_callback, ctx^.default_passwd_callback_userdata); end; SSL_FILETYPE_ASN1: begin j := ERR_R_ASN1_LIB; LKey := d2i_PrivateKey_bio(B, nil); end; else SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE, SSL_R_BAD_SSL_FILETYPE); Exit; end; if not Assigned(LKey) then begin SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE, j); Exit; end; Result := SSL_CTX_use_PrivateKey(ctx, LKey); EVP_PKEY_free(LKey); finally BIO_free(B); end; finally FreeAndNil(LM); end; end;
As you can see, Indy loads the certificate file in
, extracts from it
and passes it to the OpenSSL functions
to load the key into the
object session (Indy does not support RSA / ASN1 private keys at this time). Thus, you will have to adopt similar logic in your own code. Of course, you will need to get your code to run at the right time, and for that you will need to modify the Indy source code to add the code to
and then recompile Indy. At least until Indy provides native access in a future release.
source to share
If the Indy code accepts a stream, you can easily load whatever you could load from the file from
instead, but that doesn't solve the question of how you load the certificate
in the first place.
What it all boils down to is that your program will have to download the certificate at some point, either from disk or from the network, before it can get it into memory. You can embed it as a resource in the EXE itself and load it using TResourceStream, but it is still considered to be on disk because it is part of the EXE. And downloading it over the network means you have a server somewhere serving your private key, so it's not a good idea for obvious reasons. In fact, there is no way to use your private key on disk; just make sure your server is configured in such a way that it cannot serve this file as a download.
source to share