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