I recently needed to implement RSA cryptography in Delphi, sticking to a few requirements of my own choosing:
1. The library used should be free
2. The library license should be permissive enough that I can use the library in a commercial application
3. The library should have as few extra files to include as possible
I looked initially at LockBox, but it came with a multitude of files to either copy to my source folder or include in my library path. I don't want other developers in my team to have to add yet more third party libraries, so I gave this a miss. Additionally, the documentation wasn't great and finding example online was quite tricky.
I then tried PGPCrypt, but could only find a version of that for Lazarus, the free Delphi compiler.
Next was the TMS Cryptography pack, but this isn't free, so again a non-starter.
Then I thought about OpenSSL. We already use OpenSSL as a DLL for a different, but related, purpose so the file is already present in our source and program folder. What we don't have are function headers as we're calling the DLL functions by importing them directly (we only use three at present), but RSA is more complicated and uses a particular structure that would need replicating in Delphi. I thought this might mean OpenSSL was also a non-starter, but after much searching online I came across the Delphi-OpenSSL project at https://github.com/UniGe/delphiopenssl/ which consists really of only three .pas files, only one of which is necessary for what I'm trying to implement: RSA encryption and decryption. For this purpose I need use only the LibEAY32.pas file coupled with the LibEAY32.dll file. We're using version 1.0.2 of this, and the Delphi-OpenSSL project is compatible with that. Great!
I needed to make a few minor changes, as despite the programmers' best efforts it seemed confused about the difference between Char and AnsiChar in Delphi 2009, but the changes were minimal and consisted, almost entirely, of me replacing references to PChar or PCharacter with PAnsiChar.
The next challenge was to see if I could use an existing key to encrypt a file.
I added my modulus, public exponent and private exponent (not needed for encryption) as Base64 encoded string constants and used:
_rsa = RSA_New();
to create a new RSA structure to populate with the key information. This actually returns a pointer to an RSA object, so it needs both dereferencing and casting when used. The properties we're interested in are the modulus, n; the public exponent, e; and the private exponent, d (when decrypting, later).
These properties take the form of BigNum objects as they can be sometimes thousands of bits long. The BigNums need to be created before being used:
RSA(_rsa^).n := BN_New();
RSA(_rsa^).e := BN_New();
The Base64 values were then decoded and altered, one byte at a time, to hex using IntToHex (which returns a string) and then BN_HexToBN was used to set the RSA record's properties accordingly:
BN_HexToBN(RSA(_rsa^).n, ansistring(public_modulus));
BN_HexToBN(RSA(_rsa^).e, ansistring(public_exponent));
The input string can then be encrypted by calling:
enc_in := SysUtils.TEncoding.ASCII.GetBytes(string(input_text));
to get the string character values as a byte array, followed by:
RSA_Public_Encrypt(length(enc_in),PAnsiChar(enc_in), PAnsiChar(@textout),_rsa,RSA_PKCS1_PADDING);
to do the actual encryption. The output cypher text is held in textout. This will look like garbage but can be converted to a Base64 string for convenient storage.
And that's it.
Two caveats:
I had to add an extra property to the RSA record to match the version of OpenSSL we're using. This was:
engine: pointer;
below the meth property.
And I had to initialise the SSL library by calling:
SSLEay_version(_SSLEAY_VERSION);
before making any calls to encryption or decryption routines. There's probably a better way to do this, but this did the trick.
I put this information here in the hope that it helps someone else who struggled as much as I did to implement this!