2012-02-08 15 views
4

Я бы хотел, чтобы AES-128 зашифровал строку в Delphi с паролем. Я хотел бы загрузить это на свой сервер и иметь возможность расшифровать, учитывая тот же пароль в C#.Как AES-128 шифровать строку с использованием пароля в Delphi и расшифровывать в 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. Но я хотел бы избежать помещать это в DLL для P/Invoke.

(Мой пример показывает, что моя шифруется последовательность сама строка. Это не является обязательным требованием для меня. Поток байтов в порядке.)

+0

Непонятно, в чем ваш вопрос. – Bill

+0

Спасибо, Билл. Я отредактировал вопрос, чтобы сделать его более понятным. – Troy

+0

Delphi и LockBox, похоже, имеют мало общего с вашим вопросом C#. Сосредоточьтесь на AES и всех связанных деталях на языке криптологии. – menjaraz

ответ

15

Наконец-то я нашел совместимое решение между Delphi и C# для AES-128. Это также работает на Вино.Вот мой 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); 
     } 
    } 
} 
+1

У меня нет времени, чтобы просмотреть весь код, но я вижу, что вы используете статический набор IV для всех нулей. Должно быть довольно легко использовать случайный IV и добавить его к шифруемому тексту перед его кодировкой в ​​базе 64. Также обратите внимание, что IV не является байтами KEY_SIZE, а BlockSize в байтах (в этом примере они, к счастью, равны). Большую часть времени вы можете задать экземпляр алгоритма для размера блока. Наконец, вы можете использовать UTF8 вместо UNICODE, тем более, что после этого невозможно сжать зашифрованный текст (у вас, вероятно, будет в 2 раза меньше зашифрованного текста). –

5
  • Не используйте LockBox 3. Это не хорошо качественная библиотека.
  • Не возвращайте зашифрованные данные в строки «текст». Зашифрованные данные представляют собой произвольные последовательности байтов, а не строки (в виде текстовых данных). Delphi использует строки с контролируемой длиной и может хранить почти все, что угодно, но может, но вы можете столкнуться с проблемами, проходящими вокруг строк, которые содержат последовательности байтов, которые могут быть интерпретированы неверным способом другими языками, то есть $ 00 приложением C/C++. .). Если сама библиотека использует строки, ну, это признак, это библиотека низкого качества.
  • Не преобразовывать зашифрованные данные! При преобразовании зашифрованного ANSIString в Unicode (я думаю, это причина вашего последнего приведения), вы уничтожаете зашифрованное значение. Если вы передадите эту строку вокруг, она не будет расшифровываться, если не применяется обратное преобразование, если оно не является «потерянным».
+0

+1 Не знаю о качестве LockBox, но использование строк для последовательностей байтов было довольно обычным делом в формате Unicode Delphi. Я думаю, из-за легкой индексации и «свободного» управления памятью. Это «только» стало реальной проблемой с Unicode Delphi из-за неявных и явных преобразований строк. –

+0

Как я уже сказал, я не зацикливаюсь на использовании Lockbox. Но что бы вы посоветовали мне использовать вместо этого? Я редактировал свой вопрос, чтобы показать, что я не застрял на использовании строки для хранения зашифрованных данных. – Troy

+0

Преобразование в моем примере из AnsiString в Unicode находится в кодировке base64. Таким образом, нет никаких шансов на потерю данных. Но опять же, мой пример не мой пример. Мой пример показывает только возможную отправную точку. – Troy

10

В отличие от любой пламенной приманки, которую вы можете прочитать, LockBox 3 на самом деле является криптографической библиотекой хорошего качества. Соответствие стандартам LB3 является безупречным. Там, где у вас могут возникнуть проблемы с совместимостью с другими языками, библиотеки & относятся к опциям, которые находятся за пределами стандарта. Если вы используете Lockbox на стороне Delphi, вам просто нужно убедиться, что эти параметры обрабатываются одинаково на стороне другого языка. Если это невозможно, выберите другую библиотеку. Я рассмотрю каждый из этих вариантов ниже.

Нет ничего плохого в альтернативных решениях (OpenSSL, CryptoAPI и Eldos). Некоторые из них могут быть черными. Это может быть проблемой для некоторых людей.

  1. Преобразование пароля в ключ. 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). Это должно быть довольно безопасно, если реализация другого языка наполовину достойна.

  2. UTF-16 против паролей ansi-string/UTF-8 Это не такой вариант, как ловушка для молодых игроков. Мы, программисты, склонны думать о «струнах» как о «струнах». Но это не так. В Delphi 2010 полезная нагрузка строк хранится в кодировке UTF-16LE с размером кода 2 байта. Но в других языках, таких как 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. Этот 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 ничего не делает для сообщений, которые уже находятся в целых блоках. Очень вероятно, что внешний кодек имеет ту же политику - но вам опять же нужно проверить документацию для внешнего кодека.

+1

Отличный ответ. Я также столкнулся с ситуацией, когда мне нужно зашифровать то, что может расшифровать другой инструмент. Мы можем выбрать инструмент дешифрования, но он должен быть чем-то стандартным и легкодоступным, поэтому мы будем работать с OpenSSL. Я в процессе изучения, может ли LB3 шифровать что-то в AES, которое OpenSSL может расшифровать. Этот ответ дает мне много знаний, которых у меня раньше не было. –

+1

Огромное вам спасибо, Шон, за действительно конструктивный ответ. Это подтверждает мое подозрение, что реальная проблема заключается в том, чтобы преобразовать пароль в 128-битный ключ последовательно между платформами. Ваша информация помогает мне в моем путешествии по этому поводу. Там так много для этого шифрования, трудно держать прямо. Хотелось бы, чтобы это было простое решение! Спасибо, что помогли мне встать на правильный путь. Теперь, если кто-то может просто указать мне пример кода, который это демонстрирует! ;) – Troy

+0

LOL! Вы являетесь автором этой библиотеки, поэтому не удивительно, что вы думаете, что это «хорошее качество». Пользователи могут думать совсем по-другому. Более того, вы украли имя и даже попытались сменить лицензию, когда у вас не было права это делать - я не стал бы доверять никакому «коду безопасности» от кого-то, кто вообще не заботится о чьих-либо правах. –

0

Я просто была такая же проблема. Я знаю, что это старая тема, но мне это очень помогло. Я просто оставляю его здесь для записи.

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 при наличии более одного блока. Я не пробовал, потому что для меня достаточно одного блока, но его нужно легко адаптировать.

0

Я смог успешно реализовать код Delphi Трои в 10.2 Токио с несколькими модификациями.

Я удалил TNTLxUtils из Uses, поскольку он не был нужен (и у меня его не было) и добавил IdGlobal. Причина использования IdGlobal заключается в том, что вам нужно преобразовать типы TBytes в TIdBytes в функцию Base64_Encode и TIBytes обратно в TBytes в Base64_Decode.

Примечание. Данное устройство будет работать только в 32-разрядных приложениях, поскольку оно ссылается на 32-битный Windows API.

Спасибо, Трой за то, что указал мне в правильном направлении на бесплатный метод шифрования, который не требует покупки инструментария для реализации.

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.