Decrypt C#-encrypted data in C++ using Windows AES crypto provider

3

I need to encrypt the data in C# using RijndaelManaged and decrypt in C++ code.

C# encryption code:

static string Encrypt(string plainText)
{
    byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);

    var keyBytes = new byte[] { /* ... 32 bytes of a key */};
    byte[] iv = new byte[] { /* ... 16 bytes of IV */ };


    var symmetricKey = new RijndaelManaged() 
    { 
        Mode = CipherMode.CBC, 
        Padding = PaddingMode.Zeros, 
        BlockSize = 128, // Must be 128 to be compatible with AES
        KeySize = 256 
    };

    var encryptor = symmetricKey.CreateEncryptor(keyBytes, iv);

    byte[] cipherTextBytes;
    using(var memoryStream = new MemoryStream())
    {
        using(var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
        {
            cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
            cryptoStream.FlushFinalBlock();
            cipherTextBytes = memoryStream.ToArray();
            cryptoStream.Close();
        }
        memoryStream.Close();
    }
    return Convert.ToBase64String(cipherTextBytes);
}

But when decrypting it in C++ code I always get NTE_BAD_DATA reply from CryptDecrypt. Here is C++ code (all checks are removed for clarity):

__declspec(dllexport) DWORD  Decrypt(char* stringBuffer)
{
string encryptedString(stringBuffer);

// Decode base64 string to byte array. Works ok, the binary array is the same as the one in C# code.
vector<BYTE> encryptionBuffer = Base64::decode(encryptedString);
DWORD bufferSize = encryptionBuffer.size();

struct CryptoBlob {
    BLOBHEADER header;
    DWORD cbKeySize;
    BYTE rgbKeyData[32];
} keyBlob;

keyBlob.header.bType = PLAINTEXTKEYBLOB;
keyBlob.header.bVersion = CUR_BLOB_VERSION;
keyBlob.header.reserved = 0;
keyBlob.header.aiKeyAlg = CALG_AES_256;
keyBlob.cbKeySize = 32;

BYTE keyData[32] = { /* 32 bytes of a key the same as in C# code */ };
BYTE ivData[16] = { /* 16 bytes of IV the same as in C# code */ };

memcpy(keyBlob.rgbKeyData, keyData, 32);

HCRYPTKEY hPubKey;
HCRYPTPROV hProv;

CryptAcquireContext(
    &hProv,
    NULL,
    NULL,
    PROV_RSA_AES,
    CRYPT_VERIFYCONTEXT);

CryptImportKey(hProv, (const LPBYTE)&keyBlob, sizeof(keyBlob), 0, 0, &hPubKey);
CryptSetKeyParam(hPubKey, KP_IV, ivData, 0);

// Here the error happens, the value returned is 0x80090005 (NTE_BAD_DATA)
DWORD err = CryptDecrypt(hPubKey, 0, TRUE, 0, encryptionBuffer.data(), &bufferSize);

// overwrite the input buffer with decrypted data
memset(stringBuffer, 0, encryptedString.length());
memcpy(stringBuffer, encryptionBuffer.data(), bufferSize);

return 0;
}

Any idea what could be wrong? Thanks!

c#
c++
encryption
aes
asked on Stack Overflow Apr 21, 2017 by ElDog • edited Apr 21, 2017 by ElDog

2 Answers

5

When you pass TRUE as the 3rd parameter to CryptDecrypt it attempts to undo PKCS#7 padding. When it cannot undo that padding it emits NTE_BAD_DATA.

Since you've changed the padding mode for the encryption to a value other than Pkcs7, you need to pass FALSE and perform manual depadding.

Since PaddingMode.Zeros is not inherently undoable, there's no depadding to perform.

answered on Stack Overflow Apr 21, 2017 by bartonjs
0

If the above is not the answer, I'd recommend looking at they key/iv both in C++ and C# and making sure the byte arrays look exactly the same.

An extra char at the end can cause issues.

If they don't match, be aware that between programming languages and across implementations there can be differences in the types of output returned (e.g. signed/unsigned, char/byte array)and that too can cause issues.

answered on Stack Overflow Apr 21, 2017 by didiz • edited Apr 22, 2017 by didiz

User contributions licensed under CC BY-SA 3.0