2

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, '\0');
    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?

Hans Malherbe
  • 2,988
  • 24
  • 19
  • I tried you program, it returns correct result. Using .netcore app though. – Mike Twc Jun 28 '21 at 16:38
  • Typically, when you Base64 decode something, the data that comes back is binary (the reason you use Base64 encoding is to make binary data deliverable via ASCII protocol). You have it saving to a `std::string` which can not store binary data properly. What I would do is change your `base64_decode` to decode to a byte array. – Andy Jun 28 '21 at 19:06
  • In C++ we can store binary data in a std::string just fine. – Hans Malherbe Jun 29 '21 at 06:01
  • You appear to have not specified the IV in your C++ code (it’s presumably zero-initialized… or stack garbage). That only affects the first block (16 bytes in this case) of the decryption output – bartonjs Jan 12 '22 at 12:51

0 Answers0