我正在尝试重现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==