4

Delphi の文字列をパスワードで AES-128 暗号化したいと思います。これをサーバーにアップロードし、C# で同じパスワードを指定して復号化できるようにしたいと考えています。

Delphi では、TurboPower LockBox 3 を使用しています。

function EncryptText_AES_128(input: string; password: string): string;
var
  Codec: TCodec;
  CipherText: AnsiString;
begin
  Codec := TCodec.Create(nil);
  try
    Codec.CryptoLibrary := TCryptographicLibrary.Create(Codec);
    //
    Codec.StreamCipherId := BlockCipher_ProgID;
    Codec.BlockCipherId := Format(AES_ProgId, [128]);
    Codec.ChainModeId := CBC_ProgId;
    //
    Codec.Password := Password;
    Codec.EncryptString(input, CipherText);
    //
    Result := string(CipherText);
  finally
    Codec.Free;
  end;
end;

結果の文字列をC#で復号化するにはどうすればよいですか? Delphi コードを変更できます。まだ何も生産されていません。私は LockBox の使用にこだわっていません。しかし、これを P/Invoke の DLL に入れることは避けたいと思います。

(私の例では、暗号化されたシーケンス自体が文字列であることを示しています。これは私の要件ではありません。バイトのストリームは問題ありません。)

4

5 に答える 5

16

AES-128 の Delphi と C# の間で互換性のあるソリューションをついに見つけました。Wineでも動作します。これが私のDelphiコードです:

unit TntLXCryptoUtils;

interface

function AES128_Encrypt(Value, Password: string): string;
function AES128_Decrypt(Value, Password: string): string;

implementation

uses
  SysUtils, Windows, IdCoderMIME, TntLXUtils;

//-------------------------------------------------------------------------------------------------------------------------
//    Base64 Encode/Decode
//-------------------------------------------------------------------------------------------------------------------------

function Base64_Encode(Value: TBytes): string;
var
  Encoder: TIdEncoderMIME;
begin
  Encoder := TIdEncoderMIME.Create(nil);
  try
    Result := Encoder.EncodeBytes(Value);
  finally
    Encoder.Free;
  end;
end;

function Base64_Decode(Value: string): TBytes;
var
  Encoder: TIdDecoderMIME;
begin
  Encoder := TIdDecoderMIME.Create(nil);
  try
    Result := Encoder.DecodeBytes(Value);
  finally
    Encoder.Free;
  end;
end;

//-------------------------------------------------------------------------------------------------------------------------
//    WinCrypt.h
//-------------------------------------------------------------------------------------------------------------------------

type
  HCRYPTPROV  = Cardinal;
  HCRYPTKEY   = Cardinal;
  ALG_ID      = Cardinal;
  HCRYPTHASH  = Cardinal;

const
  _lib_ADVAPI32    = 'ADVAPI32.dll';
  CALG_SHA_256     = 32780;
  CALG_AES_128     = 26126;
  CRYPT_NEWKEYSET  = $00000008;
  PROV_RSA_AES     = 24;
  KP_MODE          = 4;
  CRYPT_MODE_CBC   = 1;

function CryptAcquireContext(var Prov: HCRYPTPROV; Container: PChar; Provider: PChar; ProvType: LongWord; Flags: LongWord): LongBool; stdcall; external _lib_ADVAPI32 name 'CryptAcquireContextW';
function CryptDeriveKey(Prov: HCRYPTPROV; Algid: ALG_ID; BaseData: HCRYPTHASH; Flags: LongWord; var Key: HCRYPTKEY): LongBool; stdcall; external _lib_ADVAPI32 name 'CryptDeriveKey';
function CryptSetKeyParam(hKey: HCRYPTKEY; dwParam: LongInt; pbData: PBYTE; dwFlags: LongInt): LongBool stdcall; stdcall; external _lib_ADVAPI32 name 'CryptSetKeyParam';
function CryptEncrypt(Key: HCRYPTKEY; Hash: HCRYPTHASH; Final: LongBool; Flags: LongWord; pbData: PBYTE; var Len: LongInt; BufLen: LongInt): LongBool;stdcall;external _lib_ADVAPI32 name 'CryptEncrypt';
function CryptDecrypt(Key: HCRYPTKEY; Hash: HCRYPTHASH; Final: LongBool; Flags: LongWord; pbData: PBYTE; var Len: LongInt): LongBool; stdcall; external _lib_ADVAPI32 name 'CryptDecrypt';
function CryptCreateHash(Prov: HCRYPTPROV; Algid: ALG_ID; Key: HCRYPTKEY; Flags: LongWord; var Hash: HCRYPTHASH): LongBool; stdcall; external _lib_ADVAPI32 name 'CryptCreateHash';
function CryptHashData(Hash: HCRYPTHASH; Data: PChar; DataLen: LongWord; Flags: LongWord): LongBool; stdcall; external _lib_ADVAPI32 name 'CryptHashData';
function CryptReleaseContext(hProv: HCRYPTPROV; dwFlags: LongWord): LongBool; stdcall; external _lib_ADVAPI32 name 'CryptReleaseContext';
function CryptDestroyHash(hHash: HCRYPTHASH): LongBool; stdcall; external _lib_ADVAPI32 name 'CryptDestroyHash';
function CryptDestroyKey(hKey: HCRYPTKEY): LongBool; stdcall; external _lib_ADVAPI32 name 'CryptDestroyKey';

