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

DEC AES加密输出与固件/其他工具不一致问题排查

Delphi DEC套件AES加密与第三方工具/设备解密不匹配问题

使用Delphi的DEC加密套件编写固件AES加密代码,需与小型设备自带AES框架的解密逻辑同步,但输出与固件工具、在线AES工具无法匹配:

  • 密钥、IV全为0时,DEC输出仅与其他工具存在UINT32字节序差异;
  • 密钥含非0值(如$1, $0, $0, $0)时,输出完全不符,调整位序操作无效。

预期输出

固件工具及在线工具的加密输出为:D5 AA 45 05 7C A9 A2 6D 43 D1 63 36 84 1C 3D 2A

所用代码

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  DECBaseClass,
  DECCipherBase,
  DECHash,
  DECCiphers,
  DECTypes,
  DECFormatBase,
  DECFormat;

type
  Binary = RawByteString;

var
  ACipherClass: TDECCipherClass = TCipher_AES;
  ACipherMode: TCipherMode = cmCBCx;
  ATextFormat: TDECFormatClass = TFormat_Mime64;

  InvertUINT32 : boolean = False;
  InvertText : boolean = False;

type
  //TMK6AESTextSize = Array[0..15] of UInt32;
  TMK6AESTextSize = Array[0..3] of UInt32;
  TMK6Key = Array[0..3] of UINT32;

const mk6Key : TMK6Key = ($1, $0, $0, $0);
      //mk6Key : TMK6Key = ($09CF4F3C, $ABF71588, $28AED2A6, $2B7E1516 );  // rotated
      //mk6IV : TMK6Key = ($00010203 , $04050607 , $08090A0B , $0C0D0E0F);
      mk6IV : TMK6Key = ($0 , $0 , $0 , $0);
      //mk6IV : TMK6Key = ($0C0D0E0F, $08090A0B, $04050607, $00010203);

//      mk6Plaintext : TMK6AESTextSize =
//                   ( $E2BEC16B ,$969F402E ,$117E3DE9 ,$2A179373 ,
//                     $578A2DAE ,$9CAC031E ,$AC6FB79E ,$518EAF45 ,
//                     $461CC830 ,$11E45CA3 ,$19C1FBE5 ,$EF520A1A ,
//                     $45249FF6 ,$179B4FDF ,$7B412BAD ,$10376CE6 );

//      mk6Plaintext : TMK6AESTextSize =
//                   ( $E2BEC16B ,$969F402E ,$117E3DE9 ,$2A179373 );

      //mk6Plaintext : TMK6AESTextSize =
      //             ( $2A179373, $117E3DE9, $969F402E, $E2BEC16B );
      mk6Plaintext : TMK6AESTextSize = ( $0, $0, $0, $0 );

type
  TUINT32Byte = Array[0..3] of Byte;
  PUINT32Byte = ^TUINT32Byte;

function InvUINT32( value : UINT32 ) : UINT32;
var v1, v2 : PUINT32Byte;
begin
     v1 := @value;
     v2 := @Result;

     v2^[3] := v1^[0];
     v2^[2] := v1^[1];
     v2^[1] := v1^[2];
     v2^[0] := v1^[3];
end;

function GenMK6Text( const txt : TMK6AESTextSize ) : TMK6AESTextSize;
var i : integer;
begin
     if InvertText then
     begin
          for i := 0 to High(txt) do
              Result[i] := InvUINT32(txt[i]);
     end
     else
         Result := txt;
end;

function GenMK6Key( const aKey : TMK6Key ) : TMK6Key;
var i : integer;
begin
     Result := aKey;

     if InvertUINT32 then
     begin
          for i := 0 to High(aKey) do
              Result[i] := InvUINT32(aKey[i]);
     end;
end;


function EncryptBin(const buf : TMK6AESTextSize): Binary;
var AData: Binary;
    cipher : TCipher_AES128; //AES
    aKey : TMK6Key;
    aIV : TMK6Key;
    aBuf : TMK6AESTextSize;
