| /******************************************************************************* | |
| * 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 | |
| { | |
| /// <summary> | |
| /// AES. MUST conform to the AES algorithm. | |
| /// </summary> | |
| AES, | |
| /// <summary> | |
| /// RC2. MUST conform to [RFC2268]. | |
| /// </summary> | |
| RC2, | |
| /// <summary> | |
| /// RC4. | |
| /// </summary> | |
| RC4, | |
| /// <summary> | |
| /// MUST conform to the DES algorithm. | |
| /// </summary> | |
| DES, | |
| /// <summary> | |
| /// MUST conform to the [DRAFT-DESX] algorithm. | |
| /// </summary> | |
| DESX, | |
| /// <summary> | |
| /// 3DES. MUST conform to the [RFC1851] algorithm. | |
| /// </summary> | |
| TRIPLE_DES, | |
| /// 3DES_112 MUST conform to the [RFC1851] algorithm. | |
| TRIPLE_DES_112 | |
| } | |
| internal enum eChainingMode | |
| { | |
| /// <summary> | |
| /// Cipher block chaining (CBC). | |
| /// </summary> | |
| ChainingModeCBC, | |
| /// <summary> | |
| /// Cipher feedback chaining (CFB), with 8-bit window. | |
| /// </summary> | |
| ChainingModeCFB | |
| } | |
| /// <summary> | |
| /// Hashalgorithm | |
| /// </summary> | |
| internal enum eHashAlogorithm | |
| { | |
| /// <summary> | |
| /// Sha 1-MUST conform to [RFC4634] | |
| /// </summary> | |
| SHA1, | |
| /// <summary> | |
| /// Sha 256-MUST conform to [RFC4634] | |
| /// </summary> | |
| SHA256, | |
| /// <summary> | |
| /// Sha 384-MUST conform to [RFC4634] | |
| /// </summary> | |
| SHA384, | |
| /// <summary> | |
| /// Sha 512-MUST conform to [RFC4634] | |
| /// </summary> | |
| SHA512, | |
| /// <summary> | |
| /// MD5 | |
| /// </summary> | |
| MD5, | |
| /// <summary> | |
| /// MD4 | |
| /// </summary> | |
| MD4, | |
| /// <summary> | |
| /// MD2 | |
| /// </summary> | |
| MD2, | |
| /// <summary> | |
| /// RIPEMD-128 MUST conform to [ISO/IEC 10118] | |
| /// </summary> | |
| RIPEMD128, | |
| /// <summary> | |
| /// RIPEMD-160 MUST conform to [ISO/IEC 10118] | |
| /// </summary> | |
| RIPEMD160, | |
| /// <summary> | |
| /// WHIRLPOOL MUST conform to [ISO/IEC 10118] | |
| /// </summary> | |
| WHIRLPOOL | |
| } | |
| /// <summary> | |
| /// Handels the agile encryption | |
| /// </summary> | |
| 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()); | |
| } | |
| } | |
| } | |
| /* | |
| <?xml version="1.0" encoding="UTF-8" standalone="yes"?> | |
| <encryption xmlns="http://schemas.microsoft.com/office/2006/encryption" xmlns:p="http://schemas.microsoft.com/office/2006/keyEncryptor/password" xmlns:c="http://schemas.microsoft.com/office/2006/keyEncryptor/certificate"> | |
| <keyData saltSize="16" blockSize="16" keyBits="256" hashSize="64" cipherAlgorithm="AES" cipherChaining="ChainingModeCBC" hashAlgorithm="SHA512" saltValue="pa+hrJ3s1zrY6hmVuSa5JQ==" /> | |
| <dataIntegrity encryptedHmacKey="nd8i4sEKjsMjVN2gLo91oFN2e7bhMpWKDCAUBEpz4GW6NcE3hBXDobLksZvQGwLrPj0SUVzQA8VuDMyjMAfVCA==" encryptedHmacValue="O6oegHpQVz2uO7Om4oZijSi4kzLiiMZGIjfZlq/EFFO6PZbKitenBqe2or1REaxaI7gO/JmtJzZ1ViucqTaw4g==" /> | |
| <keyEncryptors> | |
| <keyEncryptor uri="http://schemas.microsoft.com/office/2006/keyEncryptor/password"> | |
| <p:encryptedKey spinCount="100000" saltSize="16" blockSize="16" keyBits="256" hashSize="64" cipherAlgorithm="AES" cipherChaining="ChainingModeCBC" hashAlgorithm="SHA512" saltValue="u2BNFAuHYn3M/WRja3/uPg==" encryptedVerifierHashInput="M0V+fRolJMRgFyI9w+AVxQ==" encryptedVerifierHashValue="V/6l9pFH7AaXFqEbsnFBfHe7gMOqFeRwaNMjc7D3LNdw6KgZzOOQlt5sE8/oG7GPVBDGfoQMTxjQydVPVy4qng==" encryptedKeyValue="B0/rbSQRiIKG5CQDH6AKYSybdXzxgKAfX1f+S5k7mNE=" /> | |
| </keyEncryptor></keyEncryptors></encryption> | |
| */ | |
| /*** | |
| * <?xml version="1.0" encoding="UTF-8" standalone="true"?> | |
| <encryption xmlns:c="http://schemas.microsoft.com/office/2006/keyEncryptor/certificate" xmlns:p="http://schemas.microsoft.com/office/2006/keyEncryptor/password" xmlns="http://schemas.microsoft.com/office/2006/encryption"> | |
| * <keyData saltValue="XmTB/XBGJSbwd/GTKzQv5A==" hashAlgorithm="SHA512" cipherChaining="ChainingModeCBC" cipherAlgorithm="AES" hashSize="64" keyBits="256" blockSize="16" saltSize="16"/> | |
| * <dataIntegrity encryptedHmacValue="WWw3Bb2dbcNPMnl9f1o7rO0u7sclWGKTXqBA6rRzKsP2KzWS5T0LxY9qFoC6QE67t/t+FNNtMDdMtE3D1xvT8w==" encryptedHmacKey="p/dVdlJY5Kj0k3jI1HRjqtk4s0Y4HmDAsc8nqZgfxNS7DopAsS3LU/2p3CYoIRObHsnHTAtbueH08DFCYGZURg=="/> | |
| * <keyEncryptors> | |
| * <keyEncryptor uri="http://schemas.microsoft.com/office/2006/keyEncryptor/password"> | |
| * <p:encryptedKey saltValue="EeBtY0QftyOkLztCl7NF0g==" hashAlgorithm="SHA512" cipherChaining="ChainingModeCBC" cipherAlgorithm="AES" hashSize="64" keyBits="256" blockSize="16" saltSize="16" encryptedKeyValue="Z7AO8vHnnPZEb1VqyZLJ6JFc3Mq3E322XPxWXS21fbU=" encryptedVerifierHashValue="G7BxbKnZanldvtsbu51mP9J3f9Wr5vCfCpvWSh5eIJff7Sr3J2DzH1/9aKj9uIpqFQIsLohpRk+oBYDcX7hRgw==" encryptedVerifierHashInput="851eszl5y5rdU1RnTjEWHw==" spinCount="100000"/> | |
| * </keyEncryptor> | |
| * </keyEncryptors> | |
| * </encryption | |
| * ***/ | |
| internal EncryptionDataIntegrity DataIntegrity { get; set; } | |
| internal EncryptionKeyData KeyData { get; set; } | |
| internal List<EncryptionKeyEncryptor> 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<EncryptionKeyEncryptor>(); | |
| 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)); | |
| } | |
| } | |
| } | |
| } | |
| /// <summary> | |
| /// Handles the EncryptionInfo stream | |
| /// </summary> | |
| 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(); | |
| } | |
| } | |
| } |