//-------------------------------------------------------------------------------------------------------------------------

{$WARN SYMBOL_PLATFORM OFF}

function __CryptAcquireContext(ProviderType: Integer): HCRYPTPROV;
begin
  if (not CryptAcquireContext(Result, nil, nil, ProviderType, 0)) then
  begin
    if HRESULT(GetLastError) = NTE_BAD_KEYSET then
      Win32Check(CryptAcquireContext(Result, nil, nil, ProviderType, CRYPT_NEWKEYSET))
    else
      RaiseLastOSError;
  end;
end;

function __AES128_DeriveKeyFromPassword(m_hProv: HCRYPTPROV; Password: string): HCRYPTKEY;
var
  hHash: HCRYPTHASH;
  Mode: DWORD;
begin
  Win32Check(CryptCreateHash(m_hProv, CALG_SHA_256, 0, 0, hHash));
  try
    Win32Check(CryptHashData(hHash, PChar(Password), Length(Password) * SizeOf(Char), 0));
    Win32Check(CryptDeriveKey(m_hProv, CALG_AES_128, hHash, 0, Result));
    // Wine uses a different default mode of CRYPT_MODE_EBC
    Mode := CRYPT_MODE_CBC;
    Win32Check(CryptSetKeyParam(Result, KP_MODE, Pointer(@Mode), 0));
  finally
    CryptDestroyHash(hHash);
  end;
end;

function AES128_Encrypt(Value, Password: string): string;
var
  hCProv: HCRYPTPROV;
  hKey: HCRYPTKEY;
  lul_datalen: Integer;
  lul_buflen: Integer;
  Buffer: TBytes;
begin
  Assert(Password <> '');
  if (Value = '') then
    Result := ''
  else begin
    hCProv := __CryptAcquireContext(PROV_RSA_AES);
    try
      hKey := __AES128_DeriveKeyFromPassword(hCProv, Password);
      try
        // allocate buffer space
        lul_datalen := Length(Value) * SizeOf(Char);
        Buffer := TEncoding.Unicode.GetBytes(Value + '        ');
        lul_buflen := Length(Buffer);
        // encrypt to buffer
        Win32Check(CryptEncrypt(hKey, 0, True, 0, @Buffer[0], lul_datalen, lul_buflen));
        SetLength(Buffer, lul_datalen);
        // base 64 result
        Result := Base64_Encode(Buffer);
      finally
        CryptDestroyKey(hKey);
      end;
    finally
      CryptReleaseContext(hCProv, 0);
    end;
  end;
end;

function AES128_Decrypt(Value, Password: string): string;
var
  hCProv: HCRYPTPROV;
  hKey: HCRYPTKEY;
  lul_datalen: Integer;
  Buffer: TBytes;
begin
  Assert(Password <> '');
  if Value = '' then
    Result := ''
  else begin
    hCProv := __CryptAcquireContext(PROV_RSA_AES);
    try
      hKey := __AES128_DeriveKeyFromPassword(hCProv, Password);
      try
        // decode base64
        Buffer := Base64_Decode(Value);
        // allocate buffer space
        lul_datalen := Length(Buffer);
        // decrypt buffer to to string
        Win32Check(CryptDecrypt(hKey, 0, True, 0, @Buffer[0], lul_datalen));
        Result := TEncoding.Unicode.GetString(Buffer, 0, lul_datalen);
      finally
        CryptDestroyKey(hKey);
      end;
    finally
      CryptReleaseContext(hCProv, 0);
    end;
  end;
