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