예제 #1
0
        /// <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);
        }
예제 #2
0
        // 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;
		}
예제 #8
0
        /// <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));
            }
        }