end;

end.

そして、ここに私のC#コードがあります:

public class TntCryptoUtils
{
    private static ICryptoTransform __Get_AES128_Transform(string password, bool AsDecryptor)
    {
        const int KEY_SIZE = 16;
        var sha256CryptoServiceProvider = new SHA256CryptoServiceProvider();
        var hash = sha256CryptoServiceProvider.ComputeHash(Encoding.Unicode.GetBytes(password));
        var key = new byte[KEY_SIZE];
        var iv = new byte[KEY_SIZE];
        Buffer.BlockCopy(hash, 0, key, 0, KEY_SIZE);
        //Buffer.BlockCopy(hash, KEY_SIZE, iv, 0, KEY_SIZE); // On the Windows side, the IV is always 0 (zero)
        //
        if (AsDecryptor)
            return new AesCryptoServiceProvider().CreateDecryptor(key, iv);
        else
            return new AesCryptoServiceProvider().CreateEncryptor(key, iv);
    }

    public static string AES128_Encrypt(string Value, string Password)
    {
        byte[] Buffer = Encoding.Unicode.GetBytes(Value);
        //
        using (ICryptoTransform transform = __Get_AES128_Transform(Password, false))
        {
            byte[] encyptedBlob = transform.TransformFinalBlock(Buffer, 0, Buffer.Length);
            return Convert.ToBase64String(encyptedBlob);
        }
    }

    public static string AES128_Decrypt(string Value, string Password)
    {
        byte[] Buffer = Convert.FromBase64String(Value);
        //
        using (ICryptoTransform transform = __Get_AES128_Transform(Password, true))
        {
            byte[] decyptedBlob = transform.TransformFinalBlock(Buffer, 0, Buffer.Length);
            return Encoding.Unicode.GetString(decyptedBlob);
        }
    }
}
于 2012-02-15T18:02:14.167 に答える
10

あなたが読むかもしれないトロールの炎のおとりとは対照的に、LockBox 3 は実際には高品質の暗号化ライブラリです。LB3 の規格への準拠は申し分ありません。他の言語やライブラリとの相互運用性に問題がある可能性があるのは、標準外のオプションに関連しています。Delphi 側で Lockbox を使用する場合は、これらのオプションが他の言語側でも同じように処理されることを確認する必要があります。これが不可能な場合は、別のライブラリを選択する必要があります。これらの各オプションについては、以下で説明します。

