Sample code for 30+ languages & platforms
Pascal (Lazarus/Delphi)

Duplicate SQL Server ENCRYPTBYPASSPHRASE

See more Encryption Examples

Demonstrates how to duplicate SQL Server's ENCRYPTBYPASSPHRASE.

Chilkat Pascal (Lazarus/Delphi) Downloads

Pascal (Lazarus/Delphi)
program ChilkatDemo;

// Demonstrates using the Chilkat Pascal wrapper via the C bridge DLL.
// Builds as a console application under Lazarus (FPC) or Delphi.

{$IFDEF FPC}
  {$MODE DELPHI}
{$ENDIF}
{$APPTYPE CONSOLE}

uses
  {$IFDEF UNIX}
  cthreads,
  {$ENDIF}
  SysUtils,
  CkDllLoader,
  Chilkat.StringBuilder,
  Chilkat.Prng,
  Chilkat.BinData,
  Chilkat.Crypt2;

// ---------------------------------------------------------------------------

procedure RunDemo;
var
  password: string;
  encryptedHex_v1: string;
  encryptedHex_v2: string;
  sbEncHex: TStringBuilder;
  crypt: TCrypt2;
  v1: Boolean;
  ivLen: Integer;
  hashAlg: string;
  ivHex: string;
  sbPassword: TStringBuilder;
  pwd_hash: string;
  sbKey: TStringBuilder;
  bd: TBinData;
  plainText: string;
  encryptor: TCrypt2;
  prng: TPrng;
  plainTextLen: Integer;
  bdData: TBinData;
  sbEnc: TStringBuilder;

