我有一个客户端提供了一个 API,该 API 规定要发送给他们的数据必须使用AES、128 位密钥、ECB 模式和 PKCS5Padding进行加密。我正在尝试在 Delphi 10.3 Rio 中使用 LockBox 3,但没有得到与他们指出的用于验证的在线测试工具相同的加密字符串。它很近,但并不完全在那里。
在这里阅读了大量关于Unicode、PKCS5Padding和相关问题的内容,我已经到了要尝试的内容的结尾。我必须承认我在加密方面做得并不多,并且在我提出问题之前已经尽可能多地阅读以了解这一点。
我想确认几件事:
- 密码和密钥的区别。我读过 LB3 使用密码来生成密钥,但我有来自客户端的关于如何生成密钥的具体说明,所以我制作了自己的 Base64 编码密钥并调用 InitFromStream 来初始化它。我相信这可以代替设置密码,对吗?或者密码可能仅由非对称密码(不是对称密码,如 AES)使用?
- PKCS5Padding:我担心我在LB3 帮助网站上读到的内容说填充是根据密码、链接模式等的选择智能完成的。那么这是否意味着没有办法强制它使用特定的填充方法?我已将数据转换为字节数组并由自己的 PKCS5Padding 实现,但我认为 LB3 可能仍在填充之外。(我尝试过查看代码,但没有发现任何证据表明它正在这样做。)
我应该在 Delphi 中使用不同的加密库来完成这个吗? 我检查了DelphiEncryptionCompendium和DcPCryptV2,但我发现 LB3 似乎得到了最多的支持,而且我觉得它是最容易使用的,尤其是在我的 Unicode 版本的 Delphi 中。另外,我在过去几年中使用过很多 LockBox 2,所以我认为它会更熟悉(事实证明并非如此)。
为了说明我的尝试,我将代码从项目中提取到控制台应用程序中。也许我上面的假设是正确的,并且我的代码或 LB3 参数中有一个明显的错误,我不明白有人会指出:
program LB3ConsoleTest;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, System.Classes, System.NetEncoding,
uTPLb_Codec, uTPLb_CryptographicLibrary,
uTPLb_StreamUtils, uTPLb_Constants;
var
Codec: TCodec;
CryptographicLibrary: TCryptographicLibrary;
function PKCS5PadStringToBytes(RawData: string; const PadSize: Integer): TBytes;
{ implement our own block padding }
var
DataLen: Integer;
PKCS5PaddingCount: ShortInt;
begin
Result := TEncoding.UTF8.GetBytes(RawData);
DataLen := Length(RawData);
PKCS5PaddingCount := PadSize - DataLen mod PadSize;
if PKCS5PaddingCount = 0 then
PKCS5PaddingCount := PadSize;
Inc(DataLen, PKCS5PaddingCount);
SetLength(Result, DataLen);
FillChar(Result[DataLen - PKCS5PaddingCount], PKCS5PaddingCount, PKCS5PaddingCount);
end;
procedure InitializeAESKey(const AESKey: string);
{ convert the string to a byte array,
use that to initialize a ByteStream,
and call LB3's InitFromStream }
var
AESKeyBytes: TBytes;
AESKeyStream: TBytesStream;
begin
AESKeyBytes := TEncoding.UTF8.GetBytes(AESKey);
AESKeyStream := TBytesStream.Create(AESKeyBytes);
Codec.InitFromStream(AESKeyStream);
end;
const
RawData = '{"invoice_id":"456456000018047","clerk_id":"0023000130234234","trans_amount":1150034534,"cust_code":"19455605000987890641","trans_type":"TYPE1"}';
AESKeyStr = 'CEAA31AD1EE4BDC8';
var
DataBytes: TBytes;
DataStream: TBytesStream;
ResultStream: TBytesStream;
ResultBytes: TBytes;
Base64Encoder: TBase64Encoding;
begin
// create the LockBox3 objects
Codec := TCodec.Create(nil);
CryptographicLibrary := TCryptographicLibrary.Create(nil);
try
// setup LB3 for AES, 128-bit key, ECB
Codec.CryptoLibrary := CryptographicLibrary;
Codec.StreamCipherId := uTPLb_Constants.BlockCipher_ProgId;
Codec.BlockCipherId := Format(uTPLb_Constants.AES_ProgId, [128]);
Codec.ChainModeId := uTPLb_Constants.ECB_ProgId;
// prep the data, the key, and the resulting stream
DataBytes := PKCS5PadStringToBytes(RawData, 8);
DataStream := TBytesStream.Create(DataBytes);
InitializeAESKey(AESKeyStr);
ResultStream := TBytesStream.Create;
// ENCRYPT!
Codec.EncryptStream(DataStream, ResultStream);
// take the result stream, convert it to a byte array
ResultStream.Seek(0, soFromBeginning);
ResultBytes := Stream_to_Bytes(ResultStream);
// convert the byte array to a Base64-encoded string and display
Base64Encoder := TBase64Encoding.Create(0);
Writeln(Base64Encoder.EncodeBytesToString(ResultBytes));
Readln;
finally
Codec.Free;
CryptographicLibrary.Free;
end;
end.
该程序生成一个 216 个字符长的加密字符串,只有最后 25 个字符与在线工具生成的不同。
为什么?