为何Java与Delphi的AES加密结果不一致?
问题分析与修正方案
你的Delphi代码和Java代码在几个核心环节上不匹配,导致加密结果不一致,下面是具体问题和修复方法:
1. 密钥处理方式完全错误
Java代码中,你把十六进制字符串7823C4E346336621CB73868429D8CC93直接转换成字节数组作为AES密钥(没有经过任何密钥派生);但Delphi的LockBox中,你直接把这个十六进制字符串当作密码传入,LockBox默认会对密码执行**密钥派生算法(比如PBKDF2)**生成密钥,这和Java的逻辑完全不同。
你需要把十六进制密钥字符串转换成字节数组,然后直接设置给LockBox的密钥,而不是用密码属性。
2. 填充模式不匹配
Java代码明确使用了AES/ECB/NoPadding,但LockBox默认会启用PKCS#5/PKCS#7填充,这会导致加密时自动补全数据到块大小,和Java的无填充逻辑冲突。
3. 加密结果的编码方式错误
Java返回的是加密后的原始字节数组,最终转成十六进制字符串;而Delphi的EncryptAnsiString默认返回的是Base64编码的密文,你直接把这个Base64字符串转成ANSI字节,自然和Java的原始字节结果不一样。
修正后的Delphi代码
首先修正十六进制转字节数组的函数(确保和Java逻辑一致)
function HexToByteArr(const HexStr: string): TArray<Byte>; var I: Integer; HexPair: string; begin SetLength(Result, Length(HexStr) div 2); for I := 0 to (Length(HexStr) div 2) - 1 do begin HexPair := Copy(HexStr, (I * 2) + 1, 2); Result[I] := StrToInt('$' + HexPair); end; end; function ByteArrToHex(const Bytes: TArray<Byte>): string; const HexChars: array[0..15] of Char = ('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'); var I: Integer; B: Byte; begin SetLength(Result, Length(Bytes) * 2); for I := 0 to Length(Bytes) - 1 do begin B := Bytes[I]; Result[I*2 + 1] := HexChars[B shr 4]; Result[I*2 + 2] := HexChars[B and $0F]; end; end;
修正加密函数,匹配Java的逻辑
function EncryptData(const DataBytes: TArray<Byte>; const KeyBytes: TArray<Byte>): TArray<Byte>; var Codec: TCodec; CipherStream: TMemoryStream; InputStream: TMemoryStream; begin Codec := TCodec.Create(nil); InputStream := TMemoryStream.Create; CipherStream := TMemoryStream.Create; try // 初始化加密库 Codec.CryptoLibrary := TCryptographicLibrary.Create(Codec); Codec.BlockCipherId := Format(AES_ProgId, [128]); Codec.ChainModeId := ECB_ProgId; // 设置无填充,和Java的NoPadding匹配 Codec.Padding := TSymmetricPadding.pNone; // 直接使用原始字节作为密钥,不做派生 Codec.InitFromBinaryKey(KeyBytes); // 写入待加密的数据 InputStream.WriteBuffer(DataBytes[0], Length(DataBytes)); InputStream.Position := 0; // 执行加密 Codec.EncryptStream(InputStream, CipherStream); CipherStream.Position := 0; // 读取加密后的原始字节 SetLength(Result, CipherStream.Size); CipherStream.ReadBuffer(Result[0], CipherStream.Size); finally CipherStream.Free; InputStream.Free; Codec.Free; end; end;
调用示例(和Java的测试逻辑完全对齐)
procedure TestAES; var _DataHex: string; _DataBytes: TArray<Byte>; _KeyHex: string; _KeyBytes: TArray<Byte>; _EncryptedBytes: TArray<Byte>; _EncryptedHex: string; begin _DataHex := '8450696E670454696D650013323031382D31322D30352031353A34363A343700'; _DataBytes := HexToByteArr(_DataHex); _KeyHex := '7823C4E346336621CB73868429D8CC93'; _KeyBytes := HexToByteArr(_KeyHex); _EncryptedBytes := EncryptData(_DataBytes, _KeyBytes); _EncryptedHex := ByteArrToHex(_EncryptedBytes); // 现在_EncryptedHex应该和Java的结果一致:DFCC3CB95BEA276E6833207316B008DA6D219AA36A81AD6E75800BC898AE43B8 ShowMessage(_EncryptedHex); end;
关键修正点总结
- 密钥:直接使用十六进制转字节后的原始密钥,禁用LockBox的密码派生逻辑
- 填充:设置为
pNone,和Java的NoPadding匹配 - 结果处理:直接获取加密后的原始字节,再转十六进制,而不是使用Base64编码的输出
内容的提问来源于stack exchange,提问作者Edijs Kolesnikovičs
相关产品推荐
相关产品推荐

