尝试重现MS-Office的文件加密时得到错误的'encryptedVerifierHashInput'值

时间:2019-06-14 16:33:05

标签: c# encryption aes ms-office

我正在尝试重现MS-Word根据给定密码对文档进行加密的方式。

我有一个文档,该文档的密码为'1234',使用最新版本的Word和最新的算法(如果我没记错的话)进行加密,如此处所述: https://docs.microsoft.com/en-us/openspecs/office_file_formats/ms-offcrypto/87020a34-e73f-4139-99bc-bbdf6cf6fa55

加密后,我将文件提取为.zip并从EncryptionInfo流中的cryptonKey节点中获取以下xml:

<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==

0 个答案:

没有答案