代替ソリューション (OpenSSL、CryptoAPI、および Eldos) に問題はありません。それらのいくつかはブラックボックスかもしれません。これは、一部の人にとっては問題になる可能性があります。

  1. パスワードをキーに変換します。AES-128 は 16 バイトのキーを使用します。また、「キー データ」または「パスワード データ」からキーを生成する標準メカニズムは、16 バイトの入力シードにネイティブに基づいています。文字列パスワードを転送するよりも、Delphi 側で文字列パスワードからバイナリ キーを生成し、バイナリ キーを反対側に転送する方が、相互運用性にとってより安全です。これは、文字列パスワードを 16 バイトのバイナリ キーに変換するアルゴリズムが AES 標準外であるためです。それどころか、どちらの方法でも実行できます。AES-128 コーデックを初期化するために lockbox に文字列パスワードが与えられると、lockbox は文字列ペイロードをバイトの配列と見なします。ペイロードが正確に 16 バイトである場合、標準で指定されている AES キー生成アルゴリズムに直接渡すことができます。文字列ペイロードが正確に 16 バイトでない場合、ペイロードは SHA-1 でハッシュされ、20 バイトのハッシュ出力が生成されます。このハッシュの下位 16 バイトは、標準の AES キー生成関数に渡されます。したがって、キーの初期化に関する相互運用性を確保するためのオプションは次のとおりです。

    1.1。文字列パスワードの代わりにバイナリ キーを転送します。

    1.2. オプション 1.2 があまりにも不便な場合は、パスワードを転送しますが、反対側で同じパスワードからキーへのアルゴリズムを模倣します。

    1.3。1 と 2 が何らかの理由で機能しない場合は、パスワードを正確に 16 バイト (8 UTF-8 文字または 16 UTF-16 コードポイント) に制限してみてください。他の言語の実装が半分まともであれば、これはかなり安全なはずです。

  2. UTF-16 対 ansi-string/UTF-8 パスワード これはオプションではなく、若いプレーヤーにとっては罠です。私たちプログラマーは、「文字列」を「文字列」と考えがちです。しかし、そうではありません。Delphi 2010 では、文字列のペイロードは、コード単位サイズが 2 バイトの UTF-16LE エンコーディングで格納されます。しかし、PHP や python などの他の言語では、デフォルト モードでは、文字列はシングルバイトのコード単位エンコーディングであり、UTF-8 または MS Windows コード ページ ベースに基づいたもの (MS はこれを「ansistring」と呼びます) です。「mypassword」の UTF-16 エンコーディングは、UTF-8「mypassword」と同じではないことに注意してください。

  3. IVセットアップ。AES 標準は、コーデックの初期化ベクトル (IV) を設定する方法の問題を扱っていません。IV のサイズは、基礎となるブロックのサイズと同じです。AES の場合、これは 128 ビットまたは 16 バイトです。暗号化するとき、lockbox は 16 バイトの nonce を作成します。このナンスは IV の値になり、暗号文メッセージの先頭に平文で送信されます。IV 初期化の反対側のメソッド/ポリシーに関するドキュメントをお読みください。オプションは次のとおりです。

    3.1 反対側が IV を暗号文の先頭に追加する場合、あなたは親切です。

    3.2 それ以外の場合は、復号化するときに、暗号文の最初の 16 バイトを自分で読み取り、残りを外部コーデックに渡します。復号化する前に、IV が何であるかを外国のコーデックに伝えます (その API がこれに対応していると仮定します)。

  4. ブロック量子化 AES ブロック サイズは 16 バイトです。平文メッセージが正確に 16 バイトの整数倍でない場合、整数倍にするために何かを行う必要があります。この手順はブロック量子化と呼ばれ、標準では扱われておらず、実装に任されています。多くの実装では、ブロック パディングが使用されます。標準のブロック パディング スキームはなく、多くの選択肢があります。LockBox は CBC にブロック パディングを使用しません (他のモードでは異なる場合があります)。平文がブロックの整数である場合、量子化は不要であるか実行されません。それ以外の場合は、標準の CipherText 盗用が使用されます。平文のサイズが非常に小さい場合 (1 ~ 15 バイト)、暗号文を盗むことはできず、代わりにパディング スキームが使用されます。ブロックの量子化に関する相互運用性を確保するには、次のオプションがあります。

    4.1 ブロックの量子化に関連する外部コーデックのドキュメントを確認してください (「メッセージ パディング」という見出しの下にある場合があります)。外国のコーデックが暗号文の盗用を使用している場合、あなたは甘いです (ショート メッセージがないことを確認してください)。

    4.2 それ以外の場合は、独自のパディングを行うことができます。lockbox 側では、lockbox は既にブロック全体にあるメッセージに対して何もしません。おそらく、外国のコーデックにも同じポリシーがありますが、外国のコーデックのドキュメントを確認する必要があります。

于 2012-02-09T00:05:01.683 に答える
6

トロイの Delphi コードを 10.2 Tokyo にいくつかの変更を加えてうまく実装することができました。

Uses から TNTLxUtils を削除しましたが、これは不要だった (そして持っていなかった) ため、IdGlobal を追加しました。IdGlobal を使用する理由は、Base64_Encode 関数でタイプ TBytes を TIdBytes に変換し、Base64_Decode で TIBytes を TBytes に戻す必要があるためです。

注: このユニットは 32 ビット Windows API を参照するため、32 ビット アプリケーションでのみ動作します。

実装するためにツールキットを購入する必要のない無料の暗号化方法について正しい方向を示してくれた Troy に感謝します。

unit CryptoUtils;

interface

function AES128_Encrypt(Value, Password: string): string;
function AES128_Decrypt(Value, Password: string): string;

implementation

uses
  SysUtils, Windows, IdCoderMIME, IdGlobal;

//-------------------------------------------------------------------------------------------------------------------------
//    Base64 Encode/Decode
//-------------------------------------------------------------------------------------------------------------------------

function Base64_Encode(Value: TBytes): string;
var
  Encoder: TIdEncoderMIME;
begin
  Encoder := TIdEncoderMIME.Create(nil);
  try
    Result := Encoder.EncodeBytes(TIdBytes(Value));
  finally
    Encoder.Free;
  end;
end;

function Base64_Decode(Value: string): TBytes;
var
  Encoder: TIdDecoderMIME;
