Using AES decryption with JS and Delphi
This article is about using the AES classes for encrypt / decrypt passwords or other sensitive kind of data with JavaScript and Delphi when perform requests from our web page to the CGI / ISAPI controller.
To begin with you need to download the Delphi Encryption Compendium library from Github and extract the ZIP file in a folder. Make sure to add the Source directory of this library to your Delphi project’s search path. Then add the following unit references to your PAS file:
- DECCipherBase (Contains the cipher modes e.g. CBC, ECB etc)
- DECCiphers (A collection of available cipher classes)
- IdEncoderMime (Delphi Indy native library)
Write a function like the one below which should be called from your Delphi web controller when required to decrypt data from your web page:
function DecryptAesText(encryptedText: string; secretKey: string): string;
var
RCipher: TCipher_AES;
Decoder: TIdDecoderMIME;
srcstr,
dststr : TMemoryStream;
buffer : TBytes;
ii : SmallInt;
begin
Result := '';
RCipher := TCipher_AES.Create;
Decoder := TIdDecoderMIME.Create(nil);
srcstr := TMemoryStream.Create;
dststr := TMemoryStream.Create;
try
// We use 128bit encryption (=128/8)
SetLength(buffer, 16);
// Apply the UTF8 bytes of the secret key
for ii := 1 to Min(16, Length(secretKey)) do
buffer[ii - 1] := Ord(secretKey[ii]);
// Initialize AES decoder with key and IV
RCipher.Init(buffer, buffer, 0);
// We use the CBC mode but you can select another mode if you like
RCipher.Mode := cmCBCx;
// Decode the base64 encrypted text to a stream
Decoder.DecodeStream(encryptedText, srcstr);
srcstr.Position := 0;
// Decode the ciphered text with AES decoder
RCipher.DecodeStream(srcstr, dststr, srcstr.Size);
dststr.Position := 0;
SetLength(buffer, dststr.Size);
dststr.Read(buffer[0], dststr.Size);
// Transform the decoded bytes to String
Result := Trim(StringOf(buffer));
finally
FreeAndNil(RCipher);
FreeAndNil(Decoder);
FreeAndNil(srcstr);
FreeAndNil(dststr);
end;
end;
The reason we used a stream in order to get the ciphered text from the base64 input, is that the hashed text contains characters (bytes) that are not compatible with Delphi’s String class.
To do the encryption in our web page we first need to apply the following CDN reference to the header of our page:
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js" integrity="sha512-E8QSvWZ0eCLGk4km3hxSsNmGWbLtSCSUcewDQPQWZF6pEU8GlT8a5fF32wOl1i8ftdMhssTrF/OhyGWwonTcXA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
It will provide the necessary CryptoJS library that will be used for encrypting our sensitive information.
Finally you can add a JavaScript function like the one below in order to encrypt the data you want before sending them to your backend controller:
function EncryptAesText(textToEncrypt, secretKey) {
try {
var key = CryptoJS.enc.Utf8.parse(secretKey));
var txt = CryptoJS.enc.Utf8.parse(textToEncrypt);
var encrypted = CryptoJS.AES.encrypt(txt, key, { keySize: 16, iv: key, mode: CryptoJS.mode.CBC });
return encrypted.toString();
}
catch (e) {
console.log(e.message);
return "";
}
}
The above function will encrypt the “textToEncrypt” parameter with AES CBC 128 bit algorithm and will return a base64 encoded string of the encrypted text. The base64 format makes the text suitable for data transmission over the internet. Just make sure to use the same secret key and that it should not exceeds the 16 bytes in length for the 128 bit mode.
Additionally the encoded text of the previous JS function can also be decrypted by .NET functions as the following one:
public static string DecryptAesText(string encryptedText, string secretKey)
{
byte[] iv = Encoding.UTF8.GetBytes(secretKey);
byte[] buffer = Convert.FromBase64String(encryptedText);
using (var aes = Aes.Create())
{
aes.Key = iv;
aes.IV = iv;
aes.Mode = CipherMode.CBC;
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using (var memoryStream = new MemoryStream(buffer))
{
using (var cryptoStream = new CryptoStream((Stream)memoryStream, decryptor, CryptoStreamMode.Read))
{
using (var streamReader = new StreamReader((Stream)cryptoStream))
{
return streamReader.ReadToEnd();
}
}
}
}
return string.Empty;
}