/// <summary>
        /// Gets the version of the DKIM-Signature header with the signature appended, along with
        /// the CRLF.
        /// </summary>
        /// <param name="unsignedDkimHeader">The unsigned DKIM header, to use as a template.</param>
        /// <param name="canonicalizedHeaders">The headers to be included as part of the signature.</param>
        /// <returns>The signed DKIM-Signature header.</returns>
        private string GetSignedDkimHeader(
            string unsignedDkimHeader, 
            IEnumerable<string> canonicalizedHeaders,
            DomainElement domain
            )
        {
            byte[] signatureBytes;
            string signatureText;
            StringBuilder signedDkimHeader;

            using (var stream = new MemoryStream())
            {
                using (var writer = new StreamWriter(stream))
                {
                    foreach (var canonicalizedHeader in canonicalizedHeaders)
                    {
                        writer.Write(canonicalizedHeader);
                    }

                    writer.Write(unsignedDkimHeader);
                    writer.Flush();

                    stream.Seek(0, SeekOrigin.Begin);

                    // Why not pass this.hashAlgorithm here, since we already have it? If we're supporting
                    // Exchange 2007, then we're stuck on CLR 2.0. The SHA-256 functionality was added in
                    // .NET 3.5 SP1, but it was done in such a way that the switch statement used internally
                    // by the Crypto .NET classes won't recognize the new SHA256CryptoServiceProvider type.
                    // So, we have to use the string method instead. More details available at
                    // http://blogs.msdn.com/b/shawnfa/archive/2008/08/25/using-rsacryptoserviceprovider-for-rsa-sha256-signatures.aspx
                    signatureBytes = domain.CryptoProvider.SignData(stream, this.hashAlgorithmCryptoCode);
                }
            }

            signatureText = Convert.ToBase64String(signatureBytes);
            signedDkimHeader = new StringBuilder(unsignedDkimHeader.Substring(0, unsignedDkimHeader.Length - 1));

            signedDkimHeader.Append(signatureText);
            signedDkimHeader.Append(";\r\n");

            return signedDkimHeader.ToString();
        }
 /// <summary>
 /// Builds an unsigned DKIM-Signature header. Note that the returned
 /// header will NOT have a CRLF at the end.
 /// </summary>
 /// <param name="bodyHash">The hash of the body.</param>
 /// <returns>The unsigned DKIM-Signature header.</returns>
 private string GetUnsignedDkimHeader(string bodyHash, DomainElement domain)
 {
     return string.Format(
         CultureInfo.InvariantCulture,
         "DKIM-Signature: v=1; a={0}; s={1}; d={2}; c=simple/simple; q=dns/txt; h={3}; bh={4}; b=;",
         this.hashAlgorithmDkimCode,
         domain.Selector,
         domain.Domain,
         string.Join(" : ", this.eligibleHeaders.OrderBy(x => x, StringComparer.Ordinal).ToArray()),
         bodyHash);
 }
        /// <summary>
        /// Initializes various settings based on configuration.
        /// </summary>
        private void Initialize()
        {
            // Load the signing algorithm.
            try
            {
                this.algorithm = (DkimAlgorithmKind)Enum.Parse(
                    typeof(DkimAlgorithmKind),
                    Properties.Settings.Default.Algorithm,
                    true);
            }
            catch (Exception ex)
            {
                throw new ConfigurationErrorsException(
                    Resources.DkimSigningRoutingAgentFactory_BadAlgorithmConfig,
                    ex);
            }

            // Load the list of headers to sign in each message.
            var unparsedHeaders = Properties.Settings.Default.HeadersToSign;
            if (unparsedHeaders != null)
            {
                this.headersToSign = unparsedHeaders
                    .Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
            }

            try
            {
                XmlDocument xmlDoc = new XmlDocument(); //* create an xml document object.
                xmlDoc.Load(this.GetType().Assembly.Location + ".config"); //* load the XML document from the specified file.
                XmlNodeList domainInfoList = xmlDoc.GetElementsByTagName("domainInfo");
                if (domainInfoList == null || domainInfoList.Count != 1)
                {
                    Logger.LogError("There is an error in your configuration file. domainInfo couldn't be initialized properly.");
                    return;
                }
                XmlNode domainInfo = domainInfoList.Item(0);

                domainSettings = new List<DomainElement>();

                foreach (XmlNode n in domainInfo.ChildNodes)
                {
                    DomainElement e = new DomainElement();
                    e.Domain = n.Attributes["Domain"].Value;
                    e.Selector = n.Attributes["Selector"].Value;
                    e.PrivateKeyFile = n.Attributes["PrivateKeyFile"].Value;
                    if (e.initElement(Path.GetDirectoryName(this.GetType().Assembly.Location)))
                        domainSettings.Add(e);
                }
                if (domainSettings.Count == 0)
                {
                    Logger.LogWarning("No domain configuration found. DKIM will do nothing.");
                }
            }
            catch (Exception e)
            {
                Logger.LogError("Couldn't load config: " + e.ToString());
            }
            Logger.LogInformation("Exchange DKIM started. Algorithm: " + algorithm.ToString() + " Number of domains: " + domainSettings.Count);
        }