/******************************************************************************* * You may amend and distribute as you like, but don't remove this header! * * EPPlus provides server-side generation of Excel 2007/2010 spreadsheets. * See http://www.codeplex.com/EPPlus for details. * * Copyright (C) 2011 Jan Källman * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html * * All code and executables are provided "as is" with no warranty either express or implied. * The author accepts no liability for any damage or loss of business that this product may cause. * * Code change notes: * * Author Change Date * ****************************************************************************** * Jan Källman Added 2013-01-05 *******************************************************************************/ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Xml; namespace OfficeOpenXml.Encryption { internal abstract class EncryptionInfo { internal short MajorVersion; internal short MinorVersion; internal abstract void Read(byte[] data); internal static EncryptionInfo ReadBinary(byte[] data) { var majorVersion = BitConverter.ToInt16(data, 0); var minorVersion = BitConverter.ToInt16(data, 2); EncryptionInfo ret; if ((minorVersion == 2 || minorVersion == 3) && majorVersion <= 4) // minorVersion==1 is RC4, not supported. { ret = new EncryptionInfoBinary(); } else if (majorVersion == 4 && minorVersion==4) { ret = new EncryptionInfoAgile(); } else { throw (new NotSupportedException("Unsupported encryption format")); } ret.MajorVersion = majorVersion; ret.MinorVersion = minorVersion; ret.Read(data); return ret; } } internal enum eCipherAlgorithm { /// /// AES. MUST conform to the AES algorithm. /// AES, /// /// RC2. MUST conform to [RFC2268]. /// RC2, /// /// RC4. /// RC4, /// /// MUST conform to the DES algorithm. /// DES, /// /// MUST conform to the [DRAFT-DESX] algorithm. /// DESX, /// /// 3DES. MUST conform to the [RFC1851] algorithm. /// TRIPLE_DES, /// 3DES_112 MUST conform to the [RFC1851] algorithm. TRIPLE_DES_112 } internal enum eChainingMode { /// /// Cipher block chaining (CBC). /// ChainingModeCBC, /// /// Cipher feedback chaining (CFB), with 8-bit window. /// ChainingModeCFB } /// /// Hashalgorithm /// internal enum eHashAlogorithm { /// /// Sha 1-MUST conform to [RFC4634] /// SHA1, /// /// Sha 256-MUST conform to [RFC4634] /// SHA256, /// /// Sha 384-MUST conform to [RFC4634] /// SHA384, /// /// Sha 512-MUST conform to [RFC4634] /// SHA512, /// /// MD5 /// MD5, /// /// MD4 /// MD4, /// /// MD2 /// MD2, /// /// RIPEMD-128 MUST conform to [ISO/IEC 10118] /// RIPEMD128, /// /// RIPEMD-160 MUST conform to [ISO/IEC 10118] /// RIPEMD160, /// /// WHIRLPOOL MUST conform to [ISO/IEC 10118] /// WHIRLPOOL } /// /// Handels the agile encryption /// internal class EncryptionInfoAgile : EncryptionInfo { XmlNamespaceManager _nsm; public EncryptionInfoAgile() { var nt = new NameTable(); _nsm = new XmlNamespaceManager(nt); _nsm.AddNamespace("d", "http://schemas.microsoft.com/office/2006/encryption"); _nsm.AddNamespace("c", "http://schemas.microsoft.com/office/2006/keyEncryptor/certificate"); _nsm.AddNamespace("p", "http://schemas.microsoft.com/office/2006/keyEncryptor/password"); } internal class EncryptionKeyData : XmlHelper { public EncryptionKeyData(XmlNamespaceManager nsm, XmlNode topNode) : base(nsm, topNode) { } internal byte[] SaltValue { get { var s = GetXmlNodeString("@saltValue"); if (!string.IsNullOrEmpty(s)) { return Convert.FromBase64String(s); } return null; } set { SetXmlNodeString("@saltValue", Convert.ToBase64String(value)); } } internal eHashAlogorithm HashAlgorithm { get { return GetHashAlgorithm(GetXmlNodeString("@hashAlgorithm")); } set { SetXmlNodeString("@hashAlgorithm", GetHashAlgorithmString(value)); } } private eHashAlogorithm GetHashAlgorithm(string v) { switch (v) { case "RIPEMD-128": return eHashAlogorithm.RIPEMD128; case "RIPEMD-160": return eHashAlogorithm.RIPEMD160; case "SHA-1": return eHashAlogorithm.SHA1; default: try { return (eHashAlogorithm)Enum.Parse(typeof(eHashAlogorithm),v); } catch { throw (new InvalidDataException("Invalid Hash algorithm")); } } } private string GetHashAlgorithmString(eHashAlogorithm value) { switch (value) { case eHashAlogorithm.RIPEMD128: return "RIPEMD-128"; case eHashAlogorithm.RIPEMD160: return "RIPEMD-160"; case eHashAlogorithm.SHA1: return "SHA-1"; default: return value.ToString(); } } internal eChainingMode ChiptherChaining { get { var v=GetXmlNodeString("@cipherChaining"); try { return (eChainingMode)Enum.Parse(typeof(eChainingMode), v); } catch { throw (new InvalidDataException("Invalid chaining mode")); } } set { SetXmlNodeString("@cipherChaining", value.ToString()); } } internal eCipherAlgorithm CipherAlgorithm { get { return GetCipherAlgorithm(GetXmlNodeString("@cipherAlgorithm")); } set { SetXmlNodeString("@cipherAlgorithm", GetCipherAlgorithmString(value)); } } private eCipherAlgorithm GetCipherAlgorithm(string v) { switch (v) { case "3DES": return eCipherAlgorithm.TRIPLE_DES; case "3DES_112": return eCipherAlgorithm.TRIPLE_DES_112; default: try { return (eCipherAlgorithm)Enum.Parse(typeof(eCipherAlgorithm), v); } catch { throw (new InvalidDataException("Invalid Hash algorithm")); } } } private string GetCipherAlgorithmString(eCipherAlgorithm alg) { switch (alg) { case eCipherAlgorithm.TRIPLE_DES: return "3DES"; case eCipherAlgorithm.TRIPLE_DES_112: return "3DES_112"; default: return alg.ToString(); } } internal int HashSize { get { return GetXmlNodeInt("@hashSize"); } set { SetXmlNodeString("@hashSize", value.ToString()); } } internal int KeyBits { get { return GetXmlNodeInt("@keyBits"); } set { SetXmlNodeString("@keyBits", value.ToString()); } } internal int BlockSize { get { return GetXmlNodeInt("@blockSize"); } set { SetXmlNodeString("@blockSize", value.ToString()); } } internal int SaltSize { get { return GetXmlNodeInt("@saltSize"); } set { SetXmlNodeString("@saltSize", value.ToString()); } } } internal class EncryptionDataIntegrity : XmlHelper { public EncryptionDataIntegrity(XmlNamespaceManager nsm, XmlNode topNode) : base(nsm, topNode) { } internal byte[] EncryptedHmacValue { get { var s = GetXmlNodeString("@encryptedHmacValue"); if (!string.IsNullOrEmpty(s)) { return Convert.FromBase64String(s); } return null; } set { SetXmlNodeString("@encryptedHmacValue", Convert.ToBase64String(value)); } } internal byte[] EncryptedHmacKey { get { var s = GetXmlNodeString("@encryptedHmacKey"); if (!string.IsNullOrEmpty(s)) { return Convert.FromBase64String(s); } return null; } set { SetXmlNodeString("@encryptedHmacKey", Convert.ToBase64String(value)); } } } internal class EncryptionKeyEncryptor : EncryptionKeyData { public EncryptionKeyEncryptor(XmlNamespaceManager nsm, XmlNode topNode) : base(nsm, topNode) { } internal byte[] EncryptedKeyValue { get { var s = GetXmlNodeString("@encryptedKeyValue"); if (!string.IsNullOrEmpty(s)) { return Convert.FromBase64String(s); } return null; } set { SetXmlNodeString("@encryptedKeyValue", Convert.ToBase64String(value)); } } internal byte[] EncryptedVerifierHash { get { var s = GetXmlNodeString("@encryptedVerifierHashValue"); if (!string.IsNullOrEmpty(s)) { return Convert.FromBase64String(s); } return null; } set { SetXmlNodeString("@encryptedVerifierHashValue", Convert.ToBase64String(value)); } } internal byte[] EncryptedVerifierHashInput { get { var s = GetXmlNodeString("@encryptedVerifierHashInput"); if (!string.IsNullOrEmpty(s)) { return Convert.FromBase64String(s); } return null; } set { SetXmlNodeString("@encryptedVerifierHashInput", Convert.ToBase64String(value)); } } internal byte[] VerifierHashInput { get; set; } internal byte[] VerifierHash { get; set; } internal byte[] KeyValue { get; set; } internal int SpinCount { get { return GetXmlNodeInt("@spinCount"); } set { SetXmlNodeString("@spinCount", value.ToString()); } } } /* */ /*** * * * * * * * * * KeyEncryptors { get; private set; } internal XmlDocument Xml {get;set;} internal override void Read(byte[] data) { var byXml = new byte[data.Length - 8]; Array.Copy(data, 8, byXml, 0, data.Length - 8); var xml = Encoding.UTF8.GetString(byXml); ReadFromXml(xml); } internal void ReadFromXml(string xml) { Xml = new XmlDocument(); XmlHelper.LoadXmlSafe(Xml, xml, Encoding.UTF8); var node = Xml.SelectSingleNode("/d:encryption/d:keyData", _nsm); KeyData = new EncryptionKeyData(_nsm, node); node = Xml.SelectSingleNode("/d:encryption/d:dataIntegrity", _nsm); DataIntegrity = new EncryptionDataIntegrity(_nsm, node); KeyEncryptors = new List(); var list = Xml.SelectNodes("/d:encryption/d:keyEncryptors/d:keyEncryptor/p:encryptedKey", _nsm); if (list != null) { foreach (XmlNode n in list) { KeyEncryptors.Add(new EncryptionKeyEncryptor(_nsm, n)); } } } } /// /// Handles the EncryptionInfo stream /// internal class EncryptionInfoBinary : EncryptionInfo { internal Flags Flags; internal uint HeaderSize; internal EncryptionHeader Header; internal EncryptionVerifier Verifier; internal override void Read(byte[] data) { Flags = (Flags)BitConverter.ToInt32(data, 4); HeaderSize = (uint)BitConverter.ToInt32(data, 8); /**** EncryptionHeader ****/ Header = new EncryptionHeader(); Header.Flags = (Flags)BitConverter.ToInt32(data, 12); Header.SizeExtra = BitConverter.ToInt32(data, 16); Header.AlgID = (AlgorithmID)BitConverter.ToInt32(data, 20); Header.AlgIDHash = (AlgorithmHashID)BitConverter.ToInt32(data, 24); Header.KeySize = BitConverter.ToInt32(data, 28); Header.ProviderType = (ProviderType)BitConverter.ToInt32(data, 32); Header.Reserved1 = BitConverter.ToInt32(data, 36); Header.Reserved2 = BitConverter.ToInt32(data, 40); byte[] text = new byte[(int)HeaderSize - 34]; Array.Copy(data, 44, text, 0, (int)HeaderSize - 34); Header.CSPName = UTF8Encoding.Unicode.GetString(text); int pos = (int)HeaderSize + 12; /**** EncryptionVerifier ****/ Verifier = new EncryptionVerifier(); Verifier.SaltSize = (uint)BitConverter.ToInt32(data, pos); Verifier.Salt = new byte[Verifier.SaltSize]; Array.Copy(data, pos + 4, Verifier.Salt, 0, Verifier.SaltSize); Verifier.EncryptedVerifier = new byte[16]; Array.Copy(data, pos + 20, Verifier.EncryptedVerifier, 0, 16); Verifier.VerifierHashSize = (uint)BitConverter.ToInt32(data, pos + 36); Verifier.EncryptedVerifierHash = new byte[Verifier.VerifierHashSize]; Array.Copy(data, pos + 40, Verifier.EncryptedVerifierHash, 0, Verifier.VerifierHashSize); } internal byte[] WriteBinary() { MemoryStream ms = new MemoryStream(); BinaryWriter bw = new BinaryWriter(ms); bw.Write(MajorVersion); bw.Write(MinorVersion); bw.Write((int)Flags); byte[] header = Header.WriteBinary(); bw.Write((uint)header.Length); bw.Write(header); bw.Write(Verifier.WriteBinary()); bw.Flush(); return ms.ToArray(); } } }