| /******************************************************************************* |
| * 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(); |
| } |
| |
| } |
| } |