You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将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#的逻辑:

  1. 密钥初始化方式错误:Lockbox3的InitFromStream用于读取加密后的密钥容器,而非直接写入原始密钥字节。应直接用Codec.SetKey方法设置SHA256生成的32字节密钥。
  2. IV设置错误:IV需要通过Codec.IV属性直接赋值,不需要写入密文流中,C#里也是单独设置IV而非和密文混合。
  3. 结果处理完全错误:C#解密后是将明文字节转UTF8字符串,但你把解密后的字节又做了Base64编码,这会导致结果完全不符,应该直接转UTF8字符串。
  4. 冗余配置无效:Codec.AsymetricKeySizeInBits是用于非对称加密的,AES是对称加密,该设置无意义;Codec.Cipher := '[AES-256*]'属于冗余配置,BlockCipherId已经指定了AES-256。
  5. 字符串编码一致性问题: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 18:52:05