这是一个在 AES-256 中加密/解密 UTF-16LE 字符串的示例程序,将填充修改为与 C# 类型的填充可互操作。Lockbox 3 不是为非正统的填充技术(例如 C# 使用)而设计的,但通过一些工作,我们可以适应。
- 编译器 = 德尔福 XE6
- TPLB3 版本 = 3.6.0
- 目标平台 = Win32
- 配置 = 调试
- 应用类型 = VCL 表格
清单 1:程序 CSharp2.dpr
program CSharp2;
uses
Vcl.Forms,
umfmCSharp2Demo in 'umfmCSharp2Demo.pas' {mfmCSharp2Demo};
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TmfmCSharp2Demo, mfmCSharp2Demo);
Application.Run;
end.
清单 2:umfmCSharp2Demo.pas
unit umfmCSharp2Demo;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, System.Actions,
Vcl.ActnList, Vcl.PlatformDefaultStyleActnCtrls, Vcl.ActnMan, Vcl.ExtCtrls,
TPLB3.Codec, TPLB3.CryptographicLibrary;
type
TmfmCSharp2Demo = class(TForm)
edtKey: TLabeledEdit;
edtIV: TLabeledEdit;
rdChain: TRadioGroup;
memoPlaintext: TMemo;
lblPlaintext: TLabel;
edtCiphertext: TLabeledEdit;
actmngrMainActions: TActionManager;
Button1: TButton;
Button2: TButton;
memoResults: TMemo;
actEncrypt: TAction;
actDecrypt: TAction;
procedure actEncryptExecute(Sender: TObject);
procedure actDecryptExecute(Sender: TObject);
private
procedure Put( const Line: string; const Args: array of const);
procedure Setup( var Codec: TCodec; var CryptographicLibrary: TCryptographicLibrary);
procedure SetIV( var Codec: TCodec; var Padder: TObject);
end;
var
mfmCSharp2Demo: TmfmCSharp2Demo;
implementation
uses TPLB3.Constants,
TPLB3.StreamUtils, TPLB3.StreamToBlock;
{$R *.dfm}
type
TCSharpPadder = class( TObject)
private
FIV: TBytes;
public
constructor Create( const AIV: TBytes);
procedure SetIV_For_Decrypt( Value: TMemoryStream);
end;
function StripSpace( const Spacey: string): string;
var
Builder: TStringBuilder;
i: integer;
begin
Builder := TStringBuilder.Create;
Builder.Append( Spacey);
for i := Builder.Length - 1 downto 0 do
if CharInSet( Builder.Chars[ i], [' ', #9, #$A, #$D]) then
Builder.Remove( i, 1);
result := Builder.ToString;
Builder.Free
end;
procedure TmfmCSharp2Demo.Setup(
var Codec: TCodec;
var CryptographicLibrary: TCryptographicLibrary);
begin
Codec := TCodec.Create( nil);
CryptographicLibrary := TCryptographicLibrary.Create( nil);
Codec.CryptoLibrary := CryptographicLibrary;
Codec.StreamCipherId := BlockCipher_ProgId;
Codec.BlockCipherId := Format( AES_ProgId, [256]);
case rdChain.ItemIndex of
0: Codec.ChainModeId := CBC_ProgId;
1: Codec.ChainModeId := CFB_ProgId;
2: Codec.ChainModeId := CFB8bit_ProgId;
3: Codec.ChainModeId := CTR_ProgId;
4: Codec.ChainModeId := OFB_ProgId;
end
end;
procedure TmfmCSharp2Demo.SetIV( var Codec: TCodec; var Padder: TObject);
var
IV_AsStream: TStream;
IV: TBytes;
CSharpPadder: TCSharpPadder;
begin
Padder := nil;
IV_AsStream := TMemoryStream.Create;
try
Base64_to_stream( StripSpace( edtIV.Text), IV_AsStream);
IV_AsStream.Position := 0;
SetLength( IV, IV_AsStream.Size);
IV_AsStream.ReadData( IV, Length( IV))
finally
IV_AsStream.Free
end;
if Length( IV) <> 16 then
raise Exception.Create( 'To encrypt C# style, you must supply a 16 byte IV.');
CSharpPadder := TCSharpPadder.Create( IV);
Padder := CSharpPadder;
Codec.OnSetIV := CSharpPadder.SetIV_For_Decrypt
end;
procedure TmfmCSharp2Demo.actEncryptExecute( Sender: TObject);
var
Key: TStream;
Codec: TCodec;
CryptographicLibrary: TCryptographicLibrary;
s: string;
Builder: TStringBuilder;
BlockLenInUnits, PadIdx: integer;
Ciphertext: TStream;
Padder: TObject;
Temp: TBytes;
Len: integer;
begin
try
// 1. Create the components dynamically.
// An alternative would be to use design-time properties.
Setup( Codec, CryptographicLibrary);
Ciphertext := TMemoryStream.Create;
try
// 2. Pad the plaintext, C# style.
// This is an inferior style of padding. Only do it if you need
// interoperability with C# libraries.
Builder := TStringBuilder.Create;
Builder.Append( memoPlaintext.Lines.Text);
BlockLenInUnits := 16 div SizeOf( Char);
for PadIdx := 1 to (BlockLenInUnits - (Builder.Length mod BlockLenInUnits)) mod BlockLenInUnits do
// C# just pads out with zeros.
Builder.Append( Char( #0));
s := Builder.ToString;
Builder.Free;
// 3. Read in the IV.
// Normally IV is set automatically by TP Lockbox 3, but C# library is pretty stupid
// and requires manual management.
SetIV( Codec, Padder);
try
// 4. Read in the binary key (instead of setting a string password),
// and initialize the Codec.
Key := TMemoryStream.Create;
try
Base64_to_stream( StripSpace( edtKey.Text), Key);
Key.Position := 0;
Codec.InitFromStream( Key);
finally
Key.Free
end;
// 5. Encrypt using a UTF-16LE format.
Codec.Begin_EncryptMemory( Ciphertext);
if s.Length > 0 then
Codec.EncryptMemory( s[ Low( s)], s.Length * SizeOf( Char));
Codec.End_EncryptMemory;
finally
Padder.Free
end;
// 8. TP LockBox 3 automatically seeds (prepend) the cipher stream with
// the low 8 bytes of the IV. But we don't need this for C# interoperability,
// so remove these bytes from the stream.
if CipherText.Size <> 0 then
begin
Ciphertext.Position := 8;
Len := CipherText.Size - CipherText.Position;
SetLength( Temp, Len);
if Len > 0 then
CipherText.ReadData( Temp, Len);
CipherText.Size := Len;
CipherText.Position := 0;
CipherText.WriteData( Temp, Len)
end;
edtCiphertext.Text := Stream_to_Base64( Ciphertext);
// 9. Tidy up
finally
Ciphertext.Free;
Codec.Free;
CryptographicLibrary.Free;
end;
Put( 'Encryption done.', [])
except on E: Exception do
begin
Put( 'Encryption failed: %s: %s', [E.ClassName, E.Message]);
end
end;
end;
procedure TmfmCSharp2Demo.actDecryptExecute( Sender: TObject);
var
Key: TStream;
Codec: TCodec;
CryptographicLibrary: TCryptographicLibrary;
Plaintext: TStream;
Padder: TObject;
Temp: TBytes;
Len: integer;
CipherStream: TMemoryStream;
idx, Count: Integer;
begin
try
// 1. Setup
Setup( Codec, CryptographicLibrary);
Plaintext := TMemoryStream.Create;
try
// 2. Read in the IV.
// Normally IV is set automatically by TP Lockbox 3, but C# library is pretty stupid
// and requires manual management.
SetIV( Codec, Padder);
CipherStream := TMemoryStream.Create;
try
// 3. Read in the binary key (instead of setting a string password),
// and initialize the Codec.
Key := TMemoryStream.Create;
try
Base64_to_stream( StripSpace( edtKey.Text), Key);
Key.Position := 0;
Codec.InitFromStream( Key);
finally
Key.Free
end;
// 3. Fake a prepended IV. This value doesn't matter any way.
// It gets overwritten by the Padder object.
Temp := TBytes.Create( 0, 0, 0, 0, 0, 0, 0, 0);
CipherStream.WriteData( Temp, 8);
// 4. Decrypt using a UTF-16LE format.
Codec.Begin_DecryptMemory( Plaintext);
Base64_to_stream( StripSpace( edtCiphertext.Text), CipherStream);
Codec.DecryptMemory( CipherStream.Memory^, CipherStream.Size);
Codec.End_DecryptMemory;
finally
Padder.Free;
CipherStream.Free
end;
// 5. Strip C# style padding.
Plaintext.Position := 0;
SetLength( Temp, Plaintext.Size);
Plaintext.ReadData( Temp, Length( Temp));
Count := 0;
Len := Length( Temp);
if Len >= 2 then
for idx := (Len div 2) - 1 downto 0 do
begin // Detect how many padding zeroes.
if (Temp[idx*2] = 0) and (Temp[idx*2 + 1] = 0) then continue;
Count := (idx + 1) * 2;
break
end;
memoPlaintext.Text := TEncoding.Unicode.GetString( Temp, 0, Count);
// 9. Tidy up
finally
Plaintext.Free;
Codec.Free;
CryptographicLibrary.Free;
end;
Put( 'Decryption done.', [])
except on E: Exception do
begin
Put( 'Decryption failed: %s: %s', [E.ClassName, E.Message]);
end
end;
end;
procedure TmfmCSharp2Demo.Put( const Line: string; const Args: array of const);
var
sLine: string;
begin
sLine := Line;
if Length( Args) > 0 then
sLine := Format( sLine, Args);
memoResults.Lines.Add( sLine)
end;
constructor TCSharpPadder.Create( const AIV: TBytes);
begin
FIV := AIV
end;
procedure TCSharpPadder.SetIV_For_Decrypt( Value: TMemoryStream);
begin
Value.Size := Length( FIV);
Value.Position := 0;
Value.WriteData( FIV, Length( FIV))
end;
end.
清单 3:umfmCSharp2Demo.dfm
object mfmCSharp2Demo: TmfmCSharp2Demo
Left = 0
Top = 0
Caption = 'C# Demo 2'
ClientHeight = 424
ClientWidth = 554
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
DesignSize = (
554
424)
PixelsPerInch = 96
TextHeight = 13
object lblPlaintext: TLabel
Left = 210
Top = 96
Width = 289
Height = 13
Caption = 'Plaintext datum (encoded as UTF-16LE; CR.LF line endings):'
FocusControl = memoPlaintext
end
object edtKey: TLabeledEdit
Left = 8
Top = 24
Width = 538
Height = 24
Anchors = [akLeft, akTop, akRight]
EditLabel.Width = 171
EditLabel.Height = 13
EditLabel.Caption = 'Key (32 bytes encoded as base64):'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'Courier New'
Font.Style = []
ParentFont = False
TabOrder = 0
Text = '0f2EYYpNRTE16ve7dYxbvGuk+BHR/YRhik1FMTXq97s='
end
object edtIV: TLabeledEdit
Left = 8
Top = 72
Width = 538
Height = 24
Anchors = [akLeft, akTop, akRight]
EditLabel.Width = 261
EditLabel.Height = 13
EditLabel.Caption = 'Initialization Vector (IV; 16 bytes encoded as base64):'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'Courier New'
Font.Style = []
ParentFont = False
TabOrder = 1
Text = 'kHLzYvYBxooAAAAAAAAAAA=='
end
object rdChain: TRadioGroup
Left = 8
Top = 99
Width = 185
Height = 102
Caption = 'Chaining mode'
Columns = 2
ItemIndex = 0
Items.Strings = (
'CBC'
'CFB'
'CFB - 8 bit'
'CTR'
'OFB')
TabOrder = 2
end
object memoPlaintext: TMemo
Left = 208
Top = 112
Width = 338
Height = 89
Anchors = [akLeft, akTop, akRight]
ScrollBars = ssVertical
TabOrder = 3
end
object edtCiphertext: TLabeledEdit
Left = 8
Top = 224
Width = 538
Height = 21
Anchors = [akLeft, akTop, akRight]
EditLabel.Width = 399
EditLabel.Height = 13
EditLabel.Caption =
'Ciphertext datum (encoded as base64; byte count must be a whole ' +
'multiple of 16):'
TabOrder = 4
end
object Button1: TButton
Left = 8
Top = 251
Width = 273
Height = 25
Action = actEncrypt
TabOrder = 5
end
object Button2: TButton
Left = 287
Top = 251
Width = 259
Height = 25
Action = actDecrypt
TabOrder = 6
end
object memoResults: TMemo
Left = 8
Top = 282
Width = 538
Height = 134
Anchors = [akLeft, akTop, akRight, akBottom]
Color = clInfoBk
ReadOnly = True
ScrollBars = ssVertical
TabOrder = 7
end
object actmngrMainActions: TActionManager
Left = 256
Top = 144
StyleName = 'Platform Default'
object actEncrypt: TAction
Caption = 'Encrypt by AES-256 using C# style padding'
OnExecute = actEncryptExecute
end
object actDecrypt: TAction
Caption = 'Decrypt by AES-256 using C# style padding'
OnExecute = actDecryptExecute
end
end
end