Delphiでお手軽プログラミング

Delphiでお手軽プログラミングメニュー

DelphiでAES暗号化AES復号化ができるユニット(クラス)


使用方法

ページ下部にMam.AES ユニットのソース一式を表示しています。
ファイル名「Mam.AES.pas」で保存して
uses Mam.AES;
で使用できます。(多分Delphi XE8以降で使用できます。)
推奨するモードは、AES-128-CBC、AES-192-CBC、AES-256-CBC、AES-128-CTR、AES-192-CTR、AES-256-CTRです。
AES-128-ECB、AES-192-ECB、AES-256-ECBは推奨しません。
また乱数生成などによるSalt値を使用することを推奨します。
ブロックサイズ鍵長 初期化ベクトルIV
AES-128-ECB128Bit(16Byte)128Bit(16Byte)無し
AES-192-ECB128Bit(16Byte)192Bit(24Byte)無し
AES-256-ECB128Bit(16Byte)256Bit(32Byte)無し
AES-128-CBC128Bit(16Byte)128Bit(16Byte)128Bit(16Byte)
AES-192-CBC128Bit(16Byte)192Bit(24Byte)128Bit(16Byte)
AES-256-CBC128Bit(16Byte)256Bit(32Byte)128Bit(16Byte)
AES-128-CTR128Bit(16Byte)128Bit(16Byte)128Bit(16Byte)
AES-192-CTR128Bit(16Byte)192Bit(24Byte)128Bit(16Byte)
AES-256-CTR128Bit(16Byte)256Bit(32Byte)128Bit(16Byte)

(1)使用例 AES-128-ECB(非推奨)

AESでECBの場合は初期化ベクトルIVを使用しない。
uses Mam.AES,
     System.NetEncoding;

procedure TForm1.Button1Click(Sender: TObject);
var aes:TMamAes;
    data,password:String;
    dec,key,enc:TBytes;
    st:string;
begin
  //暗号化するデータ
  data:='この文字列を暗号化します';
  //暗号化パスワード(そのまま鍵とする 16バイト)
  password:='1234567890123456';

  //暗号化するデータの準備(UTF8文字列としてバイナリ配列を取得する)
  dec:=TEncoding.UTF8.GetBytes(data);
  key:=TEncoding.ASCII.GetBytes(password);//暗号化鍵

  aes:=TMamAes.Create(TMamAesType.MamAES128ECB);
  //AES-128-ECBの暗号化を行う
  enc:=aes.Encrypt(dec,key);
  //AES-128-ECBの復号化を行う
  dec:=aes.Decrypt(enc,key);
  
  //復号化したデータ(バイト配列)をUTF8文字列に変換する
  st:=TEncoding.UTF8.GetString(dec);
  showmessage(st);

  aes.Free;
end;

(2)使用例2 AES-192-CBC

OpenSSLと同じようにSalt値を乱数で生成し、パスワード+ソルト値からMD5ハッシュで鍵を生成し、IVをMD5ハッシュで生成して暗号化復号化する関数を使う
uses Mam.AES,
     System.NetEncoding;

procedure TForm1.Button1Click(Sender: TObject);
var aes:TMamAes;
    data,password:String;
    dec,enc:TBytes;
    st:string;
begin
  aes:=TMamAes.Create(TmamAesType.MamAES192CBC);
  data:='日本語で機種依存文字㈱㈲㌔①②③でも使える';
  dec:=TEncoding.UTF8.GetBytes(data);
  password:='abcdefghijklmnop';
  //暗号化を行う
  enc:=aes.OpenSslEncrypt(dec,password);

  //復号化を行う
  dec:=aes.OpenSslDecrypt(enc,password);
  st:=TEncoding.UTF8.GetString(dec);
  showmessage(st);

  aes.Free;
end;


Mam.AES.pas

// 2020/12/03 CTRのバグ修正
// 2020/11/24 バグ修正
unit Mam.AES;

interface
uses Winapi.Windows,system.SysUtils;

