using OfficeOpenXml.Utils; using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Xml; namespace OfficeOpenXml { /// /// Algorithm for password hash /// internal enum eProtectedRangeAlgorithm { /// /// Specifies that the MD2 algorithm, as defined by RFC 1319, shall be used. /// MD2, /// /// Specifies that the MD4 algorithm, as defined by RFC 1319, shall be used. /// MD4, /// /// Specifies that the MD5 algorithm, as defined by RFC 1319, shall be used. /// MD5, /// /// Specifies that the RIPEMD-128 algorithm, as defined by RFC 1319, shall be used. /// RIPEMD128, /// /// Specifies that the RIPEMD-160 algorithm, as defined by ISO/IEC10118-3:2004 shall be used. /// RIPEMD160, /// /// Specifies that the SHA-1 algorithm, as defined by ISO/IEC 10118-3:2004 shall be used. /// SHA1, /// /// Specifies that the SHA-256 algorithm, as defined by ISO/IEC10118-3:2004 shall be used. /// SHA256, /// /// Specifies that the SHA-384 algorithm, as defined by ISO/IEC 10118-3:2004 shall be used. /// SHA384, /// /// Specifies that the SHA-512 algorithm, as defined by ISO/IEC10118-3:2004 shall be used. /// SHA512, /// /// Specifies that the WHIRLPOOL algorithm, as defined by ISO/IEC 10118-3:2004 shall be used. /// WHIRLPOOL } public class ExcelProtectedRange : XmlHelper { public string Name { get { return GetXmlNodeString("@name"); } set { SetXmlNodeString("@name",value); } } ExcelAddress _address=null; public ExcelAddress Address { get { if(_address==null) { _address=new ExcelAddress(GetXmlNodeString("@sqref")); } return _address; } set { SetXmlNodeString("@sqref", SqRefUtility.ToSqRefAddress(value.Address)); _address=value; } } internal ExcelProtectedRange(string name, ExcelAddress address, XmlNamespaceManager ns, XmlNode topNode) : base(ns,topNode) { Name = name; Address = address; } /// /// Sets the password for the range /// /// public void SetPassword(string password) { var byPwd = Encoding.Unicode.GetBytes(password); var rnd = RandomNumberGenerator.Create(); var bySalt=new byte[16]; rnd.GetBytes(bySalt); //Default SHA512 and 10000 spins Algorithm=eProtectedRangeAlgorithm.SHA512; SpinCount = SpinCount < 100000 ? 100000 : SpinCount; //Combine salt and password and calculate the initial hash var hp=new SHA512CryptoServiceProvider(); var buffer=new byte[byPwd.Length + bySalt.Length]; Array.Copy(bySalt, buffer, bySalt.Length); Array.Copy(byPwd, 0, buffer, 16, byPwd.Length); var hash = hp.ComputeHash(buffer); //Now iterate the number of spinns. for (var i = 0; i < SpinCount; i++) { buffer=new byte[hash.Length+4]; Array.Copy(hash, buffer, hash.Length); Array.Copy(BitConverter.GetBytes(i), 0, buffer, hash.Length, 4); hash = hp.ComputeHash(buffer); } Salt = Convert.ToBase64String(bySalt); Hash = Convert.ToBase64String(hash); } public string SecurityDescriptor { get { return GetXmlNodeString("@securityDescriptor"); } set { SetXmlNodeString("@securityDescriptor",value); } } internal int SpinCount { get { return GetXmlNodeInt("@spinCount"); } set { SetXmlNodeString("@spinCount",value.ToString(CultureInfo.InvariantCulture)); } } internal string Salt { get { return GetXmlNodeString("@saltValue"); } set { SetXmlNodeString("@saltValue", value); } } internal string Hash { get { return GetXmlNodeString("@hashValue"); } set { SetXmlNodeString("@hashValue", value); } } eProtectedRangeAlgorithm _algorithm = eProtectedRangeAlgorithm.SHA512; internal eProtectedRangeAlgorithm Algorithm { get { var v=GetXmlNodeString("@algorithmName"); return (eProtectedRangeAlgorithm)Enum.Parse(typeof(eProtectedRangeAlgorithm), v.Replace("-", "")); } set { var v = value.ToString(); if(v.StartsWith("SHA")) { v=v.Insert(3,"-"); } else if(v.StartsWith("RIPEMD")) { v=v.Insert(6,"-"); } SetXmlNodeString("@algorithmName", v); } } } }