begin
  Encoder := TIdDecoderMIME.Create(nil);
  try
    Result := TBytes(Encoder.DecodeBytes(Value));
  finally
    Encoder.Free;
  end;
end;

//-------------------------------------------------------------------------------------------------------------------------
//    WinCrypt.h
//-------------------------------------------------------------------------------------------------------------------------

type
  HCRYPTPROV  = Cardinal;
  HCRYPTKEY   = Cardinal;
  ALG_ID      = Cardinal;
  HCRYPTHASH  = Cardinal;

const
  _lib_ADVAPI32    = 'ADVAPI32.dll';
  CALG_SHA_256     = 32780;
  CALG_AES_128     = 26126;
  CRYPT_NEWKEYSET  = $00000008;
  PROV_RSA_AES     = 24;
  KP_MODE          = 4;
  CRYPT_MODE_CBC   = 1;

function CryptAcquireContext(var Prov: HCRYPTPROV; Container: PChar; Provider: PChar; ProvType: LongWord; Flags: LongWord): LongBool; stdcall; external _lib_ADVAPI32 name 'CryptAcquireContextW';
function CryptDeriveKey(Prov: HCRYPTPROV; Algid: ALG_ID; BaseData: HCRYPTHASH; Flags: LongWord; var Key: HCRYPTKEY): LongBool; stdcall; external _lib_ADVAPI32 name 'CryptDeriveKey';
function CryptSetKeyParam(hKey: HCRYPTKEY; dwParam: LongInt; pbData: PBYTE; dwFlags: LongInt): LongBool stdcall; stdcall; external _lib_ADVAPI32 name 'CryptSetKeyParam';
function CryptEncrypt(Key: HCRYPTKEY; Hash: HCRYPTHASH; Final: LongBool; Flags: LongWord; pbData: PBYTE; var Len: LongInt; BufLen: LongInt): LongBool;stdcall;external _lib_ADVAPI32 name 'CryptEncrypt';
function CryptDecrypt(Key: HCRYPTKEY; Hash: HCRYPTHASH; Final: LongBool; Flags: LongWord; pbData: PBYTE; var Len: LongInt): LongBool; stdcall; external _lib_ADVAPI32 name 'CryptDecrypt';
function CryptCreateHash(Prov: HCRYPTPROV; Algid: ALG_ID; Key: HCRYPTKEY; Flags: LongWord; var Hash: HCRYPTHASH): LongBool; stdcall; external _lib_ADVAPI32 name 'CryptCreateHash';
function CryptHashData(Hash: HCRYPTHASH; Data: PChar; DataLen: LongWord; Flags: LongWord): LongBool; stdcall; external _lib_ADVAPI32 name 'CryptHashData';
function CryptReleaseContext(hProv: HCRYPTPROV; dwFlags: LongWord): LongBool; stdcall; external _lib_ADVAPI32 name 'CryptReleaseContext';
function CryptDestroyHash(hHash: HCRYPTHASH): LongBool; stdcall; external _lib_ADVAPI32 name 'CryptDestroyHash';
function CryptDestroyKey(hKey: HCRYPTKEY): LongBool; stdcall; external _lib_ADVAPI32 name 'CryptDestroyKey';

//-------------------------------------------------------------------------------------------------------------------------

{$WARN SYMBOL_PLATFORM OFF}

function __CryptAcquireContext(ProviderType: Integer): HCRYPTPROV;
begin
  if (not CryptAcquireContext(Result, nil, nil, ProviderType, 0)) then
  begin
    if HRESULT(GetLastError) = NTE_BAD_KEYSET then
      Win32Check(CryptAcquireContext(Result, nil, nil, ProviderType, CRYPT_NEWKEYSET))
    else
      RaiseLastOSError;
  end;
end;

function __AES128_DeriveKeyFromPassword(m_hProv: HCRYPTPROV; Password: string): HCRYPTKEY;
var
  hHash: HCRYPTHASH;
  Mode: DWORD;
begin
  Win32Check(CryptCreateHash(m_hProv, CALG_SHA_256, 0, 0, hHash));
  try
    Win32Check(CryptHashData(hHash, PChar(Password), Length(Password) * SizeOf(Char), 0));
    Win32Check(CryptDeriveKey(m_hProv, CALG_AES_128, hHash, 0, Result));
    // Wine uses a different default mode of CRYPT_MODE_EBC
    Mode := CRYPT_MODE_CBC;
    Win32Check(CryptSetKeyParam(Result, KP_MODE, Pointer(@Mode), 0));
  finally
    CryptDestroyHash(hHash);
  end;
