2012-04-21 2 views
2

У меня есть код для шифрования на C#, который я должен переписать на C++ Я видел несколько подобных вопросов здесь, на SO, но почему-то я все еще не мог понять это. Кодирование одной и той же строки с тем же паролем дает разные результаты..NET triple des to Windows API

C# код

byte[] TestEncrypt(string data) 
    { 
     byte[] plainText = System.Text.Encoding.ASCII.GetBytes(data); 
     TripleDES des3 = new  System.Security.Cryptography.TripleDESCryptoServiceProvider(); 
     des3.Mode = CipherMode.CBC; 
     des3.Key = System.Text.Encoding.ASCII.GetBytes("12656b2e4ba2f22e"); 
     des3.IV = System.Text.Encoding.ASCII.GetBytes("d566gdbc"); 
     ICryptoTransform transform = des3.CreateEncryptor(); 
     MemoryStream memStreamEncryptedData = new MemoryStream(); 
     CryptoStream encStream = new CryptoStream(memStreamEncryptedData, 
      transform, CryptoStreamMode.Write); 
     encStream.Write(plainText, 0, plainText.Length); 
     encStream.FlushFinalBlock(); 
     encStream.Close(); 
     byte[] cipherText = memStreamEncryptedData.ToArray(); 
     return cipherText; 
    } 

Результат 255,142,22,151,93,255,156,10,174,10,250,92,144,0,60,142 Редакцией: версия Добавлен новый C++

string Test3DES() 
    { 
     string key = "12656b2e4ba2f22e"; 
     HCRYPTPROV hCryptProv = NULL; 
     HCRYPTHASH hHash = NULL; 
     HCRYPTKEY hCryptKey = NULL; 
     char pIV[] = "d566gdbc"; //simple test IV for 3DES 
     CryptAcquireContext(&hCryptProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL,CRYPT_VERIFYCONTEXT); 
     PlainTextKeyBlob keyBlob ={0}; 
     keyBlob.hdr.bType = PLAINTEXTKEYBLOB; 
     keyBlob.hdr.bVersion = CUR_BLOB_VERSION; 
     keyBlob.hdr.reserved = 0; 
     keyBlob.hdr.aiKeyAlg = CALG_3DES_112; 
     keyBlob.cbKeySize = key.size(); 
     memcpy(keyBlob.key, key.c_str(), key.size()); 
     DWORD dwSizeBlob = sizeof(BLOBHEADER)+sizeof(DWORD)+key.size(); 
     ret = CryptImportKey(hCryptProv, (const BYTE*)&keyBlob, dwSizeBlob, 0, CRYPT_EXPORTABLE, &hCryptKey); 
     DWORD dwMode = CRYPT_MODE_CBC; 
     CryptSetKeyParam(hCryptKey, KP_MODE, (BYTE*)&dwMode, 0); 
     CryptSetKeyParam(hCryptKey, KP_IV,(const BYTE*) pIV, 0) ; 
     DWORD dwFilled = 0; 
     BOOL ret = CryptEncrypt(hCryptKey, NULL, TRUE, 0, (LPBYTE)cipherText.c_str(), &dwFilled, (DWORD)str.size()); 
     cipherText.resize(dwFilled); 
     if(hCryptKey) CryptDestroyKey(hCryptKey); 
     if(hHash) CryptDestroyHash(hHash); 
     if(hCryptProv) CryptReleaseContext(hCryptProv, 0); 
     return cipherText; 
    } 

результат 167,177,201,56,123,240,169,174

Старая версия на С ++

C++

string Test3DES() 
    { 
     string key = "12656b2e4ba2f22e"; 
     HCRYPTPROV hCryptProv = NULL; 
     HCRYPTHASH hHash = NULL; 
     HCRYPTKEY hCryptKey = NULL; 
     char pIV[] = "d566gdbc"; //simple test IV for 3DES 
     CryptAcquireContext(&hCryptProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); 
     CryptCreateHash(hCryptProv, CALG_MD5, NULL, 0, &hHash); 
     CryptHashData(hHash, (LPBYTE)key.c_str(), (DWORD)key.size(), 0); 
     DWORD dwMode = CRYPT_MODE_CBC; 
     CryptDeriveKey(hCryptProv, CALG_3DES, hHash, 0, &hCryptKey); 
     CryptSetKeyParam(hCryptKey, KP_MODE, (BYTE*)&dwMode, 0); 
     CryptSetKeyParam(hCryptKey, KP_IV,(const BYTE*) pIV, 0) ; 
     DWORD dwFilled = 0; 
     BOOL ret = CryptEncrypt(hCryptKey, NULL, TRUE, 0, (LPBYTE)cipherText.c_str(), &dwFilled, (DWORD)str.size()); 
     cipherText.resize(dwFilled); 
     if(hCryptKey) CryptDestroyKey(hCryptKey); 
     if(hHash) CryptDestroyHash(hHash); 
     if(hCryptProv) CryptReleaseContext(hCryptProv, 0); 
     return cipherText; 
    } 
+2

В чем ваш вопрос/проблема? – Mat

+3

Ваш ключ слишком короткий (120 бит), минимум 128 бит. [MSDN] (http://msdn.microsoft.com/en-us/library/system.security.cryptography.tripledes.key.aspx): этот алгоритм поддерживает длину ключа от 128 бит до 192 бит с шагом 64 бит –

+0

Он отлично работает в C# – SparcU

ответ

8

Я создал несколько примеров проектов, начиная с вашего кода. Вы не включили все, поэтому мне пришлось добавить кое-что. К тому времени, когда я скомпилировал и протестировал, я получил тот же ответ как на C++, так и на C#. Я подозреваю, что проблема может быть что-то в том, как вы указываете буфер cipherText?Это все из моего тестового кода, так что должно быть легко для вас, чтобы настроить некоторые образцы проекта и посмотреть, если вы получите тот же результат тоже, то, возможно, вы можете понять это оттуда:

C#

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Security.Cryptography; 
using System.IO; 

namespace _3dtest 
{ 
    class Program 
    { 
     static byte[] TestEncrypt(string data) 
     { 
      byte[] plainText = System.Text.Encoding.ASCII.GetBytes(data); 
      TripleDES des3 = new System.Security.Cryptography.TripleDESCryptoServiceProvider(); 
      des3.Mode = CipherMode.CBC; 
      des3.Key = System.Text.Encoding.ASCII.GetBytes("12656b2e4ba2f22e"); 
      des3.IV = System.Text.Encoding.ASCII.GetBytes("d566gdbc"); 
      ICryptoTransform transform = des3.CreateEncryptor(); 
      MemoryStream memStreamEncryptedData = new MemoryStream(); 
      CryptoStream encStream = new CryptoStream(memStreamEncryptedData, 
       transform, CryptoStreamMode.Write); 
      encStream.Write(plainText, 0, plainText.Length); 
      encStream.FlushFinalBlock(); 
      encStream.Close(); 
      byte[] cipherText = memStreamEncryptedData.ToArray(); 
      return cipherText; 
     } 

     static void Main(string[] args) 
     { 
      var info = TestEncrypt("password"); 
      foreach (byte b in info) 
      { 
       Console.Write(b.ToString()); 
       Console.Write(", "); 
      } 
      Console.WriteLine(); 
     } 
    } 
} 

C++

#include "stdafx.h" 
#include <Windows.h> 
#include <WinCrypt.h> 
#include <cassert> 
#include <vector> 
#include <string> 
#include <algorithm> 
#include <iostream> 

using namespace std; 

struct PlainTextKeyBlob { 
     BLOBHEADER hdr; 
     DWORD cbKeySize; 
     BYTE key[16]; 
}; 

std::wstring LastError(DWORD lasterr) 
{ 
    LPVOID lpMsgBuf; 
    DWORD dw = GetLastError(); 

    FormatMessage(
     FORMAT_MESSAGE_ALLOCATE_BUFFER | 
     FORMAT_MESSAGE_FROM_SYSTEM | 
     FORMAT_MESSAGE_IGNORE_INSERTS, 
     NULL, 
     dw, 
     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
     (LPTSTR) &lpMsgBuf, 
     0, NULL); 
    return (wchar_t*)lpMsgBuf; // Leaking, don't care 
} 

std::vector<BYTE> Test3DES(const std::string& passwd) 
{ 
     string key = "12656b2e4ba2f22e"; 
     unsigned char pIV[] = "d566gdbc"; //simple test IV for 3DES 
     HCRYPTPROV hCryptProv = NULL; 
     HCRYPTHASH hHash = NULL; 
     HCRYPTKEY hCryptKey = NULL; 
     DWORD ret = CryptAcquireContext(&hCryptProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL,CRYPT_VERIFYCONTEXT); 
     if(ret == 0) std::wcout << LastError(GetLastError()) << std::endl; 

     PlainTextKeyBlob keyBlob ={0}; 
     keyBlob.hdr.bType = PLAINTEXTKEYBLOB; 
     keyBlob.hdr.bVersion = CUR_BLOB_VERSION; 
     keyBlob.hdr.reserved = 0; 
     keyBlob.hdr.aiKeyAlg = CALG_3DES_112; 
     keyBlob.cbKeySize = key.size(); 
     memcpy(keyBlob.key, key.c_str(), key.size()); 

     DWORD dwSizeBlob = sizeof(BLOBHEADER)+sizeof(DWORD)+key.size(); 
     ret = CryptImportKey(hCryptProv, (const BYTE*)&keyBlob, dwSizeBlob, 0, CRYPT_EXPORTABLE, &hCryptKey); 
     if(ret == 0) std::wcout << LastError(GetLastError()) << std::endl; 

     DWORD dwMode = CRYPT_MODE_CBC; 
     CryptSetKeyParam(hCryptKey, KP_MODE, (BYTE*)&dwMode, 0); 
     CryptSetKeyParam(hCryptKey, KP_IV,(const BYTE*) pIV, 0) ; 

     std::vector<BYTE> buffer(1024); 
     memcpy(&buffer[0], passwd.c_str(), passwd.size()); 
     DWORD dwFilled = passwd.size(); 
     ret = CryptEncrypt(hCryptKey, NULL, TRUE, 0, (LPBYTE)&buffer[0], &dwFilled, (DWORD)buffer.size()); 
     if(ret == 0) std::wcout << LastError(GetLastError()) << std::endl; 
     buffer.resize(dwFilled); 
     if(hCryptKey) CryptDestroyKey(hCryptKey); 
     if(hHash) CryptDestroyHash(hHash); 
     if(hCryptProv) CryptReleaseContext(hCryptProv, 0); 
     return buffer; 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    auto result = Test3DES("password"); 
    std::for_each(begin(result), end(result), [](BYTE b) { 
     cout << to_string((_ULonglong)b) << " "; 
    }); 
    cout << std::endl; 

    return 0; 
} 
+0

Этот образец работал отлично. Не уверен, что я сделал не так, но теперь он отлично работает – SparcU

+0

Да, я тоже не видел проблемы. Я не думаю, что проблема была в коде, который вы опубликовали, или я просто не мог прищуриться, чтобы это увидеть. – Skrymsli

+0

@Skrymsli Когда я устанавливаю IV в 24 байта, я получаю: «Указанный вектор инициализации (IV) не соответствует размеру блока для этого алгоритма» сообщение об ошибке. Это как-то связано с CipherMode? –

0

Ваш C++ компилируются со строками шириной/Unicode - так что вы используете в качестве ключевого широкого/Unicode байт, соответствующей строке "5656b2e4ba2f22e" (30 байт, вероятно), в то время как в C# ваши преобразовывают ту же строку в байты используя кодировку ASCII, поэтому вы получаете байты ASCII для одной и той же строки (15 байт).

Попробуйте использовать кодировку Unicode на C# или лучше объявить ключи как массивы байтов вместо строк.

EDIT

Неправильный ответ - string всегда 8 бит на символ, так что проблема не может быть из-за разницы в кодировании.

+0

C++ - это UNICODE, но char и string не влияют столько, сколько знаю – SparcU

+0

Если строки Unicode '(LPBYTE) cipherText.c_str()' is '0x35 0x00 0x36 0x00. , .', тогда как 'Encoding.ASCII.GetBytes (" 5656b2e4ba2f22e ");' is '0x35 0x36. , .' – MiMo

+0

строки не являются юникодами, если вы не переопределяете их. – SparcU

1

Таким образом, ваш ключ System.Text.Encoding.ASCII.GetBytes("5656b2e4ba2f22e") составляет 15 байт, что не является допустимой длиной ключа для Triple DES, вам нужно 16 байт или 24 байта. Я предполагаю, что эти две реализации компенсируют тот факт, что они хотят иметь более крупный ключ по-разному.

P.S.

Это действительно странно, чтобы использовать значение исходного байта строки константы ascii для использования для ключа и IV. В вашем примере ключ и IV являются шестнадцатеричными числами в строке. Похоже, что они должны быть в два раза больше длины байт вы хотите так, что каждые два символа представлен полный диапазон возможных байт, а затем преобразуется с помощью шестнадцатеричных Conveter, How can I convert a hex string to a byte array?, C++ convert string to hexadecimal and vice versa

Подразумевается безопасность иметь всегда имея Hexidecimal представленного что вы получаете только ASCII-байты для вашего алгоритма, это то, что вы разрезаете возможное пространство ключей вниз квадратным корнем!

+0

Я исправил ключевой вопрос. Теперь это 16 байт. Все еще зашифрованные данные отличаются с использованием методов .NET и C++. – SparcU

0

Имейте в виду, что ключи DES являются ключами с уверенностью. Это означает, что один бит каждого байта используется для проверки правильности вашего ключа. Возможно, у вас эти биты неправильные? Вы сможете найти это на странице DES wikipedia.

Кроме того, для работы TDES вам потребуется не менее двух целых (64-разрядных) клавиш.

+0

Вы имеете в виду, что случайная последовательность, которую я использую, может быть недействительным? – SparcU

Смежные вопросы