type
  TMamAesType=(
    MamAES128ECB,MamAES192ECB,MamAES256ECB,
    MamAES128CBC,MamAES192CBC,MamAES256CBC,
    MamAES128CTR,MamAES192CTR,MamAES256CTR
  );
  TAesMatrix44=array[0..3] of array[0..3]of byte;
  PAesMatrix44=^TAesMatrix44;

  TMamAes=class(TObject)
    private
      fAesType:TMamAesType;
      fKeyNum:Byte;    //鍵長
      fKey:TBytes;     //鍵配列(128,192,256Bitの何れか)
      fIV:TBytes;      //CBC、CTR時の初期ベクトル(128Bit固定)
      fNumOfRound:Byte;//必要なラウンド処理の数
      //fRound:Byte;     //現在のラウンド数
      fNk:Byte;        //鍵長がNb(4バイト)単位でいくつになるか
      fRoundKey:TBytes;//ラウンド鍵
      fData:TBytes;    //暗号化復号化するデータを保持
      fIsECB:Boolean;  //ECB
      fIsCBC:Boolean;  //CBC
      fIsCTR:Boolean;  //CTR

      //Round用の鍵を生成する
      procedure KeyExpansion();
      //初期ベクトルIVの設定
      procedure SetIv(iv:TBytes);
      //暗号復号種類(ECB128,CBC128等)の設定
      procedure SetAesType(AesType:TMamAesType);
      //IVとの排他論理和を行う
      procedure XorIV(buf:PByte);
      procedure Cipher(State:PAesMatrix44);
      procedure invCipher(State:PAesMatrix44);
      procedure AddRoundKey(Round:Byte;State:PAesMatrix44);
      procedure SubBytes(State:PAesMatrix44);
      procedure InvSubBytes(State:PAesMatrix44);
      procedure ShiftRows(State:PAesMatrix44);
      procedure InvShiftRows(State:PAesMatrix44);
      procedure MixColumns(State:PAesMatrix44);
      procedure InvMixColumns(State:PAesMatrix44);
      function Multiply(x,y:Byte):Byte;
      function XTime(x:byte):byte;
    public
      //コンストラクタ
      constructor Create(AesType:TMamAesType);
      //暗号化
      function Encrypt(data,key:TBytes):TBytes;overload;
      function Encrypt(data,key,iv:TBytes):TBytes;overload;
      //復号化
      function Decrypt(data,key:TBytes):TBytes;overload;
      function Decrypt(data,key,iv:TBytes):TBytes;overload;
      //Base64Encode
      function Base64Encode(data:TBytes):String;
      //Base64Decode
      function Base64Decode(data:String):TBytes;
      //OpenSSLと同じようにSalt値を乱数で生成し、
      //パスワード+ソルト値からMD5ハッシュで鍵を生成し、
      //IVをMD5ハッシュで生成して暗号化復号化する関数
      function OpenSslEncrypt(data:TBytes;Password:String):TBytes;
      function OpenSslDecrypt(data:TBytes;Password:String):TBytes;
  end;

implementation

uses System.Hash, ////Delphi XE8以降で使えるユニット
     System.NetEncoding;


const
  //AESの列数(4バイト単位で暗号化する)
  Nb:Byte=4;
  //AESのブロックサイズ(128Bit[16バイト]固定)
  AES_BlockSize:Byte=16;

const
SBox:array[0..255]of byte=(
$63, $7c, $77, $7b, $f2, $6b, $6f, $c5, $30, $01, $67, $2b, $fe, $d7, $ab, $76,
$ca, $82, $c9, $7d, $fa, $59, $47, $f0, $ad, $d4, $a2, $af, $9c, $a4, $72, $c0,
$b7, $fd, $93, $26, $36, $3f, $f7, $cc, $34, $a5, $e5, $f1, $71, $d8, $31, $15,
$04, $c7, $23, $c3, $18, $96, $05, $9a, $07, $12, $80, $e2, $eb, $27, $b2, $75,
$09, $83, $2c, $1a, $1b, $6e, $5a, $a0, $52, $3b, $d6, $b3, $29, $e3, $2f, $84,
$53, $d1, $00, $ed, $20, $fc, $b1, $5b, $6a, $cb, $be, $39, $4a, $4c, $58, $cf,
$d0, $ef, $aa, $fb, $43, $4d, $33, $85, $45, $f9, $02, $7f, $50, $3c, $9f, $a8,
$51, $a3, $40, $8f, $92, $9d, $38, $f5, $bc, $b6, $da, $21, $10, $ff, $f3, $d2,
$cd, $0c, $13, $ec, $5f, $97, $44, $17, $c4, $a7, $7e, $3d, $64, $5d, $19, $73,
$60, $81, $4f, $dc, $22, $2a, $90, $88, $46, $ee, $b8, $14, $de, $5e, $0b, $db,
$e0, $32, $3a, $0a, $49, $06, $24, $5c, $c2, $d3, $ac, $62, $91, $95, $e4, $79,
$e7, $c8, $37, $6d, $8d, $d5, $4e, $a9, $6c, $56, $f4, $ea, $65, $7a, $ae, $08,
$ba, $78, $25, $2e, $1c, $a6, $b4, $c6, $e8, $dd, $74, $1f, $4b, $bd, $8b, $8a,
$70, $3e, $b5, $66, $48, $03, $f6, $0e, $61, $35, $57, $b9, $86, $c1, $1d, $9e,
$e1, $f8, $98, $11, $69, $d9, $8e, $94, $9b, $1e, $87, $e9, $ce, $55, $28, $df,
$8c, $a1, $89, $0d, $bf, $e6, $42, $68, $41, $99, $2d, $0f, $b0, $54, $bb, $16
);

