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?