Я пытаюсь воспроизвести способ шифрования документов MS-Word на основе заданного пароля.
У меня есть документ с паролем '1234' , зашифрованныйс последней версией Word с их самым последним алгоритмом (если я не ошибаюсь) , как описано здесь: https://docs.microsoft.com/en-us/openspecs/office_file_formats/ms-offcrypto/87020a34-e73f-4139-99bc-bbdf6cf6fa55
После шифрования я извлек файл как .zip и получилследующий xml из потока EncryptionInfo в узле encryptedKey:
<p:encryptedKey
spinCount="100000"
saltSize="16"
blockSize="16"
keyBits="256"
hashSize="64"
cipherAlgorithm="AES"
cipherChaining="ChainingModeCBC"
hashAlgorithm="SHA512"
saltValue="80qqrantPuTWTxMNG1Rc/w=="
encryptedVerifierHashInput="X37eBfS7J5BNwTcD4dK2CQ=="
encryptedVerifierHashValue="NByt5WceaCTt1VqwV5JWRVpn6hCKru+SKQY3/JsevZ+MQ0NSTaHYvwRCFz1FYpaDBIzwKIiUfe1Z2w38BXqmdw=="
encryptedKeyValue="GXTPnjPkcLkZ1BQOAgFMKA7yVeZ3a1r82NfxHMC8MRw="
/>
Из saltValue и исходного пароля я хотел найти значение encryptedVerifierHashInput , но я получаюдругой результат, и я не могу найти то, что я пропустил.
Вот основная функция:
string str_password = "1234";
string base64_saltValue = "80qqrantPuTWTxMNG1Rc/w==";
string expectedBase64_encryptedVerifierHashInput = "X37eBfS7J5BNwTcD4dK2CQ==";
byte[] password = Encoding.Unicode.GetBytes(str_password);
byte[] salt = Convert.FromBase64String(base64_saltValue);
// BlockKey for encryptedVerifierHashInput's key, as specified in:
// https://docs.microsoft.com/en-us/openspecs/office_file_formats/ms-offcrypto/a57cb947-554f-4e5e-b150-3f2978225e92
byte[] blockKey = { 0xfe, 0xa7, 0xd2, 0x76, 0x3b, 0x4b, 0x9e, 0x79 };
Console.WriteLine("salt length: \t" + salt.Length);
SHA512 shaM = new SHA512Managed();
byte[] IV_tmp = shaM.ComputeHash(Combine(salt, blockKey)); // length = 32: truncating
byte[] IV = new byte[16];
Array.Copy(IV_tmp, IV, 16);
Console.WriteLine("IV length: \t" + IV.Length);
Console.WriteLine("blockKey length: " + blockKey.Length);
byte[] key_tmp = CreateKey(salt, password, blockKey); // length = 64: truncating
byte[] key = new byte[32];
Array.Copy(key_tmp, key, 32);
Console.WriteLine("key length: \t" + key.Length);
Console.WriteLine("key base64:\t" + Convert.ToBase64String(key));
byte [] encryptedVerifierHashInput = Encrypt(salt, key, IV);
Console.WriteLine("\n encryptedVerifierHashInput from password '" + str_password + "': \n");
Console.WriteLine("Expected: " + expectedBase64_encryptedVerifierHashInput);
Console.WriteLine("Got: " + Convert.ToBase64String(encryptedVerifierHashInput));
Console.Read();
Функция, генерирующая ключ:
// https://docs.microsoft.com/en-us/openspecs/office_file_formats/ms-offcrypto/74d60145-a0f0-44be-99ce-c65d211b4eb7
private static byte[] CreateKey(byte[] salt, byte[] password, byte[] blockKey, int spinCount= 100000)
{
SHA512 shaM = new SHA512Managed();
// Initial password hash: H0 = H(salt + password)
byte[] hash = shaM.ComputeHash(Combine(salt, password));
// Hash iteration: Hn = H(iterator + Hn-1)
for (int iter = 0; iter < spinCount; iter++)
{
hash = shaM.ComputeHash(
Combine(BitConverter.GetBytes(iter), hash)
);
}
// Hfinal = H(Hn + blockKey)
hash = shaM.ComputeHash(Combine(hash, blockKey));
return hash;
}
Функции шифрования, что я взял из другого поста:
public static byte[] Encrypt(byte[] data, byte[] key, byte[] iv)
{
using (var aes = Aes.Create())
{
aes.KeySize = 256;
aes.BlockSize = 128;
aes.Padding = PaddingMode.Zeros;
aes.Key = key;
aes.IV = iv;
using (var encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
{
return PerformCryptography(data, encryptor);
}
}
}
private static byte[] PerformCryptography(byte[] data, ICryptoTransform cryptoTransform)
{
using (var ms = new MemoryStream())
using (var cryptoStream = new CryptoStream(ms, cryptoTransform, CryptoStreamMode.Write))
{
cryptoStream.Write(data, 0, data.Length);
cryptoStream.FlushFinalBlock();
return ms.ToArray();
}
}
Функция объединения (хотя не должно вызывать никаких проблем) :
public static byte[] Combine(byte[] first, byte[] second)
{
byte[] ret = new byte[first.Length + second.Length];
Buffer.BlockCopy(first, 0, ret, 0, first.Length);
Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);
return ret;
}
И, наконец, выводЯ получаю:
salt length: 16
IV length: 16
blockKey length: 8
key length: 32
key base64: dgtuBO7k4UhhoXdICagE7pkcbPKsxY1zEX89ayzXNn0=
encryptedVerifierHashInput from password '1234':
Expected: X37eBfS7J5BNwTcD4dK2CQ==
Got: kv8EFmm1+ho1dy7zTyFFWg==