InvSBox:array[0..255]of byte=(
$52, $09, $6a, $d5, $30, $36, $a5, $38, $bf, $40, $a3, $9e, $81, $f3, $d7, $fb,
$7c, $e3, $39, $82, $9b, $2f, $ff, $87, $34, $8e, $43, $44, $c4, $de, $e9, $cb,
$54, $7b, $94, $32, $a6, $c2, $23, $3d, $ee, $4c, $95, $0b, $42, $fa, $c3, $4e,
$08, $2e, $a1, $66, $28, $d9, $24, $b2, $76, $5b, $a2, $49, $6d, $8b, $d1, $25,
$72, $f8, $f6, $64, $86, $68, $98, $16, $d4, $a4, $5c, $cc, $5d, $65, $b6, $92,
$6c, $70, $48, $50, $fd, $ed, $b9, $da, $5e, $15, $46, $57, $a7, $8d, $9d, $84,
$90, $d8, $ab, $00, $8c, $bc, $d3, $0a, $f7, $e4, $58, $05, $b8, $b3, $45, $06,
$d0, $2c, $1e, $8f, $ca, $3f, $0f, $02, $c1, $af, $bd, $03, $01, $13, $8a, $6b,
$3a, $91, $11, $41, $4f, $67, $dc, $ea, $97, $f2, $cf, $ce, $f0, $b4, $e6, $73,
$96, $ac, $74, $22, $e7, $ad, $35, $85, $e2, $f9, $37, $e8, $1c, $75, $df, $6e,
$47, $f1, $1a, $71, $1d, $29, $c5, $89, $6f, $b7, $62, $0e, $aa, $18, $be, $1b,
$fc, $56, $3e, $4b, $c6, $d2, $79, $20, $9a, $db, $c0, $fe, $78, $cd, $5a, $f4,
$1f, $dd, $a8, $33, $88, $07, $c7, $31, $b1, $12, $10, $59, $27, $80, $ec, $5f,
$60, $51, $7f, $a9, $19, $b5, $4a, $0d, $2d, $e5, $7a, $9f, $93, $c9, $9c, $ef,
$a0, $e0, $3b, $4d, $ae, $2a, $f5, $b0, $c8, $eb, $bb, $3c, $83, $53, $99, $61,
$17, $2b, $04, $7e, $ba, $77, $d6, $26, $e1, $69, $14, $63, $55, $21, $0c, $7d
);
RCon:array[0..10]of byte=(
  $8d, $01, $02, $04, $08, $10, $20, $40, $80, $1b, $36);
MamBase64: String =
  'ABCDEFGHIJKLMNOPQRSTUVWXYZ'+
  'abcdefghijklmnopqrstuvwxyz'+
  '0123456789+/';


{ TMamAes }

procedure TMamAes.AddRoundKey(Round: Byte; State: PAesMatrix44);
var i,j:Cardinal;
begin
  for i := 0 to 3 do
    for j := 0 to 3 do
      state^[i][j]:=State^[i][j] xor fRoundKey[(Round*Nb*4)+(i*Nb)+j];
end;

function TMamAes.Base64Decode(data:String):TBytes;
var i,Len:integer;
    b:byte;
    i1,i2,i3,i4:integer;
