Попытка перенести старый алгоритм шифрования на C #

1

Начну с того, что мне очень помогла следующая ссылка:

Перевод API-интерфейса Win32 Crypto на С# с помощью System.Security.Cryptography

У меня такая же проблема, как у него, кроме его исправления не удалось исправить мой код.

Здесь мой старый (vb6) код:

Private Const ALG_CLASS_DATA_ENCRYPT = 24576&
Private Const ALG_CLASS_HASH = 32768
Private Const ALG_SID_3DES = 3&
Private Const ALG_SID_SHA1 = 4&
Private Const ALG_TYPE_ANY = 0&
Private Const ALG_TYPE_BLOCK = 1536&
Private Const CALG_3DES = (ALG_CLASS_DATA_ENCRYPT Or ALG_TYPE_BLOCK Or ALG_SID_3DES)
Private Const CALG_SHA1 = (ALG_CLASS_HASH Or ALG_TYPE_ANY Or ALG_SID_SHA1)
Private Const CRYPT_SILENT = &H40&
Private Const CRYPT_VERIFYCONTEXT = &HF0000000
Private Const MS_ENHANCED_PROV = "Microsoft Enhanced Cryptographic Provider v1.0"
Private Const PROV_RSA_FULL = 1&

Private Declare Function CryptAcquireContextApi Lib "advapi32.dll" Alias "CryptAcquireContextA" (ByRef phProv As Long, ByVal pszContainer As String, ByVal pszProvider As String, ByVal dwProvType As Long, ByVal dwFlags As Long) As Long
Private Declare Function CryptCreateHashApi Lib "advapi32.dll" Alias "CryptCreateHash" (ByVal hProv As Long, ByVal Algid As Long, ByVal hKey As Long, ByVal dwFlags As Long, ByRef phHash As Long) As Long
Private Declare Function CryptDecryptApi Lib "advapi32.dll" Alias "CryptDecrypt" (ByVal hKey As Long, ByVal hHash As Long, ByVal Final As Long, ByVal dwFlags As Long, ByRef pbData As Byte, ByRef pdwDataLen As Long) As Long
Private Declare Function CryptDeriveKeyApi Lib "advapi32.dll" Alias "CryptDeriveKey" (ByVal hProv As Long, ByVal Algid As Long, ByVal hBaseData As Long, ByVal dwFlags As Long, ByRef phKey As Long) As Long
Private Declare Function CryptDestroyHashApi Lib "advapi32.dll" Alias "CryptDestroyHash" (ByVal hHash As Long) As Long
Private Declare Function CryptDestroyKeyApi Lib "advapi32.dll" Alias "CryptDestroyKey" (ByVal hKey As Long) As Long
Private Declare Function CryptEncryptApi Lib "advapi32.dll" Alias "CryptEncrypt" (ByVal hKey As Long, ByVal hHash As Long, ByVal Final As Long, ByVal dwFlags As Long, ByRef pbData As Byte, ByRef pdwDataLen As Long, ByVal dwBufLen As Long) As Long
Private Declare Function CryptGetHashParamApi Lib "advapi32.dll" Alias "CryptGetHashParam" (ByVal hHash As Long, ByVal dwParam As Long, pbData As Any, pdwDataLen As Long, ByVal dwFlags As Long) As Long
Private Declare Function CryptHashDataApi Lib "advapi32.dll" Alias "CryptHashData" (ByVal hHash As Long, ByRef pbData As Byte, ByVal dwDataLen As Long, ByVal dwFlags As Long) As Long
Private Declare Function CryptReleaseContextApi Lib "advapi32.dll" Alias "CryptReleaseContext" (ByVal hProv As Long, ByVal dwFlags As Long) As Long
Private Declare Function CryptGetKeyParamApi Lib "advapi32.dll" Alias "CryptGetKeyParam" (ByVal hKey As Long, ByVal dwParam As Long, ByRef pbData As Byte, ByRef pdwDataLen As Long, dwFlags As Long) As Long

Private Declare Sub CopyMemoryApi Lib "kernel32" Alias "RtlMoveMemory" (pDst As Any, pSrc As Any, ByVal ByteLen As Long)