end;

function AES128_Encrypt(Value, Password: string): string;
var
  hCProv: HCRYPTPROV;
  hKey: HCRYPTKEY;
  lul_datalen: Integer;
  lul_buflen: Integer;
  Buffer: TBytes;
begin
  Assert(Password <> '');
  if (Value = '') then
    Result := ''
  else begin
    hCProv := __CryptAcquireContext(PROV_RSA_AES);
    try
      hKey := __AES128_DeriveKeyFromPassword(hCProv, Password);
      try
        // allocate buffer space
        lul_datalen := Length(Value) * SizeOf(Char);
        Buffer := TEncoding.Unicode.GetBytes(Value + '        ');
        lul_buflen := Length(Buffer);
        // encrypt to buffer
        Win32Check(CryptEncrypt(hKey, 0, True, 0, @Buffer[0], lul_datalen, lul_buflen));
        SetLength(Buffer, lul_datalen);
        // base 64 result
        Result := Base64_Encode(Buffer);
      finally
        CryptDestroyKey(hKey);
      end;
    finally
      CryptReleaseContext(hCProv, 0);
    end;
  end;
end;

function AES128_Decrypt(Value, Password: string): string;
var
  hCProv: HCRYPTPROV;
  hKey: HCRYPTKEY;
  lul_datalen: Integer;
  Buffer: TBytes;
begin
  Assert(Password <> '');
  if Value = '' then
    Result := ''
  else begin
    hCProv := __CryptAcquireContext(PROV_RSA_AES);
    try
      hKey := __AES128_DeriveKeyFromPassword(hCProv, Password);
      try
        // decode base64
        Buffer := Base64_Decode(Value);
        // allocate buffer space
        lul_datalen := Length(Buffer);
        // decrypt buffer to to string
        Win32Check(CryptDecrypt(hKey, 0, True, 0, @Buffer[0], lul_datalen));
        Result := TEncoding.Unicode.GetString(Buffer, 0, lul_datalen);
      finally
        CryptDestroyKey(hKey);
      end;
    finally
      CryptReleaseContext(hCProv, 0);
    end;
  end;
end;

end.
于 2017-07-15T14:21:49.293 に答える
5
  • LockBox 3 は使用しないでください。これは高品質のライブラリではありません。
  • 暗号化されたデータを「テキスト」文字列に戻さないでください。暗号化されたデータは、(テキスト データとしての) 文字列ではなく、任意のバイト シーケンスです。Delphi は「長さ制御」文字列を使用し、ほとんどすべてのものを保存できますが、他の言語によって間違った方法で解釈される可能性のあるバイト シーケンスを含む文字列を渡す際に問題が発生する可能性があります。たとえば、C/C++ アプリケーションでは $00 です。 .)。ライブラリ自体が文字列を使用している場合、まあ、それは低品質のライブラリであるという症状です
  • 暗号化されたデータを変換しないでください。暗号化された ANSIString を Unicode に変換すると (それが最後のキャストの理由だと思います)、暗号化された値を破棄しています。その文字列を渡すと、「損失」がない限り、逆変換が適用されない限り復号化できません。
于 2012-02-08T09:12:38.573 に答える
-1

私はちょうど同じ問題を抱えていました。これは古いトピックであることは知っていますが、非常に役立ちました。記録のためにここに残しておきます。

Function LockBoxDecrypt(Password As String, Data() As Byte) As String

    Dim AesProvider = AesCryptoServiceProvider.Create()
    Dim IV(15) As Byte, PaddedData(15) As Byte

    Array.Copy(Data, 0, IV, 0, 8)
    Array.Copy(Data, 8, PaddedData, 0, Data.Length - 8)

    AesProvider.Key = SHA1.Create().ComputeHash(Encoding.Default.GetBytes(Password)).Take(16).ToArray()
    AesProvider.IV = IV
    AesProvider.Mode = CipherMode.CFB
    AesProvider.Padding = PaddingMode.None

    Return Encoding.Default.GetString(AesProvider.CreateDecryptor().TransformFinalBlock(PaddedData, 0, PaddedData.Length), 0, Data.Length - 8)

End Function

ショーンの回答に基づいて、複数のブロックがある場合はモードを CTS に変更する必要があると思います。1ブロックで十分なので、試しませんでしたが、コードを簡単に適応できるはずです。

于 2016-10-25T00:21:03.133 に答える