begin
  SetLength(Result,0);
  i:=0;
  Len:=length(data);
  while i<Len do
  begin
    i1:=Pos(data.Substring(i,1),MamBase64)-1;
    inc(i);
    i2:=Pos(data.Substring(i,1),MamBase64)-1;
    inc(i);
    i3:=Pos(data.Substring(i,1),MamBase64)-1;
    inc(i);
    i4:=Pos(data.Substring(i,1),MamBase64)-1;
    if i3=-1 then
    begin
      b:=(i1 shl 2)+((i2 and $30) shr 4);
      SetLength(Result,Length(Result)+1);
      Result[Length(Result)-1]:=b;
    end
    else if i4=-1 then
    begin
      b:=(i1 shl 2)+((i2 and $30) shr 4);
      SetLength(Result,Length(Result)+1);
      Result[Length(Result)-1]:=b;
      b:=((i2 and $0f) shl 4) + ((i3 and $3c) shr 2);
      SetLength(Result,Length(Result)+1);
      Result[Length(Result)-1]:=b;
    end
    else
    begin
      b:=(i1 shl 2)+((i2 and $30) shr 4);
      SetLength(Result,Length(Result)+1);
      Result[Length(Result)-1]:=b;
      b:=((i2 and $0f) shl 4) + ((i3 and $3c) shr 2);
      SetLength(Result,Length(Result)+1);
      Result[Length(Result)-1]:=b;
      b:=((i3 and $03) shl 6)+ (i4 and $3f);
      SetLength(Result,Length(Result)+1);
      Result[Length(Result)-1]:=b;
    end;
    inc(i);
  end;
end;

function TMamAes.Base64Encode(data: TBytes): String;
var i,Len:Cardinal;
    enc:array[0..3] of String;
    b1,b2,b3:byte;
begin
  Len:=Length(data);
  result:='';
  i:=0;
  while i<Len do
  begin
    enc[0]:='';
    enc[1]:='';
    enc[2]:='';
    enc[3]:='';
    //ZeroMemory(@enc,4);
    b1:=data[i];
    enc[0]:=MamBase64.Substring((b1 and $fc) shr 2,1);
    inc(i);
    if i>=Len then
    begin
      enc[1]:=MamBase64.Substring((b1 and $3) shl 4,1);
      enc[2]:='=';
      enc[3]:='=';
    end
    else
    begin
      b2:=data[i];
      enc[1]:=MamBase64.Substring(((b1 and $3) shl 4 )+((b2 and $f0) shr 4),1);
      inc(i);
      if i>=Len then
      begin
        enc[2]:=MamBase64.Substring((b2 and $f) shl 2,1);
        enc[3]:='=';
      end
      else
      begin
        b3:=data[i];
        enc[2]:=MamBase64.Substring(((b2 and $f) shl 2)+((b3 and $c0) shr 6),1);
        enc[3]:=MamBase64.Substring(b3 and $3f,1);
      end;
    end;
    Result:=Result+enc[0]+enc[1]+enc[2]+enc[3];
    inc(i);
  end;
end;

procedure TMamAes.Cipher(State: PAesMatrix44);
var Round:Byte;
begin
  Round:=0;
  AddRoundKey(Round,State);
  inc(round);
  while true do
  begin
    SubBytes(State);
    shiftrows(State);
    if Round>=fNumOfRound then break;
    mixcolumns(State);
    AddRoundKey(Round,State);
    inc(Round);
  end;
  AddRoundKey(Round,State);
end;
procedure TMamAes.InvCipher(State: PAesMatrix44);
var Round:byte;
begin
  AddRoundKey(fNumOfRound,State);
  Round:=fNumOfRound-1;
  while true do
  begin
    InvShiftRows(State);
    InvSubBytes(State);
    AddRoundKey(Round,State);
    if round=0 then break;
    InvMixColumns(state);
    dec(Round);
  end;
end;

constructor TMamAes.Create(AesType:TMamAesType);
//コンストラクタ
begin
  inherited Create();
  setLength(fIV,AES_BlockSize);
  ZeroMemory(fIV,AES_BlockSize);
  SetAesType(AesType);
end;

function TMamAes.Decrypt(data, key: TBytes):TBytes;
var i,len,bi:Cardinal;
    NextIv:TBytes;
    buf:TBytes;