begin
     cipher := TCipher_AES128.Create;
     try
        cipher.Mode := ACipherMode;
        aKey := GenMK6Key(mk6Key);
        aIV := GenMK6Key(mk6IV);
        aBuf := GenMK6Text(buf);
        cipher.Init( aKey, sizeof(mk6key), aIV, sizeof(mk6IV) );

        SetLength(AData, sizeof(buf));
        cipher.Encode(aBuf, AData[1], Length(AData));

        Result := AData;
     finally
            cipher.Free;
     end;
end;

function DecryptBin(buf : Binary): TMK6AESTextSize;
var AData: Binary;
    //ACheck: Binary;
    //APass: Binary;
    ALen: Integer;
    ARes : Binary;
    cipher : TCipher_AES128; 
    aKey : TMK6Key;
    aIV : TMK6Key;
begin
     cipher := TCipher_AES128.Create;
     try
        //AData := ValidFormat(ATextFormat).Decode(AText);
        AData := buf;
        ALen := Length(AData); // - cipher.Context.BufferSize;
        //ALen := Length(AData); // - cipher.Context.BufferSize;
        //ACheck := System.Copy(Adata, ALen + 1, cipher.Context.BufferSize);
        cipher.Mode := ACipherMode;
        aKey := GenMK6Key(mk6Key);
        aIV := GenMK6Key(mk6IV);
        cipher.Init(aKey, sizeof(mk6key), aIV, sizeof(mk6IV));
        SetLength(ARes, ALen);
        cipher.Decode(AData[1], ARes[1], ALen);
        //if ACheck <> cipher.CalcMAC then
//           raise Exception.Create('Invalid data');

        Assert(Length(ARes) >= sizeof(Result), 'WTF');
        Move(ARes[1], Result, sizeof(Result));
        Result := GenMK6Text(Result);
     finally
            cipher.Free;
     end;
end;

var binBuf : Binary;
    decBuf : TMK6AESTextSize;
    i : integer;
begin
  try
    binBuf := EncryptBin(mk6Plaintext);

    for i := 1 to Length(binBuf) do
    begin
         Write( IntToHex( Ord(binBuf[i]), 2 ) + ' ' );
    end;

    Writeln;

    decBuf := DecryptBin(binBuf);

    for i := 0 to Length(decBuf) - 1 do
        Writeln( IntToHex( decBuf[i], 4 ) );

    readln;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

问题原因分析

  1. DEC的CBC模式变种差异:代码中使用cmCBCx模式,这是DEC套件特有的带MAC或填充的CBC变种,而固件和在线工具使用的是标准CBC模式,两者逻辑不一致导致输出差异。
  2. 密钥的双重字节序处理缺失:当密钥非0时,仅反转单个UINT32的字节序不够——DEC可能会将UINT32数组按内存顺序直接拼接成密钥,而设备/在线工具可能要求UINT32数组整体反转后再拼接。比如密钥($1, $0, $0, $0),DEC直接用内存字节是01 00 00 00 00 00 00 00 ...,而设备可能需要00 00 00 00 00 00 00 00 ... 00 00 00 01。
  3. 明文/密钥的内存传递方式:直接将UINT32数组传递给DEC的Init和Encode方法,DEC对内存的解析顺序可能与设备预期不符,应该先手动转换为符合要求的字节数组再传递。

修正建议

  • 切换为标准CBC模式:将ACipherMode从cmCBCx改为cmCBC,确保与标准AES逻辑对齐。
  • 完整处理密钥/明文的字节序:
    // 示例:将UINT32数组转换为设备要求的字节数组
    function KeyToBytes(const Key: TMK6Key): RawByteString;
    var
      I: Integer;
      B: TUINT32Byte;
    begin
      Result := '';
      // 先反转UINT32数组的顺序,再反转每个UINT32的字节序
      for I := High(Key) downto Low(Key) do
      begin
        Move(Key[I], B, SizeOf(UINT32));
        Result := Result + Chr(B[3]) + Chr(B[2]) + Chr(B[1]) + Chr(B[0]);
      end;
    end;
    
  • 直接传递字节数组:在EncryptBin中,将明文、密钥、IV都转换为RawByteString后,再调用DEC的方法,避免内存解析差异。

内容的提问来源于stack exchange,提问作者mrabat

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 14:57:23