Nasreddine Galfout
Nasreddine Galfout

Reputation: 2591

Using OpenSSL to Generate KeyPairs

I'm using delphiopenssl wrapper to generate .pem format key files. I'm using Generate RSA Key example to generate these keys.

What I need

Two days ago I was hoping to find a simple way to generate RSA keys and use them to encrypt/dcrypt some strings or TBytes Buffer. Now after searching every possible solution I decided to use OpenSSL todo the job

My problem

the thing is I cant create the files with inputted file name to the function. But I still get a file named 'C' or 'm' (without an extension) that contains the public and private key together in PEM format and what is weirder is I'm giving the function two file names for Public and Private key

What I tried

this the code responsible

procedure GenerateKeyPair;
var
  kp: TKeyPairGenerator;
begin
kp := TKeyPairGenerator.Create;
kp.KeyFileNames(GetCurrentDir + '\mykeys');  // it create a pair c:\temp\mykeys.key
                                    // and c:\temp\mykeys.pub
kp.Password := 'mypasswd';          // Required
kp.GenerateRSA;
end;


procedure TMainForm.Button2Click(Sender: TObject);
begin
  InitOpenSSL;

  GenerateKeyPair;

  FreeOpenSSL;
end;

function TKeyPairGenerator.GenerateRSA: Integer;
var
  rsa: pRSA;
  PrivateKeyOut, PublicKeyOut, ErrMsg: pBIO;
  buff: array [0..1023] of char;
  enc: pEVP_CIPHER;

begin

Result := 0;

if (fPrivateKeyFile = '') or (fPublicKeyFile = '') then
  raise EOpenSSL.Create('Key filenames must be specified.');
if (fPassword = '') then
  raise EOpenSSL.Create('A password must be specified.');

ERR_load_crypto_strings;
OpenSSL_add_all_ciphers;

enc := EVP_des_ede3_cbc; ///??????

// Load a pseudo random file
RAND_load_file(PAnsiChar(fSeedFile), -1);

rsa := RSA_generate_key(fKeyLength, RSA_F4, nil, ErrMsg);
if rsa=nil then
  begin
  BIO_reset(ErrMsg);
  BIO_read(ErrMsg, @buff, 1024);
  raise EOpenSSL.Create(PChar(@buff));
  end;

PrivateKeyOut := BIO_new(BIO_s_file());
if BIO_write_filename(PrivateKeyOut, PAnsiChar(fPrivateKeyFile)) <> 0 then Result := - 1; // I get a 1 here meaning failure whit out any useful info
PublicKeyOut := BIO_new(BIO_s_file());
if BIO_write_filename(PublicKeyOut, PAnsiChar(fPublicKeyFile)) <> 0 then Result := ERR_peek_last_error; // ERR_peek_last_error returns 0

PEM_write_bio_RSAPrivateKey(PrivateKeyOut, rsa, enc, nil, 0, nil, PChar(fPassword));
PEM_write_bio_RSAPublicKey(PublicKeyOut, rsa);

if rsa <> nil then RSA_free(rsa);
if PrivateKeyOut <> nil then BIO_free_all(PrivateKeyOut);
if PublicKeyOut <> nil then BIO_free_all(PublicKeyOut);
end;

and this is the content of the file named C

-----BEGIN RSA PUBLIC KEY-----
MIIBBgKB/gC1aSSL+rlH/owIISeoNNO9mVmlPfWVsnRloFUHlYQMZyVovcTHZZhd
CvweTjMlwRHTqNAnX3CpFSwjcf5FVyiB7qoWQHDXlTSLD4kFQzUfGVTorwuB1jii
Su3tt3GCJE//xE5RWrsAIETuxIk2ZSkf4T0htAu44gBbup7CT4cSOaUeTr6/D9WL
xl+jGCi9d4oG+JkVJ21VHl1O5/UG4HRKiKx+PfNrBZvR4COVzYV6clXv7fd2EZo7
Gbz/d3yUG9jVMuQmbSDA0Ew3vE9iYTIpXeGSM1aZKgkOWqehO7b8yIqhmUbW2Yl5
sydL/xx7WEsQuTqvPST1lkpfdyIpAgMBAAE=
-----END RSA PUBLIC KEY-----
xng1PPL79FUIjo1i3Fjg1qagYELOy023nnekp9ZzgPrVsuZT4fnXTaqFHoOjhTr
IqhHVMsaUIxG0OOdmkDZzWbHjlJbA3BpNvB0NqSlb8vQrg+d9Tq4wh4heKNl/Wim
IocSUi3qULEC29H2rA2VnutilcpDTcc4fiKwWlAnYGOQieVHMnNGP+RrCqjIzurN
M4aTK7mRna3OGYOZBl89xDd9elmYtToFrb/aVEgE2FY3190AosRyb/9bjR+ol39Y
XrtXKAPOwGum6O9Vc5jTAw6wC6OTpCTZXw6NuIm6WST0u5Aknvc0mGEx/w8yYxil
fJvavRADkWIBYLvWj5tzm2pOeT3C1SoCqtEwmpK4eZpR+TeODsje4blaGIeIMtOl
BOnGZSy8KLk36kH2mT6pO3/QHNK7yZEhyd9uw7Hol8pFk8ZrCPu25vju8UnC0/k6
djExrjl2+V/EGeO8k79W2kpHjIIcY5SG+hNI909j/OIwJH8UJCmQrSPaxSloUquy
upQPhPhaz1UKrZJw/u7oKHzeYPFI9NmWBbz18Iax52wsmHaw/mqwt8h96wUujVB2
CWG0IfW+LF7r6rZ6wREW72ldMLiKpGAucbaGBpGKbMLpbai9BTmkpdtpi/PH7yOG
8/gS2Av6VvD2wzdNt94Zqqlz+qN3K/t4qIjOIBHSpCcm7DJLQWK452xheZpiYlP0
hBAX7V9WvnQcuhfiX0zm097dmqcjySzpKL53T7dk3dNoHsbPPzKrS/WDMvGZLMwF
4hifva+O3lnI/DlBZymcPNlPNwTTztDDWxFwv2Y9jq20yXbJLjEP1Qjpk+oS9dVh
HTTun0ZkrfzVaXcfcWggKWrpiTIen2jCqqVSMyS0xe4h9v+/gjkMA54OzN+zPyIH
zpgRmHxVIXYQ+AuK1zEf6lZaeeUiKWG4ywpgML3X4Ln5SWNZA0iLYQKr98XDn3VJ
WoVA1sVqsi2cuq+9Td2Z9TbD4FCxZlrrOZCN5x+YaMjp+KzFA5m+7rEvS96Z+Kit
Pw1mZkrQ2QioXOmkDiqypFk08Z8BiPIb+hklXrrD7Vkp3VdMO9UQpKppfZFMQ0mG
6OGcf51kBKtfEPcHEBkQM/sPw5H4zC+pRaxBseL/5Fzcq/B5ywPzEjMfQc4sjpTi
uFZFA9rVzikCOEv1R8MPrdiFKzrBv7xR1SjA+W8DeTJaeXmHRTzT75rovvH2GUvP
RUMyGKfp1MXIFzyU5FA4xgPVPve2K/+P
-----END RSA PRIVATE KEY-----

