у нас есть проект, написанный в Delphi, который мы хотим преобразовать в С#. Проблема в том, что у нас есть пароли и настройки, которые зашифрованы и записаны в реестр. Когда нам нужен указанный пароль, мы получаем его из реестра и расшифровываем его, чтобы мы могли его использовать. Для преобразования в С# мы должны сделать это таким же образом, чтобы приложение также могло использоваться пользователями, имеющими старую версию, и их необходимо обновить.
Вот код, который мы используем для шифрования/дешифрования строк в Delphi:
unit uCrypt;
interface
function EncryptString(strPlaintext, strPassword : String) : String;
function DecryptString(strEncryptedText, strPassword : String) : String;
implementation
uses
DCPcrypt2, DCPblockciphers, DCPdes, DCPmd5;
const
CRYPT_KEY = '1q2w3e4r5t6z7u8';
function EncryptString(strPlaintext) : String;
var
cipher : TDCP_3des;
strEncryptedText : String;
begin
if strPlaintext <> '' then
begin
try
cipher := TDCP_3des.Create(nil);
try
cipher.InitStr(CRYPT_KEY, TDCP_md5);
strEncryptedText := cipher.EncryptString(strPlaintext);
finally
cipher.Free;
end;
except
strEncryptedText := '';
end;
end;
Result := strEncryptedText;
end;
function DecryptString(strEncryptedText) : String;
var
cipher : TDCP_3des;
strDecryptedText : String;
begin
if strEncryptedText <> '' then
begin
try
cipher := TDCP_3des.Create(nil);
try
cipher.InitStr(CRYPT_KEY, TDCP_md5);
strDecryptedText := cipher.DecryptString(strEncryptedText);
finally
cipher.Free;
end;
except
strDecryptedText := '';
end;
end;
Result := strDecryptedText;
end;
end.
Итак, например, когда мы хотим зашифровать строку asdf1234
, получаем результат WcOb/iKo4g8=
.
Теперь мы хотим расшифровать эту строку в С#. Вот что мы пытались сделать:
public static void Main(string[] args)
{
string Encrypted = "WcOb/iKo4g8=";
string Password = "1q2w3e4r5t6z7u8";
string DecryptedString = DecryptString(Encrypted, Password);
}
public static string DecryptString(string Message, string Passphrase)
{
byte[] Results;
System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding();
// Step 1. We hash the passphrase using MD5
// We use the MD5 hash generator as the result is a 128 bit byte array
// which is a valid length for the TripleDES encoder we use below
MD5CryptoServiceProvider HashProvider = new MD5CryptoServiceProvider();
byte[] TDESKey = HashProvider.ComputeHash(UTF8.GetBytes(Passphrase));
// Step 2. Create a new TripleDESCryptoServiceProvider object
TripleDESCryptoServiceProvider TDESAlgorithm = new TripleDESCryptoServiceProvider();
// Step 3. Setup the decoder
TDESAlgorithm.Key = TDESKey;
TDESAlgorithm.Mode = CipherMode.ECB;
TDESAlgorithm.Padding = PaddingMode.None;
// Step 4. Convert the input string to a byte[]
byte[] DataToDecrypt = Convert.FromBase64String(Message);
// Step 5. Attempt to decrypt the string
try
{
ICryptoTransform Decryptor = TDESAlgorithm.CreateDecryptor();
Results = Decryptor.TransformFinalBlock(DataToDecrypt, 0, DataToDecrypt.Length);
}
finally
{
// Clear the TripleDes and Hashprovider services of any sensitive information
TDESAlgorithm.Clear();
HashProvider.Clear();
}
// Step 6. Return the decrypted string in UTF8 format
return UTF8.GetString(Results);
}
Ну, результат отличается от ожидаемого результата. После того, как мы назовем DecryptString()
, мы ожидаем получить asdf1234
, но получим что-то еще.
Кто-нибудь имеет представление о том, как правильно его расшифровать?
Спасибо заранее
Simon
EDIT:
Хорошо, спасибо всем вам за ваши предложения. Мы не смогли узнать, как сделать все это на С#, поэтому мы решили взять нашу версию обратно, используя DLL Delphi с P/Invoke, как было предложено.
Я бы порекомендовал другой подход. Вы должны P/вызывать исходный код, чтобы прочитать пароли назад в первый раз, а затем повторно сохранить с помощью .net-кода. Таким образом, вы можете избежать проблем с различными процедурами шифрования на двух платформах.
Что-то отличается от ваших реализаций Delphi и С# - где-то.
Я предлагаю вам запустить дешифрование с тестовыми данными на обоих языках и вывести свои промежуточные продукты на каждом этапе процесса: см., где они расходятся. В частности, вам нужно проверить полученный ключ 3DES и массив байтов, переданный в качестве ввода в шифр.
Imports System.IO
Imports System.Text
Imports System.Security.Cryptography
Public Class Crypto
Private Shared DES As New TripleDESCryptoServiceProvider
Private Shared MD5 As New MD5CryptoServiceProvider
Private Shared Function MD5Hash(ByVal value As String) As Byte()
Return MD5.ComputeHash(ASCIIEncoding.ASCII.GetBytes(value))
End Function
Public Shared Function Encrypt(ByVal stringToEncrypt As String, ByVal key As String) As String
DES.Key = Crypto.MD5Hash(key)
DES.Mode = CipherMode.ECB
Dim Buffer As Byte() = ASCIIEncoding.ASCII.GetBytes(stringToEncrypt)
Return Convert.ToBase64String(DES.CreateEncryptor().TransformFinalBlock(Buffer, 0, Buffer.Length))
End Function
Public Shared Function Decrypt(ByVal encryptedString As String, ByVal key As String) As String
Try
DES.Key = Crypto.MD5Hash(key)
DES.Mode = CipherMode.ECB
Dim Buffer As Byte() = Convert.FromBase64String(encryptedString)
Return ASCIIEncoding.ASCII.GetString(DES.CreateDecryptor().TransformFinalBlock(Buffer, 0, Buffer.Length))
Catch ex As Exception
MessageBox.Show("Invalid Key", "Decryption Failed", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
End Try
End Function
End Class
DES - это блок-шифр. И для выполнения операций шифрования и дешифрования требуется отступы. Если исходные данные для шифрования не кратны 64 битам, для шифрования требуется отступы. Если вы не вставляете данные, вы получите неожиданные результаты в зависимости от того, какая схема заполнения используется по умолчанию здесь и там.
Итак, если вы можете, вы должны повторно зашифровать все пароли в Delphi, если они будут дополнены до шифрования. Популярной схемой заполнения является добавление 0x80 к данным и добавление как можно большего количества 0x00, чтобы размер данных был кратным 8 байтам. После дешифрования вы сможете отключить отладку.
Просматривая исходный код DCP, похоже, что InitStr() инициализирует шифр 3DES в режиме CBC с помощью IV = EncryptECB (0000000000000000) или IV = EncryptECB (FFFFFFFFFFFFFFFF) (в зависимости от IFDEF).
Используя те, я все еще не могу воспроизвести значения из Delphi-кода.
Я предлагаю вам отлаживать код с каждой стороны и точно знать, как строки преобразуются в байты и какие значения присваивает код производному ключу и IV.