检查两边的填充方法。AES 加密本身是标准的,但是当明文不是块长度的倍数时,不同的库使用不同的(默认)填充方法。我让它在 C# 端使用空填充,并且 a) 使用带有手动 #0 填充的 DCPCrypt(DCPCrypt 不做填充)和 b) Turbopower LockBox 3,作者 Sean Durkin 对其代码进行了扩展以处理C# 空填充。
旧 D2007 测试代码,按“原样”提供:
========== TPLB3 pas文件==============
unit uEncDecTests_LockBox;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Buttons, uTPLb_BaseNonVisualComponent,
uTPLb_Codec, uTPLb_CryptographicLibrary;
type
TFrmEncDecTests = class(TForm)
EdtPlainText1: TEdit;
EdtCypher: TEdit;
Label1: TLabel;
Label2: TLabel;
EdtPlainText2: TEdit;
Label3: TLabel;
Label4: TLabel;
BiBEncrypt: TBitBtn;
BiBDecrypt: TBitBtn;
Label5: TLabel;
EdtKey: TEdit;
Label6: TLabel;
EdtCypher64: TEdit;
EdtIV: TEdit;
Label7: TLabel;
Label8: TLabel;
EdtCypherHex: TEdit;
AESCodec: TCodec;
CryptographicLibrary1: TCryptographicLibrary;
BtnInit: TButton;
procedure BiBEncryptClick(Sender: TObject);
procedure BiBDecryptClick(Sender: TObject);
procedure BtnInitClick(Sender: TObject);
private
FOut: String;
public
{ Public declarations }
end;
var
FrmEncDecTests: TFrmEncDecTests;
implementation
{$R *.dfm}
procedure TFrmEncDecTests.BiBDecryptClick(Sender: TObject);
var lPlainText: String;
begin
AESCodec.DecryptString(FOut,lPlainText);
EdtPlainText2.Text := lPlainText;
end;
procedure TFrmEncDecTests.BiBEncryptClick(Sender: TObject);
var
lPlainText,
sb,sh: String;
b,o : byte;
begin
lPlainText := EdtPlaintext1.Text;
AESCodec.EncryptString(lPlainText,FOut);
sh := '';
sb := '';
for B := 0 to Length(FOut) do begin
o := Ord(FOut[b]);
sh := sh + IntToHex(o,2) + ' ';
if o < 10 then sb := sb + '0';
if o < 100 then sb := sb + '0';
sb := sb + inttostr(o) + ' ';
end;
EdtCypher.Text := FOut;
EdtCypher64.Text := sb;
EdtCypherHex.Text := sh;
end;
procedure TFrmEncDecTests.BtnInitClick(Sender: TObject);
var
p : PChar;
MS: TMemoryStream;
begin
p := pChar(EdtKey.Text);
MS := TMemoryStream.Create;
MS.Write(P^,Length(EdtKey.Text));
MS.Seek(soFromBeginning,0);
AESCodec.InitFromStream(MS);
MS.Free;
BiBEncrypt.Enabled := true;
BiBDecrypt.Enabled := true;
end;
end.
========== TPLB3 dfm 文件 ===============
object FrmEncDecTests: TFrmEncDecTests
Left = 0
Top = 0
Caption = 'Encryptie/decryptie tests (LockBox)'
ClientHeight = 328
ClientWidth = 535
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object Label1: TLabel
Left = 16
Top = 150
Width = 114
Height = 13
Caption = 'Cyphertext (raw bytes)'
end
object Label2: TLabel
Left = 16
Top = 102
Width = 46
Height = 13
Caption = 'Plaintext:'
end
object Label3: TLabel
Left = 16
Top = 280
Width = 46
Height = 13
Caption = 'Plaintext:'
end
object Label4: TLabel
Left = 16
Top = 16
Width = 22
Height = 13
Caption = 'Key:'
end
object Label5: TLabel
Left = 339
Top = 16
Width = 182
Height = 13
Caption = 'Testing LockBox 3 AES-256, CBC'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = [fsBold]
ParentFont = False
end
object Label6: TLabel
Left = 16
Top = 189
Width = 101
Height = 13
Caption = 'Cyphertext (Base64)'
end
object Label7: TLabel
Left = 16
Top = 59
Width = 14
Height = 13
Caption = 'IV:'
end
object Label8: TLabel
Left = 17
Top = 230
Width = 85
Height = 13
Caption = 'Cyphertext (Hex)'
end
object EdtPlainText1: TEdit
Left = 16
Top = 118
Width = 505
Height = 21
TabOrder = 0
Text = 'somethingorother'
end
object EdtCypher: TEdit
Left = 16
Top = 166
Width = 505
Height = 21
TabOrder = 1
end
object EdtPlainText2: TEdit
Left = 16
Top = 296
Width = 505
Height = 21
TabOrder = 2
end
object BiBEncrypt: TBitBtn
Left = 368
Top = 141
Width = 73
Height = 23
Caption = 'Encrypt'
Enabled = False
TabOrder = 3
OnClick = BiBEncryptClick
Glyph.Data = {
76010000424D7601000000000000760000002800000020000000100000000100
04000000000000010000120B0000120B00001000000000000000000000000000
800000800000008080008000000080008000808000007F7F7F00BFBFBF000000
FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00333333333333
333333333333333333333333333333333333333333333333FFF3333333333333
00333333333333FF77F3333333333300903333333333FF773733333333330099
0333333333FF77337F3333333300999903333333FF7733337333333700999990
3333333777333337F3333333099999903333333373F333373333333330999903
33333333F7F3337F33333333709999033333333F773FF3733333333709009033
333333F7737737F3333333709073003333333F77377377F33333370907333733
33333773773337333333309073333333333337F7733333333333370733333333
3333377733333333333333333333333333333333333333333333}
NumGlyphs = 2
end
object BiBDecrypt: TBitBtn
Left = 368
Top = 270
Width = 73
Height = 23
Caption = 'Decrypt'
Enabled = False
TabOrder = 4
OnClick = BiBDecryptClick
Glyph.Data = {
76010000424D7601000000000000760000002800000020000000100000000100
04000000000000010000120B0000120B00001000000000000000000000000000
800000800000008080008000000080008000808000007F7F7F00BFBFBF000000
FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00333333333333
33333333333333333333333333333333333333FF333333333333300333333333
33333773FF33333333333090033333333333373773FF33333333330990033333
3333337F3773FF33333333099990033333333373F33773FFF333333099999007
33333337F33337773333333099999903333333373F3333733333333309999033
333333337F3337F333333333099990733333333373F3F77F3333333330900907
3333333337F77F77F33333333003709073333333377377F77F33333337333709
073333333733377F77F33333333333709033333333333377F7F3333333333337
0733333333333337773333333333333333333333333333333333}
NumGlyphs = 2
end
object EdtKey: TEdit
Left = 16
Top = 32
Width = 265
Height = 21
TabOrder = 5
Text = '12345678912345678912345678912345'
end
object EdtCypher64: TEdit
Left = 16
Top = 205
Width = 505
Height = 21
TabOrder = 6
end
object EdtIV: TEdit
Left = 16
Top = 75
Width = 265
Height = 21
TabOrder = 7
Text = '1234567891234567'
end
object EdtCypherHex: TEdit
Left = 17
Top = 246
Width = 505
Height = 21
TabOrder = 8
end
object BtnInit: TButton
Left = 336
Top = 80
Width = 75
Height = 25
Caption = 'Init enc'
TabOrder = 9
OnClick = BtnInitClick
end
object AESCodec: TCodec
AsymetricKeySizeInBits = 2048
AdvancedOptions2 = []
CryptoLibrary = CryptographicLibrary1
Left = 336
Top = 40
StreamCipherId = 'native.StreamToBlock'
BlockCipherId = 'native.AES-256'
ChainId = 'native.CBC'
end
object CryptographicLibrary1: TCryptographicLibrary
Left = 384
Top = 40
end
end
========== DCPCrypt 密码文件 ==============
unit uEncDecTests_DCPCrypt;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Buttons, LbCipher, LbClass, DCPcrypt2, DCPblockciphers,
DCPrijndael;
const
cOutBufSize = 1024;
type
TFrmEncDecTests = class(TForm)
EdtPlainText1: TEdit;
EdtCypher: TEdit;
Label1: TLabel;
Label2: TLabel;
EdtPlainText2: TEdit;
Label3: TLabel;
Label4: TLabel;
BiBEncrypt: TBitBtn;
BiBDecrypt: TBitBtn;
Label5: TLabel;
EdtKey: TEdit;
Label6: TLabel;
EdtCypher64: TEdit;
EdtIV: TEdit;
Label7: TLabel;
Label8: TLabel;
EdtCypherHex: TEdit;
BtnKAT: TButton;
DCP_rijndael1: TDCP_rijndael;
BtnHexConvert: TButton;
procedure BiBEncryptClick(Sender: TObject);
procedure BiBDecryptClick(Sender: TObject);
procedure BtnKATClick(Sender: TObject);
procedure BtnHexConvertClick(Sender: TObject);
private
public
end;
var
FrmEncDecTests: TFrmEncDecTests;
implementation
{$R *.dfm}
Uses
DCPbase64,
uRijndael,
uKATVectors, uHexConvert;
procedure TFrmEncDecTests.BiBEncryptClick(Sender: TObject);
type
KeyBuffer = Array[1..32] of Byte;
IVBuffer = Array[1..16] of Byte;
var
KeyHex, IVHex, PlainTxt, PlainHex, CypherStr, CypherHex, CypherB64, CypherBytes: String;
i : Integer;
begin
KeyHex := EdtKey.Text;
IVHex := EdtIV.Text;
PlainTxt := EdtPlainText1.Text;
PlainHex := StringToHex(PlainTxt);
CypherHex := RijndaelEncryptHex(KeyHex, IVHex, PlainHex);
CypherStr := HexToString(CypherHex);
CypherB64 := Base64EncodeStr(CypherStr);
CypherBytes := '';
CypherBytes := ''; for i := 1 to Length(CypherStr) do CypherBytes := CypherBytes + IntToStr(Ord(CypherStr[i])) + ' ';
EdtCypherHex.Text := CypherHex;
EdtCypher.Text := CypherBytes;
EdtCypher64.Text := CypherB64;
end;
procedure TFrmEncDecTests.BiBDecryptClick(Sender: TObject);
var
KeyHex, IVHex, PlainHex: String;
begin
KeyHex := EdtKey.Text;
IVHex := EdtIV.Text;
PlainHex := RijndaelDecryptHex(KeyHex,IVHex,EdtCypherHex.Text);
EdtPlainText2.Text := HexToString(PlainHex);
end;
procedure TFrmEncDecTests.BtnHexConvertClick(Sender: TObject);
begin
FrmHexConvert.ShowModal;
end;
procedure TFrmEncDecTests.BtnKATClick(Sender: TObject);
begin
FrmKATVectors.ShowModal;
end;
end.
========== DCPCrypt dfm 文件 ===============
object FrmEncDecTests: TFrmEncDecTests
Left = 0
Top = 0
Caption = 'Encryptie/decryptie tests (DCPCrypt)'
ClientHeight = 328
ClientWidth = 535
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object Label1: TLabel
Left = 16
Top = 150
Width = 114
Height = 13
Caption = 'Cyphertext (raw bytes)'
end
object Label2: TLabel
Left = 16
Top = 102
Width = 129
Height = 13
Caption = 'Plaintext (readable string):'
end
object Label3: TLabel
Left = 16
Top = 280
Width = 46
Height = 13
Caption = 'Plaintext:'
end
object Label4: TLabel
Left = 16
Top = 16
Width = 78
Height = 13
Caption = 'Key (hexstring):'
end
object Label5: TLabel
Left = 301
Top = 16
Width = 232
Height = 13
Caption = 'Testing DCPCrypt Rijndael (key 256, CBC)'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = [fsBold]
ParentFont = False
end
object Label6: TLabel
Left = 16
Top = 189
Width = 101
Height = 13
Caption = 'Cyphertext (Base64)'
end
object Label7: TLabel
Left = 16
Top = 59
Width = 70
Height = 13
Caption = 'IV (hexstring):'
end
object Label8: TLabel
Left = 17
Top = 230
Width = 85
Height = 13
Caption = 'Cyphertext (Hex)'
end
object EdtPlainText1: TEdit
Left = 16
Top = 118
Width = 505
Height = 21
TabOrder = 0
Text = 'timetellbvencryptiemethode'
end
object EdtCypher: TEdit
Left = 16
Top = 166
Width = 505
Height = 21
TabOrder = 1
end
object EdtPlainText2: TEdit
Left = 16
Top = 296
Width = 505
Height = 21
TabOrder = 2
end
object BiBEncrypt: TBitBtn
Left = 368
Top = 141
Width = 73
Height = 23
Caption = 'Encrypt'
TabOrder = 3
OnClick = BiBEncryptClick
Glyph.Data = {
76010000424D7601000000000000760000002800000020000000100000000100
04000000000000010000120B0000120B00001000000000000000000000000000
800000800000008080008000000080008000808000007F7F7F00BFBFBF000000
FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00333333333333
333333333333333333333333333333333333333333333333FFF3333333333333
00333333333333FF77F3333333333300903333333333FF773733333333330099
0333333333FF77337F3333333300999903333333FF7733337333333700999990
3333333777333337F3333333099999903333333373F333373333333330999903
33333333F7F3337F33333333709999033333333F773FF3733333333709009033
333333F7737737F3333333709073003333333F77377377F33333370907333733
33333773773337333333309073333333333337F7733333333333370733333333
3333377733333333333333333333333333333333333333333333}
NumGlyphs = 2
end
object BiBDecrypt: TBitBtn
Left = 368
Top = 270
Width = 73
Height = 23
Caption = 'Decrypt'
TabOrder = 4
OnClick = BiBDecryptClick
Glyph.Data = {
76010000424D7601000000000000760000002800000020000000100000000100
04000000000000010000120B0000120B00001000000000000000000000000000
800000800000008080008000000080008000808000007F7F7F00BFBFBF000000
FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00333333333333
33333333333333333333333333333333333333FF333333333333300333333333
33333773FF33333333333090033333333333373773FF33333333330990033333
3333337F3773FF33333333099990033333333373F33773FFF333333099999007
33333337F33337773333333099999903333333373F3333733333333309999033
333333337F3337F333333333099990733333333373F3F77F3333333330900907
3333333337F77F77F33333333003709073333333377377F77F33333337333709
073333333733377F77F33333333333709033333333333377F7F3333333333337
0733333333333337773333333333333333333333333333333333}
NumGlyphs = 2
end
object EdtKey: TEdit
Left = 16
Top = 32
Width = 265
Height = 21
TabOrder = 5
Text = ''
end
object EdtCypher64: TEdit
Left = 16
Top = 205
Width = 505
Height = 21
TabOrder = 6
end
object EdtIV: TEdit
Left = 16
Top = 75
Width = 265
Height = 21
TabOrder = 7
Text = ''
end
object EdtCypherHex: TEdit
Left = 17
Top = 246
Width = 505
Height = 21
TabOrder = 8
end
object BtnKAT: TButton
Left = 301
Top = 47
Width = 201
Height = 25
Caption = 'Known Answer Test (KAT) Vectors'
TabOrder = 9
OnClick = BtnKATClick
end
object BtnHexConvert: TButton
Left = 301
Top = 80
Width = 75
Height = 20
Caption = 'Hex conversie'
TabOrder = 10
OnClick = BtnHexConvertClick
end
object DCP_rijndael1: TDCP_rijndael
Id = 9
Algorithm = 'Rijndael'
MaxKeySize = 256
BlockSize = 128
Left = 432
Top = 80
end
end
========== uRijndael.pas ==============
unit uRijndael;
// Helper functions for 256-bit Rijndael/AES encryption with DCPcrypt
interface
Uses
StdCtrls;
function RijndaelEncryptHex(hexstrKey,hexstrIV,hexstrPlain: String; MMo: TMemo = nil): String;
function RijndaelDecryptHex(hexstrKey,hexstrIV,hexstrCypher: String; MMo: TMemo = nil): String;
// Input en output zijn strings met hex waarden ('014730f80ac625fe84f026c60bfd547d')
//
// Deze routines gebruiken een 256-key AES/Rijndael encryptie, waarbij de plaintext
// met NULL waarden ge-pad wordt tot een veelvoud van de blocksize.
// Merk op dat de initializatievector even groot moet zijn als de blocksize (128 bits, dus een hex string van 32 tekens).
//
// Als TMemo gespecificeerd is wordt daar naar toe gelogd.
// Helper routines:
function HexToString(H: String): String;
function StringtoHex(Data: string; WithSpaces: Boolean = false): string;
function HexToInt(HexNum: string): LongInt;
implementation
Uses
SysUtils,
DCPbase64, DCPcrypt2, DCPblockciphers, DCPrijndael;
type
KeyBuffer = Array[1..32] of Byte;
IVBuffer = Array[1..16] of Byte;
function HexToString(H: String): String;
var I : Integer;
begin
Result:= '';
for I := 1 to length (H) div 2 do
Result:= Result+Char(StrToInt('$'+Copy(H,(I-1)*2+1,2)));
end;
function StringtoHex(Data: string; WithSpaces: Boolean = false): string;
var
i, i2: Integer;
s: string;
begin
i2 := 1;
for i := 1 to Length(Data) do
begin
Inc(i2);
if i2 = 2 then
begin
if WithSpaces then s := s + ' ';
i2 := 1;
end;
s := s + IntToHex(Ord(Data[i]), 2);
end;
Result := s;
end;
function HexToInt(HexNum: string): LongInt;
begin
Result := StrToInt('$' + HexNum) ;
end;
function FilterHex(S: String): String;
// Filters all hex characters 0..F (case insensitive) uit S
var
SOut: String;
l : Word;
begin
SOut := '';
for l := 1 to Length(S) do
if not (S[l] in ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','A','B','C','D','E','F']) then
SOut := SOut + S[l];
Result := SOut;
end;
function RijndaelEncryptHex(hexstrKey,hexstrIV,hexstrPlain: String; MMo: TMemo = nil): String;
var
InBuf,OutBuf : array of byte;
BufSizeInBytes: Word;
KeyBuf : KeyBuffer;
IVBuf : IVBuffer;
l,i : Integer;
Bytes,SOut : String;
DCPR : TDCP_rijndael;
begin
l := Length(HexStrKey);
Assert(l=64,'Key heeft ongeldige lengte (moet 64 chars zijn): ' + IntToStr(l));
Assert(FilterHex(HexStrKey) = '','Key heeft ongeldige tekens: ' + HexStrKey);
l := Length(HexStrIV);
Assert(l=32,'IV heeft ongeldige lengte (moet 32 chars zijn): ' + IntToStr(l));
Assert(FilterHex(HexStrIV) = '','IV heeft ongeldige tekens: ' + HexStrIV);
Assert(FilterHex(hexstrPlain) = '','Plaintext heeft ongeldige tekens: ' + hexstrPlain);
l := Length(hexstrPlain);
Assert(l MOD 2 = 0,'Plaintext heeft oneven lengte: ' + hexstrPlain);
if Mmo<> nil then begin
Mmo.Lines.Add('Key: ' + hexstrKey);
Mmo.Lines.Add('IV: ' + hexstrIV);
Mmo.Lines.Add('Plaintext: ' + hexstrPlain);
end;
l := Length(hexstrKey) DIV 2;
for i := 1 to l do KeyBuf[i] := HexToInt(Copy(hexstrKey,2*(i-1)+1,2));
l := Length(hexstrIV) DIV 2;
for i := 1 to l do IVBuf[i] := HexToInt(Copy(hexstrIV,2*(i-1)+1,2));
// Pad with zeroes:
while Length(hexstrPlain) MOD 32 <> 0 do hexstrPlain := hexstrPlain + '00';
BufSizeInBytes := Length(hexstrPlain) DIV 2;
SetLength(InBuf,BufSizeInBytes);
SetLength(OutBuf,BufSizeInBytes);
for i := 0 to BufSizeInBytes-1 do InBuf[i] := HexToInt(Copy(hexstrPlain,2*i+1,2));
DCPR := TDCP_rijndael.Create(nil);
DCPR.Init(KeyBuf,256,@IVBuf);
DCPR.EncryptCBC(InBuf[0],OutBuf[0],BufSizeInBytes);
DCPR.Burn; // Leeg memory buffers voor security
DCPR.Free;
SOut := '';
for i := 0 to BufSizeInBytes-1 do begin SOut := SOut + Chr(OutBuf[i]); Bytes := Bytes + IntToStr(OutBuf[i]) + ' '; end;
if Mmo<> nil then begin
Mmo.Lines.Add('Cyphertext (bytes): ' + Bytes);
Mmo.Lines.Add('Cyphertext (base64): ' + Base64EncodeStr(SOut));
end;
SOut := LowerCase(StringToHex(SOut));
if Mmo<> nil then begin
Mmo.Lines.Add('Cyphertext (hex): ' + SOut);
Mmo.Lines.Add('');
end;
Result := SOut;
end; { RijndaelEncryptHex }
function RijndaelDecryptHex(hexstrKey,hexstrIV,hexstrCypher: String; MMo: TMemo = nil): String;
var
InBuf,
OutBuf: array of byte;
BufSizeInBytes : Word;
KeyBuf: KeyBuffer;
IVBuf : IVBuffer;
l,i : Integer;
SOut : String;
DCPR : TDCP_rijndael;
begin
l := Length(HexStrKey);
Assert(l=64,'Key heeft ongeldige lengte (moet 64 chars zijn): ' + IntToStr(l));
Assert(FilterHex(HexStrKey) = '','Key heeft ongeldige tekens: ' + HexStrKey);
l := Length(HexStrIV);
Assert(l=32,'IV heeft ongeldige lengte (moet 32 chars zijn): ' + IntToStr(l));
Assert(FilterHex(HexStrIV) = '','IV heeft ongeldige tekens: ' + HexStrIV);
Assert(FilterHex(hexstrCypher) = '','Cyphertext heeft ongeldige tekens: ' + hexstrCypher);
l := Length(hexstrCypher);
Assert(l MOD 2 = 0,'Cyphertext heeft oneven lengte: ' + hexstrCypher);
if Mmo<> nil then begin
Mmo.Lines.Add('Key: ' + hexstrKey);
Mmo.Lines.Add('IV: ' + hexstrIV);
Mmo.Lines.Add('CypherText: ' + hexstrCypher);
end;
l := Length(hexstrKey) DIV 2;
for i := 1 to l do KeyBuf[i] := HexToInt(Copy(hexstrKey,2*(i-1)+1,2));
l := Length(hexstrIV) DIV 2;
for i := 1 to l do IVBuf[i] := HexToInt(Copy(hexstrIV,2*(i-1)+1,2));
// Pad with zeroes:
BufSizeInBytes := Length(hexstrCypher) DIV 2;
SetLength(InBuf,BufSizeInBytes);
SetLength(OutBuf,BufSizeInBytes);
for i := 0 to BufSizeInBytes-1 do InBuf[i] := HexToInt(Copy(hexstrCypher,2*i+1,2));
DCPR := TDCP_rijndael.Create(nil);
DCPR.Init(KeyBuf,256,@IVBuf);
DCPR.DecryptCBC(InBuf[0],OutBuf[0],BufSizeInBytes);
DCPR.Burn;
DCPR.Free;
SOut := '';
for i := 0 to BufSizeInBytes-1 do SOut := SOut + Chr(OutBuf[i]);
SOut := LowerCase(StringToHex(SOut));
if Mmo<> nil then begin
Mmo.Lines.Add('Plaintext (hex): ' + SOut);
Mmo.Lines.Add('');
end;
Result := SOut;
end; { RijndaelDecryptHex }
end.