Decrypt RijndaelManaged in C++ (CNG)

  aescng, c++, cng, rijndaelmanaged

The C# code below outputs 0123456789012345678901234567890123456789:

        static void Main(string[] args)
        {

            byte[] salt = Encoding.ASCII.GetBytes("saltycrack");
            Console.WriteLine(Decrypt("NpN45cs2+bVLtEgp6ZmBmmHLH89aFdHIbDjhstTK0Vb1VMr7jBsliKD9siQGGxGS", "pass", salt));
        }

        public static string Decrypt(string encryptedText, string encryptionPassword, byte[] salt)
        {
            var algorithm = GetAlgorithm(encryptionPassword, salt);
            using (ICryptoTransform decryptor = algorithm.CreateDecryptor(algorithm.Key, algorithm.IV))
            {
                byte[] encryptedBytes = Convert.FromBase64String(encryptedText);
                return Encoding.UTF8.GetString(InMemoryCrypt(encryptedBytes, decryptor));
            }
        }
        private static byte[] InMemoryCrypt(byte[] data, ICryptoTransform transform)
        {
            MemoryStream memory = new MemoryStream();
            using (Stream stream = new CryptoStream(memory, transform, CryptoStreamMode.Write))
            {
                stream.Write(data, 0, data.Length);
            }
            return memory.ToArray();
        }
        private static RijndaelManaged GetAlgorithm(string encryptionPassword, byte[] salt)
        {
            var key = new Rfc2898DeriveBytes(encryptionPassword, salt);
            var algorithm = new RijndaelManaged();
            algorithm.Key = key.GetBytes(algorithm.KeySize / 8);
            algorithm.IV = key.GetBytes(algorithm.BlockSize / 8);
            return algorithm;
        }

I want to do the same decryption using Microsoft CNG in C++:

int main(int argc, char** argv)
{
    auto salt = std::string("saltycrack");
    auto pass = std::string("pass");
    std::string cipher_text = base64_decode("NpN45cs2+bVLtEgp6ZmBmmHLH89aFdHIbDjhstTK0Vb1VMr7jBsliKD9siQGGxGS");
    NTSTATUS bcryptResult = 0;

    BCRYPT_ALG_HANDLE alg = 0;
    assert(BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&alg, BCRYPT_AES_ALGORITHM, 0, 0)));

    BCRYPT_ALG_HANDLE prf = NULL;
    assert(BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&prf, BCRYPT_SHA1_ALGORITHM, nullptr, BCRYPT_ALG_HANDLE_HMAC_FLAG)));
        
    auto key_len = 32;
    ULONGLONG iter = 1000;
    ULONG kbh_len = sizeof(BCRYPT_KEY_DATA_BLOB_HEADER) + key_len;
    BCRYPT_KEY_DATA_BLOB_HEADER* kbh = (BCRYPT_KEY_DATA_BLOB_HEADER*)malloc(kbh_len);
    assert(BCRYPT_SUCCESS(BCryptDeriveKeyPBKDF2(prf, (PUCHAR)pass.data(), pass.size(), (PUCHAR)salt.data(), salt.size(), iter, (uint8_t*)(kbh + 1), key_len, 0)));

    kbh->dwMagic = BCRYPT_KEY_DATA_BLOB_MAGIC;
    kbh->dwVersion = BCRYPT_KEY_DATA_BLOB_VERSION1;
    kbh->cbKeyData = key_len;
    DWORD key_obj_len;
    ULONG got;
    assert(BCRYPT_SUCCESS(BCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, (PBYTE)&key_obj_len, sizeof(DWORD), &got, 0)));
    uint8_t* key_obj = new uint8_t[key_obj_len];
    BCRYPT_KEY_HANDLE key = 0;

    assert(BCRYPT_SUCCESS(BCryptImportKey(alg, NULL, BCRYPT_KEY_DATA_BLOB, &key, key_obj, key_obj_len, (PUCHAR)kbh, kbh_len, 0)));

    {
        std::wstring mode = BCRYPT_CHAIN_MODE_CBC;
        BYTE* ptr = reinterpret_cast<BYTE*>(const_cast<wchar_t*>(mode.data()));
        ULONG size = static_cast<ULONG>(sizeof(wchar_t) * (mode.size() + 1));
        assert(BCRYPT_SUCCESS(BCryptSetProperty(alg, BCRYPT_CHAINING_MODE, ptr, size, 0)));
    }
    DWORD bytes_done = 0;
    DWORD block_len = 0;
    assert(BCRYPT_SUCCESS(BCryptGetProperty(alg, BCRYPT_BLOCK_LENGTH, (BYTE*)&block_len, sizeof(block_len), &bytes_done, 0)));
    auto iv = std::vector<uint8_t>(block_len);
    ULONG bufferlen = 0;
    assert(BCRYPT_SUCCESS(BCryptDecrypt(key, (PUCHAR)cipher_text.data(), cipher_text.size(), 0, (PUCHAR)iv.data(), iv.size(), 0, 0, &bufferlen, BCRYPT_BLOCK_PADDING)));
    auto plaintext = std::string(bufferlen, '{$content}');
    assert(BCRYPT_SUCCESS(BCryptDecrypt(key, (PUCHAR)cipher_text.data(), cipher_text.size(), 0, (PUCHAR)iv.data(), iv.size(), (PUCHAR)plaintext.data(), plaintext.size(), &bufferlen, BCRYPT_BLOCK_PADDING)));
   
    std::cout << plaintext << "n";
    return 0;
}

The C++ program outputs #♂b⌡Cεk╘┴╟AÑ@├0⌠678901234567890123456789.
It looks like decryption partially succeeded which doesn’t make sense.

Do I need to decode the decrypted data or is there data corruption somewhere?

Source: Windows Questions C++

LEAVE A COMMENT