Function Encrypt(message As String, Password As String) As String

   If CryptAcquireContextApi(provider, vbNullString, ProviderName, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) = 0 Then
      Goto Encrypt_Failure
   End-If
   If CryptCreateHashApi(provider, CALG_SHA1, 0&, 0&, hash) = 0 Then
      Goto Encrypt_Failure
   End-If

   buffer = StrConv(Password, vbFromUnicode)
   If CryptHashDataApi(hash, buffer(0), CLng(UBound(buffer) + 1), 0&) = 0 Then
      GoTo Encrypt_Failure
   End If

   If CryptDeriveKeyApi(provider, CALG_3DES, hash, 1&, key) = 0 Then
      key = 0
      GoTo Encrypt_Failure
   End If

   length = Len(message)
   bfrlen = length
   If CryptEncryptApi(key, 0&, 1&, 0&, ByVal 0&, bfrlen, bfrlen) = 0 Then
      GoTo Encrypt_Failure
   End If

   ReDim buffer(bfrlen - 1)

   For i = 0 To length - 1
      buffer(i) = Asc(Mid(message, i + 1, 1))
   Next i

   If CryptEncryptApi(key, 0&, 1&, 0&, buffer(0), length, bfrlen) = 0 Then
      GoTo Encrypt_Failure
   End If

   Call CryptDestroyKeyApi(key)
   Call CryptReleaseContextApi(provider, 0)

   Encrypt = left(StrConv(buffer, vbUnicode), length)

Encrypt_Failure:
   If key Then
      Call CryptDestroyKeyApi(key)
   End If
   If hash Then
      Call CryptDestroyHashApi(hash)
   End If
   If provider Then
      Call CryptReleaseContextApi(provider, 0)
   End If

Exit Function

Здесь новая версия С#:

   public byte[] Encrypt(string source, string pass)
    {
        byte[] password = Encoding.UTF8.GetBytes(pass);
        byte[] resultArray = null;
        byte[] streamToEncrypt = Encoding.UTF8.GetBytes(source);

        if(streamToEncrypt.Length % 8 != 0)
        {
            byte[] inputArray = new byte[streamToEncrypt.Length + (8 - (streamToEncrypt.Length % 8))]; //add padding to the end to make the message groups of 8 bytes
            int i = 0;
            foreach (byte element in streamToEncrypt)
            {
                inputArray[i] = element;
                i++;
            }
            streamToEncrypt = inputArray;
        }


        using (TripleDESCryptoServiceProvider prov3des = new TripleDESCryptoServiceProvider())
        {

           prov3des.Mode = CipherMode.CBC;
           prov3des.Padding = PaddingMode.PKCS7;
           prov3des.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; //8 bytes, zero-ed

            using (PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, null)) //No salt needed here
            {
                prov3des.Key = pdb.CryptDeriveKey("TripleDES", "SHA1", 168, new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 });
            }

            ICryptoTransform cTransform = prov3des.CreateEncryptor();
            resultArray = cTransform.TransformFinalBlock(streamToEncrypt,0,streamToEncrypt.Length);

        }

        return resultArray;

    }

И, как и плакат, связанный выше, хэш пароля один и тот же, и первые 8 байтов зашифрованного байтового массива одинаковы. У меня есть вектор инициализации, равный 8 байтам нуля, но я считаю, что если первые 8 байтов зашифрованных данных одинаковы, тогда вектор инициализации должен совпадать. Я просто не могу понять, почему следующие 8 байтов отличаются. Любые идеи, что я делаю неправильно здесь?

Благодарю!

Теги:
encryption-symmetric
vb6-migration
3des

2 ответа

1
Лучший ответ

На самом деле это очень простое решение, хотя это совершенно не очевидно. У вас есть блок кода, который гласит: "Если эти данные не являются краткими числами 8, нанесите их". Однако это изменяет значение, которое вы шифруете, поэтому вы получаете другой результат.

Просто удалите весь if(streamToEncrypt.Length.Dump("Length") % 8 != 0) {} и вы получите ожидаемый результат.

  • 0
    Мы будем дядей обезьяны. Когда я делал начальные сравнения, я заметил, что байтовый массив, передаваемый в старый код VB6, CryptEncrypt() первым CryptEncrypt() для получения длины, а затем ReDim buffer(bfrlen - 1) за которым следует цикл for так что я просто подражал. Работает без нареканий! Спасибо!
  • 0
    @zeusalmighty - я предполагаю, что библиотека C # достаточно умна, чтобы при необходимости выполнять автозаполнение. Иногда все, что нужно, это другая пара глаз. Рад, что смог помочь!
Показать ещё 4 комментария
1

Если первые 8 байтов совпадают, а остальные - нет, похоже, что вы используете другую форму обратной связи, чем исходный алгоритм. Попробуйте использовать разные значения CipherMode и убедитесь, что один из них дает вам совпадение.

  • 0
    Это была моя первая мысль тоже. Я перепробовал все перечисленные значения безрезультатно.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню