.NET triple des to Windows API

c#, c++, encryption, winapi

Solution

I set up some sample projects starting with your code. You didn't include everything so I had to add some stuff. By the time I compiled and tested I was getting the same answer in both C++ and C#. I suspect the problem may be something in the way you are specifying the cipherText buffer? This is all of my test code so it should be easy for you to set up some sample project and see if you get the same result too, then maybe you can figure it out from there:

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;
}

Problem

I have some code for encryption in C# that I have to rewrite in C++ I saw several similar questions here on SO but somehow I still could not figure this out. Encoding the same string with the same password yields different results. The C# code ``` 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; } ``` Result 255,142,22,151,93,255,156,10,174,10,250,92,144,0,60,142 EDITED: Added new C++ version ``` 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; } ``` result 167,177,201,56,123,240,169,174 Old C++ version 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; } ```

Original source

Related problems