begin
  //  This example requires the Chilkat API to have been previously unlocked.
  //  See Global Unlock Sample for sample code.

  //  For SQL Server 2008 - SQL Server 2016 we must use TripleDES with SHA1
  //  For SQL Server 2017 and later, use AES256 / SHA256.

  password := 'tEst1234';
  encryptedHex_v1 := '0x010000001E8E7DCDBD4061B951999E25D18445D2305474D2D71EEE98A241C755246F58AB';

  //  Here's an encrypted string using AES256/SHA256
  encryptedHex_v2 := '0x02000000FFE880C0354780481E64EF25B6197A02E2A854A4BA9D8D9BDDFDAB27EB56537ABDA0B1D9C4D1050C91B313550DECF429';

  sbEncHex := TStringBuilder.Create;
  sbEncHex.Append(encryptedHex_v1);

  //  If present, we don't want the leading "0x"
  if (sbEncHex.StartsWith('0x',False) = True) then
    begin
      sbEncHex.RemoveCharsAt(0,2);
    end;

  crypt := TCrypt2.Create;
  crypt.EncodingMode := 'hex';

  //  The encrypted hex string will begin with either 01000000 or 02000000
  //  version 1 is produced by SQL Server 2008 to SQL Server 2016, and we must use TripleDES with SHA1
  //  version 2 is for SQL Server 2017 and later, and uses AES256 / SHA256.
  v1 := sbEncHex.StartsWith('01',False);

  ivLen := 0;

  if (v1 = True) then
    begin
      crypt.CryptAlgorithm := '3des';
      crypt.CipherMode := 'cbc';
      crypt.KeyLength := 168;
      ivLen := 8;
      hashAlg := 'sha1';
    end
  else
    begin
      crypt.CryptAlgorithm := 'aes';
      crypt.CipherMode := 'cbc';
      crypt.KeyLength := 256;
      ivLen := 16;
      hashAlg := 'sha256';
    end;

  //  Remove the SQL Server version info (i.e. the "01000000")
  sbEncHex.RemoveCharsAt(0,8);

  //  Get the IV part of the sbEncHex, and also remove it from the StringBuilder.
  ivHex := sbEncHex.GetRange(0,ivLen * 2,True);
  WriteLn('IV = ' + ivHex);
  crypt.SetEncodedIV(ivHex,'hex');

  sbPassword := TStringBuilder.Create;
  sbPassword.Append(password);
  pwd_hash := sbPassword.GetHash(hashAlg,'hex','utf-16');
  sbKey := TStringBuilder.Create;
  sbKey.Append(pwd_hash);
  if (v1 = True) then
    begin
      //  For v1, we only want the 1st 16 bytes of the 20 byte hash.
      //  (remember, the hex encoding uses 2 chars per byte, so we remove the last 8 chars)
      sbKey.Shorten(8);
    end;

  WriteLn('crypt key: ' + sbKey.GetAsString());

  crypt.SetEncodedKey(sbKey.GetAsString(),'hex');

  //  Decrypt
  bd := TBinData.Create;
  bd.AppendEncoded(sbEncHex.GetAsString(),'hex');
  crypt.DecryptBd(bd);

  //  The result is composed of a header of 8 bytes which we can discard.
  //  The remainder is the decrypted text.

  //  The header we are discarding is composed of:
  //  Bytes 0-3: Magic number equal to 0DF0ADBA
  //  Bytes 4-5: Number of integrity bytes, which is 0 unless an authenticator is used. We're assuming no authenticator is used.
  //  Bytes 6-7: Number of plain-text bytes. We really don't need this because the CBC padding takes care of it.

  //  Therefore, just return the data after the 1st 8 bytes.
  //  Assuming the encrypted string was utf-8 text...
  bd.RemoveChunk(0,8);
  plainText := bd.GetString('utf-8');
  WriteLn('decrypted plain text: ' + plainText);

  //  The output:

  //  IV = 1E8E7DCDBD4061B9
  //  crypt key: 710B9C2E61ACCC9570D4112203BD9738
  //  decrypted plain text: Hello world.

  //  ------------------------------------------------------------------------------------------
  //  To encrypt, do the reverse...

  //  Let's do v1 with TripleDES with SHA1

  encryptor := TCrypt2.Create;
  encryptor.EncodingMode := 'hex';

  encryptor.CryptAlgorithm := '3des';
  encryptor.CipherMode := 'cbc';
  encryptor.KeyLength := 168;

  //  Generate a random 8-byte IV
  prng := TPrng.Create;
  ivHex := prng.GenRandom(8,'hex');
  encryptor.SetEncodedIV(ivHex,'hex');

  //  The binary password is generated the same as above.
  //  We'll use the same password (and same binary password)
  encryptor.SetEncodedKey(sbKey.GetAsString(),'hex');

  plainTextLen := 8;
  plainText := 'ABCD1234';

  //  Encrypt the header + the plain-text.
  bdData := TBinData.Create;
  bdData.AppendEncoded('0DF0ADBA','hex');
  bdData.AppendEncoded('0000','hex');
  bdData.AppendInt2(plainTextLen,True);
  WriteLn('header: ' + bdData.GetEncoded('hex'));
  bdData.AppendString(plainText,'utf-8');
  encryptor.EncryptBd(bdData);

  //  Compose the result..
  sbEnc := TStringBuilder.Create;
  sbEnc.Append('0x01000000');
  sbEnc.Append(ivHex);
  sbEnc.Append(bdData.GetEncoded('hex'));

  WriteLn('result: ' + sbEnc.GetAsString());


  sbEncHex.Free;
  crypt.Free;
  sbPassword.Free;
  sbKey.Free;
  bd.Free;
  encryptor.Free;
  prng.Free;
  bdData.Free;
  sbEnc.Free;

end;

// ---------------------------------------------------------------------------

begin

  try
    RunDemo;
  except
    on E: Exception do
      WriteLn('Unhandled exception: ', E.ClassName, ': ', E.Message);
  end;

  WriteLn;
  {$IFDEF MSWINDOWS}
  WriteLn('Press Enter to exit...');
  ReadLn;
  {$ENDIF}
end.