begin
  //鍵の設定
  len:=Length(key);
  SetLength(fkey,fKeyNum);
  ZeroMemory(@fkey[0],fKeyNum);
  if len>fKeyNum then len:=fKeyNum;
  move(key[0],fkey[0],len);

  KeyExpansion();

  //復号化するデータを保存
  len:=length(data);
  SetLength(fData,len);
  ZeroMemory(fData,len);
  Move(data[0],fData[0],len);

  if fIsECB or fIsCBC then
  begin
    //AES-ECB 又は AES-CBCの場合
    setlength(NextIv,AES_BlockSize);
    i:=0;
    while i<len do
    begin
      move(fData[i],NextIv[0],AES_BlockSize);
      invCipher(PAesMatrix44(@fData[i]));
      if fIsCBC then
      begin
        XorIv(@fData[i]);
        move(NextIv[0],fIV[0],AES_BlockSize);
      end;

      inc(i,AES_BlockSize);
    end;
    //#PKCS7
    len:=len-fData[len-1];
    setlength(fData,len);
  end
  else
  begin
    //AES-CTRの場合
    i:=0;
    bi:=AES_BlockSize;
    setlength(buf,AES_BlockSize);
    while i<len do
    begin
      if bi=AES_BlockSize then
      begin
        Move(fIV[0],buf[0],AES_BlockSize);
        Cipher(PAesMatrix44(@buf[0]));
        for bi := (AES_BlockSize-1) downto 0 do
        begin
          if (fIV[bi]=255) then
          begin
            fIV[bi]:=0;
          end
          else
          begin
            inc(fIV[bi]);
            break;
          end;
        end;
        bi:=0;
      end;
      fData[i] := (fData[i] xor buf[bi]);
      inc(i);
      inc(bi);
    end;
  end;
  Result:=fData;
end;

function TMamAes.Decrypt(data, key, iv: TBytes):TBytes;
begin
  SetIv(iv);
  Result:=Decrypt(data,key);
end;

function TMamAes.Encrypt(data, key: TBytes):TBytes;
var i,bi:Cardinal;
    Len:Cardinal;
    LenPKCS7:Cardinal;
    buf:TBytes;
begin
  //鍵の設定
  Len:=Length(key);
  SetLength(fkey,fKeyNum);
  ZeroMemory(@fkey[0],fKeyNum);
  if Len>fKeyNum then Len:=fKeyNum;
  move(key[0],fkey[0],Len);

  KeyExpansion();

  //暗号化するデータを保存
  Len:=length(data);
  SetLength(fData,Len);
  ZeroMemory(fData,Len);
  Move(data[0],fData[0],Len);


  if fIsECB or fIsCBC then
  begin
    //AES-ECB 又は AES-CBCの場合

    //#PKCS7 パディング
    LenPKCS7:=(Len div 16 * 16)+16;
    setlength(fData,LenPKCS7);
    for i := Len to LenPKCS7-1 do
      fData[i]:=byte(LenPKCS7-Len);
    Len:=LenPKCS7;

    i:=0;
    while i<Len do
    begin
      if fIsCBC then self.XorIV(@fData[i]);
      self.Cipher(PAesMatrix44(@fData[i]));
      if fIsCBC then
        Move(fData[i],fIV[0],AES_BlockSize);
      Inc(i,AES_BlockSize);
    end;
  end
  else
  begin
    //AES-CTRの場合
    i:=0;
    bi:=AES_BlockSize;
    setlength(buf,AES_BlockSize);
    while i<len do
    begin
      if bi=AES_BlockSize then
      begin
        Move(fIV[0],buf[0],AES_BlockSize);
        Cipher(PAesMatrix44(@buf[0]));
        for bi := (AES_BlockSize-1) downto 0 do
        begin
          if (fIV[bi]=255) then
          begin
            fIV[bi]:=0;
          end
          else
          begin
            inc(fIV[bi]);
            break;
          end;
        end;
        bi:=0;
      end;
      fData[i] := (fData[i] xor buf[bi]);
      inc(i);
      inc(bi);
    end;
  end;
  result:=fData;
end;

function TMamAes.Encrypt(data, key, iv: TBytes):TBytes;
begin
  self.SetIv(iv);
  result:=self.Encrypt(data,key);
end;

procedure TMamAes.KeyExpansion;
var AES_keyExpSize:Cardinal;
    i,j,k:Cardinal;
    tmp:array[0..3] of Byte;
    b:Byte;
