/// <summary> ///验证密码 /// </summary> /// <param name="key">加密密钥</param> /// <param name="encryptionInfo">从ENCRYPTIOINFO流中提取里面的OLE文档的加密信息</param> /// <returns></returns> private bool IsPasswordValid(byte[] key, EncryptionInfo encryptionInfo) { RijndaelManaged decryptKey = new RijndaelManaged(); decryptKey.KeySize = encryptionInfo.Header.KeySize; decryptKey.Mode = CipherMode.ECB; decryptKey.Padding = PaddingMode.None; ICryptoTransform decryptor = decryptKey.CreateDecryptor(key, null); //解密验证器 MemoryStream dataStream = new MemoryStream(encryptionInfo.Verifier.EncryptedVerifier); CryptoStream cryptoStream = new CryptoStream(dataStream, decryptor, CryptoStreamMode.Read); byte[] decryptedVerifier = new byte[16]; cryptoStream.Read(decryptedVerifier, 0, 16); dataStream = new MemoryStream(encryptionInfo.Verifier.EncryptedVerifierHash); cryptoStream = new CryptoStream(dataStream, decryptor, CryptoStreamMode.Read); //解密验证哈希 byte[] decryptedVerifierHash = new byte[16]; cryptoStream.Read(decryptedVerifierHash, 0, (int)16); //获取哈希解密验证 var sha = new SHA1Managed(); var hash = sha.ComputeHash(decryptedVerifier); //Equal? for (int i = 0; i < 16; i++) { if (hash[i] != decryptedVerifierHash[i]) { return(false); } } return(true); }
// Help method to print a storage part binary to c:\temp //private void PrintStorage(IStorage storage, System.Runtime.InteropServices.ComTypes.STATSTG sTATSTG, string topName) //{ // IStorage ds; // if (topName.Length > 0) // { // topName = topName[0] < 'A' ? topName.Substring(1, topName.Length - 1) : topName; // } // storage.OpenStorage(sTATSTG.pwcsName, // null, // (uint)(STGM.DIRECT | STGM.READ | STGM.SHARE_EXCLUSIVE), // IntPtr.Zero, // 0, // out ds); // System.Runtime.InteropServices.ComTypes.STATSTG statstgSub; // ds.Stat(out statstgSub, (uint)STATFLAG.STATFLAG_DEFAULT); // IEnumSTATSTG pIEnumStatStgSub = null; // System.Runtime.InteropServices.ComTypes.STATSTG[] regeltSub = { statstgSub }; // ds.EnumElements(0, IntPtr.Zero, 0, out pIEnumStatStgSub); // uint fetched = 0; // while (pIEnumStatStgSub.Next(1, regeltSub, out fetched) == 0) // { // string sName = regeltSub[0].pwcsName[0] < 'A' ? regeltSub[0].pwcsName.Substring(1, regeltSub[0].pwcsName.Length - 1) : regeltSub[0].pwcsName; // if (regeltSub[0].type == 1) // { // PrintStorage(ds, regeltSub[0], topName + sName + "_"); // } // else if(regeltSub[0].type==2) // { // File.WriteAllBytes(@"c:\temp\" + topName + sName + ".bin", GetOleStream(ds, regeltSub[0])); // } // } //} /// <summary> /// 解密文档 /// </summary> /// <param name="data">加密的数据</param> /// <param name="encryptionInfo">加密信息对象</param> /// <param name="password">密码</param> /// <returns></returns> private MemoryStream DecryptDocument(byte[] data, EncryptionInfo encryptionInfo, string password) { ExceptionHelper.TrueThrow <Exception>(encryptionInfo == null, "无效的文件缺少EncryptionInfo。"); long size = BitConverter.ToInt64(data, 0); var encryptedData = new byte[data.Length - 8]; Array.Copy(data, 8, encryptedData, 0, encryptedData.Length); MemoryStream doc = new MemoryStream(); ExceptionHelper.FalseThrow <UnauthorizedAccessException>(encryptionInfo.Header.AlgID == AlgorithmID.AES128 || (encryptionInfo.Header.AlgID == AlgorithmID.Flags && ((encryptionInfo.Flags & (Flags.fAES | Flags.fExternal | Flags.fCryptoAPI)) == (Flags.fAES | Flags.fCryptoAPI))) || encryptionInfo.Header.AlgID == AlgorithmID.AES192 || encryptionInfo.Header.AlgID == AlgorithmID.AES256, "无效的密码"); RijndaelManaged decryptKey = new RijndaelManaged(); decryptKey.KeySize = encryptionInfo.Header.KeySize; decryptKey.Mode = CipherMode.ECB; decryptKey.Padding = PaddingMode.None; var key = GetPasswordHash(password, encryptionInfo); if (IsPasswordValid(key, encryptionInfo)) { ICryptoTransform decryptor = decryptKey.CreateDecryptor(key, null); MemoryStream dataStream = new MemoryStream(encryptedData); CryptoStream cryptoStream = new CryptoStream(dataStream, decryptor, CryptoStreamMode.Read); byte[] decryptedData = new byte[size]; cryptoStream.Read(decryptedData, 0, (int)size); doc.Write(decryptedData, 0, (int)size); } return(doc); }
/// <summary> /// 创建哈希。 /// </summary> /// <param name="password">密码</param> /// <param name="encryptionInfo">从ENCRYPTIOINFO流中提取里面的OLE文档的加密信息</param> /// <returns>哈希加密文件</returns> private byte[] GetPasswordHash(string password, EncryptionInfo encryptionInfo) { byte[] hash = null; byte[] tempHash = new byte[4 + 20]; //Iterator + prev. hash try { HashAlgorithm hashProvider; if (encryptionInfo.Header.AlgIDHash == AlgorithmHashID.SHA1 || encryptionInfo.Header.AlgIDHash == AlgorithmHashID.App && (encryptionInfo.Flags & Flags.fExternal) == 0) { hashProvider = new SHA1CryptoServiceProvider(); } else if (encryptionInfo.Header.KeySize > 0 && encryptionInfo.Header.KeySize < 80) { throw new Exception("RC4 Hash provider is not supported. Must be SHA1(AlgIDHash == 0x8004)"); } else { throw new Exception("Hash provider is invalid. Must be SHA1(AlgIDHash == 0x8004)"); } hash = hashProvider.ComputeHash(CombinePassword(encryptionInfo.Verifier.Salt, password)); //Iterate 50 000 times, inserting i in first 4 bytes and then the prev. hash in byte 5-24 for (int i = 0; i < 50000; i++) { Array.Copy(BitConverter.GetBytes(i), tempHash, 4); Array.Copy(hash, 0, tempHash, 4, hash.Length); hash = hashProvider.ComputeHash(tempHash); } // Append "block" (0) Array.Copy(hash, tempHash, hash.Length); Array.Copy(System.BitConverter.GetBytes(0), 0, tempHash, hash.Length, 4); hash = hashProvider.ComputeHash(tempHash); /***** Now use the derived key algorithm *****/ byte[] derivedKey = new byte[64]; int keySizeBytes = encryptionInfo.Header.KeySize / 8; //First XOR hash bytes with 0x36 and fill the rest with 0x36 for (int i = 0; i < derivedKey.Length; i++) derivedKey[i] = (byte)(i < hash.Length ? 0x36 ^ hash[i] : 0x36); byte[] X1 = hashProvider.ComputeHash(derivedKey); //if verifier size is bigger than the key size we can return X1 if (encryptionInfo.Verifier.VerifierHashSize > keySizeBytes) return FixHashSize(X1, keySizeBytes); //Else XOR hash bytes with 0x5C and fill the rest with 0x5C for (int i = 0; i < derivedKey.Length; i++) derivedKey[i] = (byte)(i < hash.Length ? 0x5C ^ hash[i] : 0x5C); byte[] X2 = hashProvider.ComputeHash(derivedKey); //Join the two and return byte[] join = new byte[X1.Length + X2.Length]; Array.Copy(X1, 0, join, 0, X1.Length); Array.Copy(X2, 0, join, X1.Length, X2.Length); return FixHashSize(join, keySizeBytes); } catch (Exception ex) { throw (new Exception("创建encryptionkey时发生错误", ex)); } }
/// <summary> ///验证密码 /// </summary> /// <param name="key">加密密钥</param> /// <param name="encryptionInfo">从ENCRYPTIOINFO流中提取里面的OLE文档的加密信息</param> /// <returns></returns> private bool IsPasswordValid(byte[] key, EncryptionInfo encryptionInfo) { RijndaelManaged decryptKey = new RijndaelManaged(); decryptKey.KeySize = encryptionInfo.Header.KeySize; decryptKey.Mode = CipherMode.ECB; decryptKey.Padding = PaddingMode.None; ICryptoTransform decryptor = decryptKey.CreateDecryptor(key, null); //解密验证器 MemoryStream dataStream = new MemoryStream(encryptionInfo.Verifier.EncryptedVerifier); CryptoStream cryptoStream = new CryptoStream(dataStream, decryptor, CryptoStreamMode.Read); byte[] decryptedVerifier = new byte[16]; cryptoStream.Read(decryptedVerifier, 0, 16); dataStream = new MemoryStream(encryptionInfo.Verifier.EncryptedVerifierHash); cryptoStream = new CryptoStream(dataStream, decryptor, CryptoStreamMode.Read); //解密验证哈希 byte[] decryptedVerifierHash = new byte[16]; cryptoStream.Read(decryptedVerifierHash, 0, (int)16); //获取哈希解密验证 var sha = new SHA1Managed(); var hash = sha.ComputeHash(decryptedVerifier); //Equal? for (int i = 0; i < 16; i++) { if (hash[i] != decryptedVerifierHash[i]) { return false; } } return true; }
// Help method to print a storage part binary to c:\temp //private void PrintStorage(IStorage storage, System.Runtime.InteropServices.ComTypes.STATSTG sTATSTG, string topName) //{ // IStorage ds; // if (topName.Length > 0) // { // topName = topName[0] < 'A' ? topName.Substring(1, topName.Length - 1) : topName; // } // storage.OpenStorage(sTATSTG.pwcsName, // null, // (uint)(STGM.DIRECT | STGM.READ | STGM.SHARE_EXCLUSIVE), // IntPtr.Zero, // 0, // out ds); // System.Runtime.InteropServices.ComTypes.STATSTG statstgSub; // ds.Stat(out statstgSub, (uint)STATFLAG.STATFLAG_DEFAULT); // IEnumSTATSTG pIEnumStatStgSub = null; // System.Runtime.InteropServices.ComTypes.STATSTG[] regeltSub = { statstgSub }; // ds.EnumElements(0, IntPtr.Zero, 0, out pIEnumStatStgSub); // uint fetched = 0; // while (pIEnumStatStgSub.Next(1, regeltSub, out fetched) == 0) // { // string sName = regeltSub[0].pwcsName[0] < 'A' ? regeltSub[0].pwcsName.Substring(1, regeltSub[0].pwcsName.Length - 1) : regeltSub[0].pwcsName; // if (regeltSub[0].type == 1) // { // PrintStorage(ds, regeltSub[0], topName + sName + "_"); // } // else if(regeltSub[0].type==2) // { // File.WriteAllBytes(@"c:\temp\" + topName + sName + ".bin", GetOleStream(ds, regeltSub[0])); // } // } //} /// <summary> /// 解密文档 /// </summary> /// <param name="data">加密的数据</param> /// <param name="encryptionInfo">加密信息对象</param> /// <param name="password">密码</param> /// <returns></returns> private MemoryStream DecryptDocument(byte[] data, EncryptionInfo encryptionInfo, string password) { ExceptionHelper.TrueThrow<Exception>(encryptionInfo == null, "无效的文件缺少EncryptionInfo。"); long size = BitConverter.ToInt64(data, 0); var encryptedData = new byte[data.Length - 8]; Array.Copy(data, 8, encryptedData, 0, encryptedData.Length); MemoryStream doc = new MemoryStream(); ExceptionHelper.FalseThrow<UnauthorizedAccessException>(encryptionInfo.Header.AlgID == AlgorithmID.AES128 || (encryptionInfo.Header.AlgID == AlgorithmID.Flags && ((encryptionInfo.Flags & (Flags.fAES | Flags.fExternal | Flags.fCryptoAPI)) == (Flags.fAES | Flags.fCryptoAPI))) || encryptionInfo.Header.AlgID == AlgorithmID.AES192 || encryptionInfo.Header.AlgID == AlgorithmID.AES256, "无效的密码"); RijndaelManaged decryptKey = new RijndaelManaged(); decryptKey.KeySize = encryptionInfo.Header.KeySize; decryptKey.Mode = CipherMode.ECB; decryptKey.Padding = PaddingMode.None; var key = GetPasswordHash(password, encryptionInfo); if (IsPasswordValid(key, encryptionInfo)) { ICryptoTransform decryptor = decryptKey.CreateDecryptor(key, null); MemoryStream dataStream = new MemoryStream(encryptedData); CryptoStream cryptoStream = new CryptoStream(dataStream, decryptor, CryptoStreamMode.Read); byte[] decryptedData = new byte[size]; cryptoStream.Read(decryptedData, 0, (int)size); doc.Write(decryptedData, 0, (int)size); } return doc; }
private MemoryStream GetStreamFromPackage(IStorage storage, ExcelEncryption encryption) { MemoryStream ret = null; comTypes.STATSTG statstg; storage.Stat(out statstg, (uint)STATFLAG.STATFLAG_DEFAULT); IEnumSTATSTG pIEnumStatStg = null; storage.EnumElements(0, IntPtr.Zero, 0, out pIEnumStatStg); comTypes.STATSTG[] regelt = { statstg }; uint fetched = 0; uint res = pIEnumStatStg.Next(1, regelt, out fetched); if (res == 0) { byte[] data; EncryptionInfo encryptionInfo = null; while (res != 1) { switch (statstg.pwcsName) { case "EncryptionInfo": data = GetOleStream(storage, statstg); encryptionInfo = new EncryptionInfo(); encryptionInfo.ReadBinary(data); encryption.Algorithm = encryptionInfo.Header.AlgID == AlgorithmID.AES128 ? EncryptionAlgorithm.AES128 : encryptionInfo.Header.AlgID == AlgorithmID.AES192 ? EncryptionAlgorithm.AES192 : EncryptionAlgorithm.AES256; break; case "EncryptedPackage": data = GetOleStream(storage, statstg); ret = DecryptDocument(data, encryptionInfo, encryption.Password); break; } if ((res = pIEnumStatStg.Next(1, regelt, out fetched)) != 1) { statstg = regelt[0]; } } } Marshal.ReleaseComObject(pIEnumStatStg); return ret; }
/// <summary> /// Create an EncryptionInfo object to encrypt a workbook /// </summary> /// <param name="password">The password</param> /// <param name="algID"></param> /// <param name="key">The Encryption key</param> /// <returns></returns> private EncryptionInfo CreateEncryptionInfo(string password, AlgorithmID algID, out byte[] key) { ExceptionHelper.TrueThrow<ArgumentException>(algID == AlgorithmID.Flags || algID == AlgorithmID.RC4, "必须为aes128,AES192或AES256"); var encryptionInfo = new EncryptionInfo(); encryptionInfo.MajorVersion = 4; encryptionInfo.MinorVersion = 2; encryptionInfo.Flags = Flags.fAES | Flags.fCryptoAPI; //Header encryptionInfo.Header = new EncryptionHeader(); encryptionInfo.Header.AlgID = algID; encryptionInfo.Header.AlgIDHash = AlgorithmHashID.SHA1; encryptionInfo.Header.Flags = encryptionInfo.Flags; encryptionInfo.Header.KeySize = (algID == AlgorithmID.AES128 ? 0x80 : algID == AlgorithmID.AES192 ? 0xC0 : 0x100); encryptionInfo.Header.ProviderType = ProviderType.AES; encryptionInfo.Header.CSPName = "Microsoft Enhanced RSA and AES Cryptographic Provider\0"; encryptionInfo.Header.Reserved1 = 0; encryptionInfo.Header.Reserved2 = 0; encryptionInfo.Header.SizeExtra = 0; //Verifier encryptionInfo.Verifier = new EncryptionVerifier(); encryptionInfo.Verifier.Salt = new byte[16]; var rnd = RandomNumberGenerator.Create(); rnd.GetBytes(encryptionInfo.Verifier.Salt); encryptionInfo.Verifier.SaltSize = 0x10; key = GetPasswordHash(password, encryptionInfo); var verifier = new byte[16]; rnd.GetBytes(verifier); encryptionInfo.Verifier.EncryptedVerifier = EncryptData(key, verifier, true); //AES = 32 Bits encryptionInfo.Verifier.VerifierHashSize = 0x20; SHA1 sha = new SHA1Managed(); var verifierHash = sha.ComputeHash(verifier); encryptionInfo.Verifier.EncryptedVerifierHash = EncryptData(key, verifierHash, false); return encryptionInfo; }
/// <summary> /// 创建哈希。 /// </summary> /// <param name="password">密码</param> /// <param name="encryptionInfo">从ENCRYPTIOINFO流中提取里面的OLE文档的加密信息</param> /// <returns>哈希加密文件</returns> private byte[] GetPasswordHash(string password, EncryptionInfo encryptionInfo) { byte[] hash = null; byte[] tempHash = new byte[4 + 20]; //Iterator + prev. hash try { HashAlgorithm hashProvider; if (encryptionInfo.Header.AlgIDHash == AlgorithmHashID.SHA1 || encryptionInfo.Header.AlgIDHash == AlgorithmHashID.App && (encryptionInfo.Flags & Flags.fExternal) == 0) { hashProvider = new SHA1CryptoServiceProvider(); } else if (encryptionInfo.Header.KeySize > 0 && encryptionInfo.Header.KeySize < 80) { throw new Exception("RC4 Hash provider is not supported. Must be SHA1(AlgIDHash == 0x8004)"); } else { throw new Exception("Hash provider is invalid. Must be SHA1(AlgIDHash == 0x8004)"); } hash = hashProvider.ComputeHash(CombinePassword(encryptionInfo.Verifier.Salt, password)); //Iterate 50 000 times, inserting i in first 4 bytes and then the prev. hash in byte 5-24 for (int i = 0; i < 50000; i++) { Array.Copy(BitConverter.GetBytes(i), tempHash, 4); Array.Copy(hash, 0, tempHash, 4, hash.Length); hash = hashProvider.ComputeHash(tempHash); } // Append "block" (0) Array.Copy(hash, tempHash, hash.Length); Array.Copy(System.BitConverter.GetBytes(0), 0, tempHash, hash.Length, 4); hash = hashProvider.ComputeHash(tempHash); /***** Now use the derived key algorithm *****/ byte[] derivedKey = new byte[64]; int keySizeBytes = encryptionInfo.Header.KeySize / 8; //First XOR hash bytes with 0x36 and fill the rest with 0x36 for (int i = 0; i < derivedKey.Length; i++) { derivedKey[i] = (byte)(i < hash.Length ? 0x36 ^ hash[i] : 0x36); } byte[] X1 = hashProvider.ComputeHash(derivedKey); //if verifier size is bigger than the key size we can return X1 if (encryptionInfo.Verifier.VerifierHashSize > keySizeBytes) { return(FixHashSize(X1, keySizeBytes)); } //Else XOR hash bytes with 0x5C and fill the rest with 0x5C for (int i = 0; i < derivedKey.Length; i++) { derivedKey[i] = (byte)(i < hash.Length ? 0x5C ^ hash[i] : 0x5C); } byte[] X2 = hashProvider.ComputeHash(derivedKey); //Join the two and return byte[] join = new byte[X1.Length + X2.Length]; Array.Copy(X1, 0, join, 0, X1.Length); Array.Copy(X2, 0, join, X1.Length, X2.Length); return(FixHashSize(join, keySizeBytes)); } catch (Exception ex) { throw (new Exception("创建encryptionkey时发生错误", ex)); } }