Неправильное значение «encryptedVerifierHashInput» при попытке воспроизвести шифрование файлов MS-Office - PullRequest
2 голосов
/ 14 июня 2019

Я пытаюсь воспроизвести способ шифрования документов 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==
...