begin
  AES_keyExpSize:=176+32*0;
  if fNumOfRound=10 then AES_keyExpSize:=176+32*0;
  if fNumOfRound=12 then AES_keyExpSize:=176+32*1;
  if fNumOfRound=14 then AES_keyExpSize:=176+32*2;
  SetLength(fRoundKey,AES_keyExpSize);
  for i := 0 to fNk-1 do
  begin
    fRoundKey[i*4+0]:=fKey[i*4+0];
    fRoundKey[i*4+1]:=fKey[i*4+1];
    fRoundKey[i*4+2]:=fKey[i*4+2];
    fRoundKey[i*4+3]:=fKey[i*4+3];
  end;
  for i := fNk to Nb*(fNumOfRound+1)-1 do
  begin
    k:=(i-1)*4;
    tmp[0]:=fRoundKey[k+0];
    tmp[1]:=fRoundKey[k+1];
    tmp[2]:=fRoundKey[k+2];
    tmp[3]:=fRoundKey[k+3];
    if (i mod fNk)=0 then
    begin
      b:=tmp[0];
      tmp[0]:=tmp[1];
      tmp[1]:=tmp[2];
      tmp[2]:=tmp[3];
      tmp[3]:=b;

      tmp[0] := sbox[tmp[0]];
      tmp[1] := sbox[tmp[1]];
      tmp[2] := sbox[tmp[2]];
      tmp[3] := sbox[tmp[3]];

      tmp[0]:=tmp[0] xor Rcon[i div fNk];
    end;

    if fNumOfRound=14 then
    begin //aes256
      if (i mod fNk)=4 then
      begin
        tmp[0] := sbox[tmp[0]];
        tmp[1] := sbox[tmp[1]];
        tmp[2] := sbox[tmp[2]];
        tmp[3] := sbox[tmp[3]];
      end;
    end;
    j:=i*4;
    k:=(i-fNk)*4;
    fRoundKey[j+0]:=fRoundKey[k+0] xor tmp[0];
    fRoundKey[j+1]:=fRoundKey[k+1] xor tmp[1];
    fRoundKey[j+2]:=fRoundKey[k+2] xor tmp[2];
    fRoundKey[j+3]:=fRoundKey[k+3] xor tmp[3];
  end;
end;

procedure TMamAes.MixColumns(State: PAesMatrix44);
var i:integer;
    tmp,tmp1,tmp2:byte;
begin
  for i := 0 to 3 do
  begin
    tmp :=State^[i][0];
    tmp1:=State^[i][0] xor State^[i][1] xor
          State^[i][2] xor State^[i][3];
    tmp2:=State^[i][0] xor State^[i][1];
    tmp2:=XTime(tmp2);
    State^[i][0]:= State^[i][0] xor tmp2 xor tmp1;
    tmp2:=State^[i][1] xor State^[i][2];
    tmp2:=XTime(tmp2);
    State^[i][1]:= State^[i][1] xor tmp2 xor tmp1;
    tmp2:=State^[i][2] xor State^[i][3];
    tmp2:=XTime(tmp2);
    State^[i][2]:= State^[i][2] xor tmp2 xor tmp1;
    tmp2:=State^[i][3] xor tmp;
    tmp2:=XTime(tmp2);
    State^[i][3]:= State^[i][3] xor tmp2 xor tmp1;
  end;
end;
procedure TMamAes.InvMixColumns(State: PAesMatrix44);
var i:Cardinal;
    tmp1,tmp2,tmp3,tmp4:byte;
begin
  for i := 0 to 3 do
  begin
    tmp1:=State^[i][0];
    tmp2:=State^[i][1];
    tmp3:=State^[i][2];
    tmp4:=State^[i][3];
    State^[i][0]:=
      Multiply(tmp1,$0e) xor Multiply(tmp2,$0b) xor
      Multiply(tmp3,$0d) xor Multiply(tmp4,$09);
    State^[i][1]:=
      Multiply(tmp1,$09) xor Multiply(tmp2,$0e) xor
      Multiply(tmp3,$0b) xor Multiply(tmp4,$0d);
    State^[i][2]:=
      Multiply(tmp1,$0d) xor Multiply(tmp2,$09) xor
      Multiply(tmp3,$0e) xor Multiply(tmp4,$0b);
    State^[i][3]:=
      Multiply(tmp1,$0b) xor Multiply(tmp2,$0d) xor
      Multiply(tmp3,$09) xor Multiply(tmp4,$0e);
  end;
end;

procedure TMamAes.SetAesType(AesType: TMamAesType);
//AES種別の設定(ECB,CBC,CTR)
begin
  fIsECB:=False;
  fIsCBC:=False;
  fIsCTR:=False;
  //鍵長の設定
  fAesType:=AesType;
  if (fAesType=MamAES128ECB) or
     (fAesType=MamAES128CBC) or
     (fAesType=MamAES128CTR) then
    fKeyNum:=16
  else if (fAesType=MamAES192ECB) or
          (fAesType=MamAES192CBC) or
          (fAesType=MamAES192CTR) then
    fKeyNum:=24
  else
    fKeyNum:=32;
  if (fAesType=MamAES128ECB) or
     (fAesType=MamAES192ECB) or
     (fAesType=MamAES256ECB) then fIsECB:=True;
  if (fAesType=MamAES128CBC) or
     (fAesType=MamAES192CBC) or
     (fAesType=MamAES256CBC) then fIsCBC:=True;
  if (fAesType=MamAES128CTR) or
     (fAesType=MamAES192CTR) or
     (fAesType=MamAES256CTR) then fIsCTR:=True;

  fNk:=fKeyNum div Nb;//Nb(4バイト)単位でいくつか
  fNumOfRound:=fNk+6; //ラウンド処理数
