如何在Delphi中使用密码加密字符串并在C#中解密?

如何在Delphi中使用密码加密字符串并在C#中解密?,c#,delphi,encryption,turbopower,lockbox-3,C#,Delphi,Encryption,Turbopower,Lockbox 3,我想用密码在Delphi中加密一个字符串。我想把这个上传到我的服务器上,并且能够在C#中解密相同的密码 在Delphi中,我使用的是TurboPower锁箱3: function EncryptText_AES_128(input: string; password: string): string; var Codec: TCodec; CipherText: AnsiString; begin Codec := TCodec.Create(nil); try Code

我想用密码在Delphi中加密一个字符串。我想把这个上传到我的服务器上,并且能够在C#中解密相同的密码

在Delphi中,我使用的是TurboPower锁箱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代码。还没有生产任何产品。我甚至都不想用保险箱。但是,我希望避免将其放入用于P/Invoke的DLL中

(我的示例显示,我的加密序列本身就是一个字符串。这不是我的要求。字节流就可以了。)

  • 不要使用锁箱3。这不是一个高质量的图书馆
  • 不要将加密数据返回到“文本”字符串中。加密数据是字节的任意序列,而不是字符串(作为文本数据)。Delphi使用“长度控制”字符串,几乎可以存储它所包含的任何内容,但您可能会遇到传递包含字节序列的字符串的问题,这些字节序列可能会被其他语言错误地解释,例如,C/C++应用程序使用$00…)。如果库本身使用字符串,那么,这是一个症状,它是一个低质量的库
  • 不要转换加密数据当您将加密的ANSIString转换为Unicode格式时(我想这就是您上次转换的原因),您正在销毁加密的值。如果将该字符串传递,则除非应用反向转换,否则它将无法解密,只要它不是“有损的”

与您可能阅读的任何巨魔火焰诱饵相反,锁盒3实际上是一个高质量的加密库。LB3的标准符合性是无可挑剔的。在与其他语言和库的互操作性方面可能存在问题的地方是与标准之外的选项有关的。如果在Delphi端使用Lockbox,那么您只需要确保在另一种语言端以相同的方式处理这些选项。如果这是不可能的,那么您应该选择另一个库。我将在下面讨论这些选项中的每一个

替代解决方案(OpenSSL、CryptoAPI和Eldos)没有问题。其中一些可能是黑匣子。这可能是一些人的问题

  • 将密码转换为密钥。 AES-128使用16字节的密钥。此外,从“密钥数据”或“密码数据”生成密钥的标准机制本机基于16字节的输入种子。对于互操作性来说,从Delphi端的字符串密码生成二进制密钥更安全,只需将二进制密钥传输到另一端,而不是传输字符串密码。这是因为将字符串密码转换为二进制16字节密钥的算法超出了AES标准。无论如何,你可以用任何一种方法。当为lockbox提供字符串密码以初始化AES-128编解码器时,它将字符串有效负载视为字节数组。如果有效负载正好是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代码点)。如果另一种语言的实现是半正常的,那么这应该是相当安全的

  • UTF-16与ansi字符串/UTF-8密码 这不是一种选择,而是年轻球员的陷阱。我们程序员倾向于将“字符串”视为“字符串”。但事实并非如此。在Delphi 2010中,字符串的有效负载存储在UTF-16LE编码中,代码单元大小为2字节。但在其他语言(如PHP和python)中,在默认模式下,字符串是单字节代码单元编码,可以是UTF-8,也可以是基于MS windows代码页面库(MS称之为“ansistring”)的代码。记住“mypassword”的UTF-16编码与UTF-8“mypassword”不同是值得的

  • IV设置。 AES标准不涉及如何设置编解码器的初始化向量(IV)的问题。IV的大小与基础区块的大小相同。对于AES,这是128位或16字节。加密时,lockbox会创建一个16字节的nonce。该nonce成为IV的值,并在密文消息的头部以明文形式发出。阅读另一方IV初始化方法/策略的文档。你的选择是:

    3.1如果另一方在密文前加上IV,那么你是可爱的

    3.2否则,另一方面,在解密时,自己读取密文的前16个字节,并将剩余字节传递给外部编解码器。在解密之前,告诉你什么是IV(假设它的API能够做到这一点)

  • 块量化 AES块大小为16字节。当明文信息不是16字节的整数倍时,必须采取措施使其成为整数倍。此过程称为块量化,标准中未涉及此过程,但留给实施。许多实现将使用块填充。没有标准的块填充方案,有很多可供选择。锁盒不为CBC使用块填充(其他模式可能是不同的情况)。如果明文是整数块,则不需要或不进行量化,否则使用标准密文窃取。如果明文大小非常小(在1到15字节之间),则密文窃取是不可能的
    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.
    
    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);
            }
        }
    }
    
    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
    
    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.