将C# AES-256加解密代码迁移至Delphi 10.3(Lockbox3)遇匹配问题
Delphi 10.3 + Lockbox 3.7 迁移C# AES-256加解密代码异常排查
问题背景
需要将用于与远程PHP数据库脚本交互的C# AES-256加解密代码迁移到Delphi 10.3(使用Lockbox 3.7库),但自行编写的Delphi解密代码输出与原C#代码不一致,参考相关Stack Overflow问题后仍未解决,需排查是否为库版本、Delphi版本问题或代码遗漏点。
原C#解密代码
internal static string DecryptString_forPHP(string cipherText, byte[] key, byte[] iv) { string plainText = ""; try { Aes encryptor = Aes.Create(); encryptor.Mode = CipherMode.CBC; byte[] aesKey = new byte[32]; Array.Copy(key, 0, aesKey, 0, 32); encryptor.Key = aesKey; encryptor.IV = iv; MemoryStream memoryStream = new MemoryStream(); ICryptoTransform aesDecryptor = encryptor.CreateDecryptor(); CryptoStream cryptoStream = new CryptoStream(memoryStream, aesDecryptor, CryptoStreamMode.Write); try { byte[] cipherBytes = Convert.FromBase64String(cipherText); cryptoStream.Write(cipherBytes, 0, cipherBytes.Length); cryptoStream.FlushFinalBlock(); byte[] plainBytes = memoryStream.ToArray(); plainText = Encoding.UTF8.GetString(plainBytes, 0, plainBytes.Length); } finally { memoryStream.Close(); cryptoStream.Close(); } } catch (Exception e) { ErrorLog(1, System.Environment.NewLine + e.Message); } return plainText; }
原C#加密代码
internal static string EncryptString_forPHP(string plainText, byte[] key, byte[] iv) { string cipherText = ""; try { Aes encryptor = Aes.Create(); encryptor.Mode = CipherMode.CBC; byte[] aesKey = new byte[32]; Array.Copy(key, 0, aesKey, 0, 32); encryptor.Key = aesKey; encryptor.IV = iv; MemoryStream memoryStream = new MemoryStream(); ICryptoTransform aesEncryptor = encryptor.CreateEncryptor(); CryptoStream cryptoStream = new CryptoStream(memoryStream, aesEncryptor, CryptoStreamMode.Write); byte[] plainBytes = Encoding.UTF8.GetBytes(plainText); cryptoStream.Write(plainBytes, 0, plainBytes.Length); cryptoStream.FlushFinalBlock(); byte[] cipherBytes = memoryStream.ToArray(); memoryStream.Close(); cryptoStream.Close(); cipherText = Convert.ToBase64String(cipherBytes, 0, cipherBytes.Length); } catch (Exception e) { ErrorLog(1, System.Environment.NewLine + e.Message); } return cipherText; }
C#调用示例
string password = "xxxxxxxxxxxxxxxxx"; // Create sha256 hash SHA256 mySHA256 = SHA256Managed.Create(); byte[] key = mySHA256.ComputeHash(Encoding.UTF8.GetBytes(password)); // Create secret IV byte[] iv = new byte[16] { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; string encrypted = EncryptString_forPHP(message, key, iv); string decrypted = DecryptString_forPHP(message, key, iv);
自行编写的Delphi解密代码
function DecryptAES256FromBase64String(const Value: String): String; var Codec: TCodec; CryptographicLibrary: TCryptographicLibrary; CipherStream, PlainTextStream: TStream; buffer: array of Byte; mySHA256 : THashSHA2; key : TBytes; begin Codec := TCodec.Create( nil); CryptographicLibrary := TCryptographicLibrary.Create(Codec); CipherStream := TMemoryStream.Create; PlainTextStream := TMemoryStream.Create; try Codec.CryptoLibrary := CryptographicLibrary; Codec.StreamCipherId := BlockCipher_ProgID; Codec.BlockCipherId := Format(AES_ProgId,[256]); Codec.AsymetricKeySizeInBits := 256; Codec.Cipher := '[AES-256*]'; Codec.ChainModeId := CBC_ProgId; mySHA256 := THashSHA2.Create(); key := mySHA256.GetHashBytes(password, SHA256); CipherStream.WriteBuffer(key[0], Length(key)); CipherStream.Position := 0; Codec.InitFromStream(CipherStream); CipherStream.Size := 0; CipherStream.WriteBuffer(IV[0], Length(IV)); Base64_to_stream(TNetEncoding.Base64.DecodeStringToBytes(Value), CipherStream); CipherStream.Position := 0; Codec.DecryptStream(PlainTextStream, CipherStream); PlainTextStream.Position := 0; SetLength(buffer, PlainTextStream.Size); PlainTextStream.ReadBuffer(buffer[0], Length(buffer)); Result := TNetEncoding.Base64.EncodeBytesToString(buffer); finally CipherStream.Free; PlainTextStream.Free; Codec.Free; CryptographicLibrary.Free; end; end;
问题排查与修正方案
你的Delphi代码存在多处关键错误,和库版本、Delphi版本无关,主要是Lockbox3的使用方式不符合C#的逻辑:
- 密钥初始化方式错误:Lockbox3的
InitFromStream用于读取加密后的密钥容器,而非直接写入原始密钥字节。应直接用Codec.SetKey方法设置SHA256生成的32字节密钥。 - IV设置错误:IV需要通过
Codec.IV属性直接赋值,不需要写入密文流中,C#里也是单独设置IV而非和密文混合。 - 结果处理完全错误:C#解密后是将明文字节转UTF8字符串,但你把解密后的字节又做了Base64编码,这会导致结果完全不符,应该直接转UTF8字符串。
- 冗余配置无效:
Codec.AsymetricKeySizeInBits是用于非对称加密的,AES是对称加密,该设置无意义;Codec.Cipher := '[AES-256*]'属于冗余配置,BlockCipherId已经指定了AES-256。 - 字符串编码一致性问题:C#中是对密码的UTF8字节做SHA256哈希,Delphi中需确保密码先转成UTF8字节再哈希,避免默认Ansi编码导致的哈希值不一致。
修正后的Delphi解密代码
uses System.SysUtils, System.Classes, System.NetEncoding, Lockbox3.CryptographicLibrary, Lockbox3.Codec, Lockbox3.SHA; function DecryptAES256FromBase64String(const Value: String; const Password: String; const IV: TBytes): String; var Codec: TCodec; CryptographicLibrary: TCryptographicLibrary; CipherStream, PlainTextStream: TMemoryStream; CipherBytes, PlainBytes: TBytes; SHA256Hasher: THashSHA2; KeyBytes: TBytes; begin Result := ''; CryptographicLibrary := TCryptographicLibrary.Create(nil); try Codec := TCodec.Create(nil); try // 配置Codec参数 Codec.CryptoLibrary := CryptographicLibrary; Codec.BlockCipherId := Format(AES_ProgId, [256]); Codec.ChainModeId := CBC_ProgId; // 生成和C#一致的SHA256密钥 SHA256Hasher := THashSHA2.Create; try KeyBytes := SHA256Hasher.GetHashBytes(TEncoding.UTF8.GetBytes(Password), SHA256); // 确保密钥是32字节(SHA256本来就是32字节,这里和C#的Array.Copy对齐) SetLength(KeyBytes, 32); finally SHA256Hasher.Free; end; // 设置密钥和IV Codec.SetKey(KeyBytes, Length(KeyBytes)*8); Codec.IV := IV; // 解码Base64密文到流 CipherBytes := TNetEncoding.Base64.DecodeStringToBytes(Value); CipherStream := TMemoryStream.Create(CipherBytes); try PlainTextStream := TMemoryStream.Create; try // 解密流 Codec.DecryptStream(PlainTextStream, CipherStream); // 将明文流转成UTF8字符串 SetLength(PlainBytes, PlainTextStream.Size); PlainTextStream.Position := 0; PlainTextStream.ReadBuffer(PlainBytes[0], Length(PlainBytes)); Result := TEncoding.UTF8.GetString(PlainBytes); finally PlainTextStream.Free; end; finally CipherStream.Free; end; finally Codec.Free; end; finally CryptographicLibrary.Free; end; end;
调用示例(和C#对齐)
var Password: String; IV: TBytes; EncryptedStr, DecryptedStr: String; begin Password := 'xxxxxxxxxxxxxxxxx'; // 初始化全0IV,和C#一致 SetLength(IV, 16); FillChar(IV[0], Length(IV), 0); // 假设EncryptedStr是从C#或PHP获取的Base64密文 DecryptedStr := DecryptAES256FromBase64String(EncryptedStr, Password, IV); end;
内容的提问来源于stack exchange,提问作者Jaroslav Dibitanzl
相关产品推荐
相关产品推荐