end;

procedure TMamAes.SetIv(iv: TBytes);
var len:Cardinal;
//初期ベクトルIVの設定
begin
  setLength(fIV,AES_BlockSize);
  ZeroMemory(@fIV[0],AES_BlockSize);
  len:=length(iv);
  if len>AES_BlockSize then len:=AES_BlockSize;
  Move(iv[0],fIV[0],len);
end;

procedure TMamAes.ShiftRows(State: PAesMatrix44);
var tmp:Byte;
begin
  tmp         :=State^[0][1];
  State^[0][1]:=State^[1][1];
  State^[1][1]:=State^[2][1];
  State^[2][1]:=State^[3][1];
  State^[3][1]:=tmp;
  tmp         :=State^[0][2];
  State^[0][2]:=State^[2][2];
  State^[2][2]:=tmp;
  tmp         :=State^[1][2];
  State^[1][2]:=State^[3][2];
  State^[3][2]:=tmp;
  tmp         :=State^[0][3];
  State^[0][3]:=State^[3][3];
  State^[3][3]:=State^[2][3];
  State^[2][3]:=State^[1][3];
  State^[1][3]:=tmp;
end;
procedure TMamAes.InvShiftRows(State: PAesMatrix44);
var tmp:Byte;
begin
  tmp         :=state^[3][1];
  state^[3][1]:=state^[2][1];
  state^[2][1]:=state^[1][1];
  state^[1][1]:=state^[0][1];
  state^[0][1]:=tmp;
  tmp         :=state^[0][2];
  state^[0][2]:=state^[2][2];
  state^[2][2]:=tmp;
  tmp         :=state^[1][2];
  state^[1][2]:=state^[3][2];
  state^[3][2]:=tmp;
  tmp         :=state^[0][3];
  state^[0][3]:=state^[1][3];
  state^[1][3]:=state^[2][3];
  state^[2][3]:=state^[3][3];
  state^[3][3]:=tmp;
end;

procedure TMamAes.SubBytes(State: PAesMatrix44);
var i,j:integer;
begin
  for i := 0 to 3 do
    for j := 0 to 3 do
      State^[j][i]:= SBox[State^[j][i]];
end;
procedure TMamAes.InvSubBytes(State: PAesMatrix44);
var i,j:integer;
begin
  for i := 0 to 3 do
    for j := 0 to 3 do
      State^[j][i]:= InvSBox[State^[j][i]];
end;

procedure TMamAes.XorIV(buf: PByte);
var i:Cardinal;
begin
  for i := 0 to AES_BlockSize-1 do
  begin
    buf^:=buf^ xor fIV[i];
    inc(buf);
  end;
end;

function TMamAes.Multiply(x, y: Byte): Byte;
begin
  result:=(
    ((y and 1) * x) xor
    (((y shr 1) and 1)*xtime(x)) xor
    (((y shr 2) and 1)*xtime(xtime(x))) xor
    (((y shr 3) and 1)*xtime(xtime(xtime(x)))) xor
    (((y shr 4) and 1)*xtime(xtime(xtime(xtime(x)))))
  );
end;

function TMamAes.OpenSslDecrypt(data: TBytes; Password: String): TBytes;
var b1,b2,b3:TBytes;
    pass,salted,salt,iv,pass_salt,key:TBytes;
    i:Cardinal;
    flag:boolean;
    dec:TBytes;
    md5:THashMD5;
    data_nosalt:TBytes;