My question how to solve this or Is there any docs about those methods I can read .

OpenSSL version 1.0.2 from Indy servers.

Side note: I submitted a feature request (System.Cryptography) for whom interested to vote for the cause

Upvotes: 4

Views: 2024

Answers (2)

Nasreddine Galfout
Nasreddine Galfout

Reputation: 2591

This is my solution to the problem in question

The problem: Generate an RSA Key Pairs (public/private) and then read them from file, stream or even a TBytes buffer.

Solutions Found:

  • the first thing that you can find (google) is the Turbo Pack LockBox for Delphi but the problem is that it does not support above 1048 bit key sizes (at the time of writing) which is way below the current minimum key size (2048 at the time of writing), So you could omit that from possible solutions (though it has a native implementation of what I need).
  • a Wrapper for CryptoAPI. The Idea is beautiful because you get the chance to use Microsoft own Crypto Library which will make it easier for deployment (since it is built in the OS it self). However there is little support for Pascal (it is written in c++) and would require tremendous work to support and maintain and it is not cross platform.
  • a Wrapper for OpenSSl. Now that is an awesome Idea because the library is already used by one of the great open source projects Indy and searching for other open source projects would have higher possibility of success

    1. zizzo81/delphiopenssl has its own wrapper for OpenSSL and great demos but as the question states there is a problem or two in that source code and I could not post an issue on GitHub for one good reason the project is not maintained and it is only an imported version from an old site.
    2. lminuti/Delphi-OpenSSL is based on Indy Wrappers IdSSLOpenSSLHeaders.pas and provides basic features for this problem (encrypt/decrypt but not generate).

Finale Solution:

at the end I needed to add something to lminuti/Delphi-OpenSSL for it to be completed and thanks for allen-drennan answer to this question it was very easy.

I also forked the lminuti/Delphi-OpenSSL to present the changes concerned by this question (the fork contains a ready to use tutorial for this problem)

Bugs I found and fixed the current wrapper in the base project uses PEM_read_bio_PubKey to read Public key files in Traditional PEM formats (which is not the standard anymore) also it uses PEM_write_bio_RSAPrivateKey which is deprecated as well (I did not fix this for the moment)

References I found useful:

Upvotes: 3

Allen Drennan
Allen Drennan

Reputation: 291

I instead, usually create the public and private key separately using PEM_write_bio_* APIs to create TBytes. Once you have the TBytes for the public and private key you can use Delphi's TFile.WriteAllBytes() in the System.IOUtils unit to save the TBytes to a file.

function CreateCertificate_PKCS(out APublicKey, APrivateKey: TBytes): Boolean;
var
  Bignum: PBIGNUM;
  RSA: PRSA;
  PrivateKey, PublicKey: PBIO;
  KeyLength: Integer;
begin
  Result := False;
  Bignum := BN_new();
  try
    if BN_set_word(Bignum, RSA_F4) = 1 then
    begin
      RSA := RSA_new;
      try
        if RSA_generate_key_ex(RSA, 2048, Bignum, nil) = 1 then
        begin
          { Write the public key }
          PublicKey := BIO_new(BIO_s_mem);
          try
            PEM_write_bio_RSAPublicKey(PublicKey, RSA);
            KeyLength := BIO_pending(PublicKey);
            SetLength(APublicKey, KeyLength);
            BIO_read(PublicKey, @APublicKey[0], KeyLength);
          finally
            BIO_free(PublicKey);
          end;

          { Write the private key }
          PrivateKey := BIO_new(BIO_s_mem);
          try
            PEM_write_bio_RSAPrivateKey(PrivateKey, RSA, nil, nil, 0, nil, nil);
            KeyLength := BIO_pending(PrivateKey);
            SetLength(APrivateKey, KeyLength);
            BIO_read(PrivateKey, @APrivateKey[0], KeyLength);
          finally
            BIO_free(PrivateKey);
          end;

          Result := True;
        end;
      finally
        RSA_free(RSA);
      end;
    end;
  finally
    BN_free(Bignum);
  end;
end;

Upvotes: 3

Related Questions