begin
  // 'Salted__'+8バイトのSalt値
  //暗号鍵 = MD5(password + salt)
  //IV = MD5(暗号鍵 + password + salt)

  result:=dec;
  if not ((fAesType=TMamAesType.MamAES128CTR) or
     (fAesType=TMamAesType.MamAES192CTR) or
     (fAesType=TMamAesType.MamAES256CTR)) then
  begin
    if length(data)<32 then exit;
  end;

  //ソルト値の処理
  salted:=TEncoding.ASCII.GetBytes('Salted__');
  flag:=false;
  for i := 0 to 7 do
    if data[i]<>salted[i] then flag:=true;
  if flag then exit;
  setlength(salt,8);
  move(data[8],salt[0],8);

  setlength(data_nosalt,length(data)-16);
  move(data[16],data_nosalt[0],length(data)-16);

  //パスワード配列
  pass:=TEncoding.ASCII.GetBytes(Password);

  //pass+saltのバイト配列生成
  SetLength(pass_salt,Length(pass)+Length(salt));
  Move(pass[0],pass_salt[0],Length(pass));
  Move(salt[0],pass_salt[Length(pass)],Length(salt));

  md5:=THashMD5.Create;
  md5.Reset;
  md5.Update(pass_salt);
  b1:=md5.HashAsBytes;

  md5.Reset;
  setlength(b2,16+Length(pass_salt));
  move(b1[0],b2[0],length(b1));
  move(pass_salt[0],b2[length(b1)],length(pass_salt));
  md5.Update(b2);
  b2:=md5.HashAsBytes;

  md5.Reset;
  setlength(b3,16+Length(pass_salt));
  move(b2[0],b3[0],length(b2));
  move(pass_salt[0],b3[length(b2)],length(pass_salt));
  md5.Update(b3);
  b3:=md5.HashAsBytes;

  setlength(iv,16);
  if fKeyNum=16 then
  begin
    setlength(key,16);
    move(b1[0],key[0],16);
    move(b2[0],iv[0],16);
  end
  else if fKeyNum=24 then
  begin
    setlength(key,24);
    move(b1[0],key[0],16);
    move(b2[0],key[16],8);
    move(b2[8],iv[0],8);
    move(b3[0],iv[8],8);
  end
  else
  begin
    setlength(key,32);
    move(b1[0],key[0],16);
    move(b2[0],key[16],16);
    move(b3[0],iv[0],16);
  end;
  dec:=self.Decrypt(data_nosalt,key,iv);
  result:=dec;
end;

function TMamAes.OpenSslEncrypt(data: TBytes; Password: String): TBytes;
var i:Cardinal;
    md5:THashMD5; //Delphi XE8以降で使えるクラス
    salt,pass,pass_salt:TBytes;
    b1,b2,b3:TBytes;
    key,iv:TBytes;
    enc:TBytes;
    salted:TBytes;
    res:TBytes;
begin
  // 'Salted__'+8バイトのSalt値
  //暗号鍵 = MD5(password + salt)
  //IV = MD5(暗号鍵 + password + salt)

  pass:=TEncoding.ASCII.GetBytes(Password);

  //ランダムなSalt値の計算
  Randomize;
  SetLength(salt,8);
  for i := 0 to 7 do
    salt[i]:=Random(256);

  //pass+saltのバイト配列生成
  SetLength(pass_salt,Length(pass)+Length(salt));
  Move(pass[0],pass_salt[0],Length(pass));
  Move(salt[0],pass_salt[Length(pass)],Length(salt));

  md5:=THashMD5.Create;
  md5.Reset;
  md5.Update(pass_salt);
  b1:=md5.HashAsBytes;

  md5.Reset;
  setlength(b2,16+Length(pass_salt));
  move(b1[0],b2[0],length(b1));
  move(pass_salt[0],b2[length(b1)],length(pass_salt));
  md5.Update(b2);
  b2:=md5.HashAsBytes;

  md5.Reset;
  setlength(b3,16+Length(pass_salt));
  move(b2[0],b3[0],length(b2));
  move(pass_salt[0],b3[length(b2)],length(pass_salt));
  md5.Update(b3);
  b3:=md5.HashAsBytes;

  setlength(iv,16);
  if fKeyNum=16 then
  begin
    setlength(key,16);
    move(b1[0],key[0],16);
    move(b2[0],iv[0],16);
  end
  else if fKeyNum=24 then
  begin
    setlength(key,24);
    move(b1[0],key[0],16);
    move(b2[0],key[16],8);
    move(b2[8],iv[0],8);
    move(b3[0],iv[8],8);
  end
  else
  begin
    setlength(key,32);
    move(b1[0],key[0],16);
    move(b2[0],key[16],16);
    move(b3[0],iv[0],16);
  end;
  enc:=self.Encrypt(data,key,iv);

  salted:=TEncoding.ASCII.GetBytes('Salted__');

  setlength(res,length(salted)+length(salt)+length(enc));
  move(salted[0],res[0],length(salted));
  move(salt[0],res[8],length(salt));
  move(enc[0],res[16],length(enc));
  result:=res;
end;

function TMamAes.XTime(x: byte): byte;
begin
  result:=((x shl 1) xor (((x shr 7) and 1) * $1b));
end;

end.

Copyright 2020 Mam