Example #1
0
 /// <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(DomainElement domain, string bodyHash)
 {
     return string.Format(
             CultureInfo.InvariantCulture,
             "DKIM-Signature: v=1; a={0}; s={1}; d={2}; c={3}/{4}; q=dns/txt; h={5}; bh={6}; b=;",
             this.hashAlgorithmDkimCode,
             domain.getSelector(),
             domain.getDomain(),
             this.headerCanonicalization.ToString().ToLower(),
             this.bodyCanonicalization.ToString().ToLower(),
             string.Join(" : ", this.eligibleHeaders.OrderBy(x => x, StringComparer.Ordinal).ToArray()),
             bodyHash);
 }
Example #2
0
        /// <summary>
        /// Button "save" in domain configuration have been click
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btDomainSave_Click(object sender, EventArgs e)
        {
            if (this.epvDomainSelector.GetError(txtDomainName) == "" && this.epvDomainSelector.GetError(txtDomainSelector) == "")
            {
                DomainElement oCurrentDomain;
                bool bAddToList = false;

                if (this.lbxDomains.SelectedItem != null)
                {
                    oCurrentDomain = (DomainElement)this.lbxDomains.SelectedItem;
                }
                else
                {
                    oCurrentDomain = new DomainElement();
                    bAddToList = true;
                }

                oCurrentDomain.Domain = this.txtDomainName.Text;
                oCurrentDomain.Selector = this.txtDomainSelector.Text;
                oCurrentDomain.PrivateKeyFile = this.txtDomainPrivateKeyFilename.Text;

                if (bAddToList)
                {
                    this.oConfig.Domains.Add(oCurrentDomain);
                    this.lbxDomains.Items.Add(oCurrentDomain);
                    this.lbxDomains.SelectedItem = oCurrentDomain;
                }

                if (this.SaveDkimSignerConfig())
                {
                    this.btDomainSave.Enabled = false;
                    this.btDomainDelete.Enabled = true;
                }
            }
            else
            {
                MessageBox.Show("You first need to fix the errors in your domain configuration before saving.", "Config error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
Example #3
0
        /// <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(DomainElement domain, string unsignedDkimHeader, IEnumerable<string> canonicalizedHeaders)
        {
            byte[] signatureBytes;
            string signatureText;
            StringBuilder signedDkimHeader;

            if (domain.CryptoProvider == null)
                throw new Exception("CryptoProvider for domain " + domain.getDomain() + " is null.");

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

                    if (this.headerCanonicalization == DkimCanonicalizationKind.Relaxed)
                    {
                        unsignedDkimHeader = Regex.Replace(unsignedDkimHeader, @" ?: ?", ":");
                        string[] temp = unsignedDkimHeader.Split(new char[] { ':' }, 2);
                        unsignedDkimHeader = temp[0].ToLower() + ":" + temp[1];
                    }

                    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();
        }
Example #4
0
        /// <summary>
        /// Returns a value indicating whether or not the unsigned MIME message in the
        /// given stream can be signed. In this case, we iterate until we see the From:
        /// header, and then we only sign it if our domain matches the domain of the From:
        /// address.
        /// </summary>
        /// <param name="inputStream">The input stream.</param>
        /// <returns>The output stream.</returns>
        public string CanSign(DomainElement domain, Stream inputStream)
        {
            if (this.disposed)
                throw new ObjectDisposedException("Exchange DkimSigner disposed.");

            inputStream.Seek(0, SeekOrigin.Begin);

            // Generate the hash for the body
            var bodyHash = this.GetBodyHash(inputStream);
            var unsignedDkimHeader = this.GetUnsignedDkimHeader(domain, bodyHash);

            // Generate the hash for the header
            var canonicalizedHeaders = this.GetCanonicalizedHeaders(inputStream);
            var signedDkimHeader = this.GetSignedDkimHeader(domain, unsignedDkimHeader, canonicalizedHeaders);

            return signedDkimHeader;
        }
Example #5
0
 /// <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()
        {
            if (RegistryHelper.Open(@"Exchange DkimSigner") != null)
            {
                DkimAlgorithmKind signingAlgorithm = DkimAlgorithmKind.RsaSha1;
                DkimCanonicalizationKind headerCanonicalization = DkimCanonicalizationKind.Simple;
                DkimCanonicalizationKind bodyCanonicalization = DkimCanonicalizationKind.Simple;
                IEnumerable<string> headersToSign = null;

                // Load the log level.
                DkimSigningRoutingAgentFactory.logLevel = 0;
                try
                {
                    string temp = RegistryHelper.Read("LogLevel", @"Exchange DkimSigner");

                    if (temp != null)
                        DkimSigningRoutingAgentFactory.logLevel = Convert.ToInt32(temp);
                }
                catch (FormatException) { }
                catch (OverflowException) { }

                if (logLevel == 0)
                    throw new ConfigurationErrorsException(Resources.DkimSigningRoutingAgentFactory_BadLogLevel);

                // Load the signing algorithm.
                try
                {
                    signingAlgorithm = (DkimAlgorithmKind)Enum.Parse(typeof(DkimAlgorithmKind), RegistryHelper.Read("Algorithm", @"Exchange DkimSigner\DKIM"), true);
                }
                catch (Exception ex)
                {
                    throw new ConfigurationErrorsException(Resources.DkimSigningRoutingAgentFactory_BadDkimAlgorithmConfig, ex);
                }

                // Load the header canonicalization algorithm.
                try
                {
                    headerCanonicalization = (DkimCanonicalizationKind)Enum.Parse(typeof(DkimCanonicalizationKind), RegistryHelper.Read("HeaderCanonicalization", @"Exchange DkimSigner\DKIM"), true);
                }
                catch (Exception ex)
                {
                    throw new ConfigurationErrorsException(Resources.DkimSigningRoutingAgentFactory_BadDkimCanonicalizationHeaderConfig, ex);
                }

                // Load the body canonicalization algorithm.
                try
                {
                    bodyCanonicalization = (DkimCanonicalizationKind)Enum.Parse(typeof(DkimCanonicalizationKind), RegistryHelper.Read("BodyCanonicalization", @"Exchange DkimSigner\DKIM"), true);
                }
                catch (Exception ex)
                {
                    throw new ConfigurationErrorsException(Resources.DkimSigningRoutingAgentFactory_BadDkimCanonicalizationBodyConfig, ex);
                }

                // Load the list of headers to sign in each message.
                string unparsedHeaders = RegistryHelper.Read("HeadersToSign", @"Exchange DkimSigner\DKIM");
                if (unparsedHeaders != null)
                {
                    headersToSign = unparsedHeaders.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                }

                // Load the list of domains
                domainSettings = new List<DomainElement>();
                string[] domainNames = RegistryHelper.GetSubKeyName(@"Exchange DkimSigner\Domain");
                if (domainNames != null)
                {
                    foreach (string domainName in domainNames)
                    {
                        string selector = RegistryHelper.Read("Selector", @"Exchange DkimSigner\Domain\" + domainName);
                        string privateKeyFile = RegistryHelper.Read("PrivateKeyFile", @"Exchange DkimSigner\Domain\" + domainName);

                        DomainElement domainElement = new DomainElement(domainName,
                                                                selector,
                                                                privateKeyFile);

                        if (domainElement.initElement(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)))
                        {
                            domainSettings.Add(domainElement);
                        }
                    }
                }

                this.dkimSigner = new DkimSigner(   signingAlgorithm,
                                                    headerCanonicalization,
                                                    bodyCanonicalization,
                                                    headersToSign);

                Logger.LogInformation("Exchange DKIM started. Signing Algorithm: " + signingAlgorithm.ToString() + ", Canonicalization Header Algorithm: " + headerCanonicalization.ToString() + ", Canonicalization Header Algorithm: " + bodyCanonicalization.ToString() + ", Number of domains: " + domainSettings.Count);
            }
            else
            {
                throw new ConfigurationErrorsException(Resources.DkimSigningRoutingAgentFactory_BadDkimConfig);
            }
        }
Example #7
0
        /// <summary>
        /// Returns a value indicating whether or not the unsigned MIME message in the
        /// given stream can be signed. In this case, we iterate until we see the From:
        /// header, and then we only sign it if our domain matches the domain of the From:
        /// address.
        /// </summary>
        /// <param name="inputStream">The input stream.</param>
        /// <returns>The output stream.</returns>
        public string CanSign(DomainElement domain, Stream inputStream)
        {
            if (this.disposed)
            {
                throw new ObjectDisposedException("Exchange DkimSigner disposed.");
            }

            inputStream.Seek(0, SeekOrigin.Begin);

            // Generate the hash for the body
            Logger.LogDebug("Creating body hash");
            string bodyHash = this.GetBodyHash(inputStream);
            Logger.LogDebug("Got body hash: " + bodyHash);
            string unsignedDkimHeader = this.GetUnsignedDkimHeader(domain, bodyHash);

            // Generate the hash for the header
            Logger.LogDebug("Creating signing header");
            IEnumerable<string> canonicalizedHeaders = this.GetCanonicalizedHeaders(inputStream);
            string signedDkimHeader = this.GetSignedDkimHeader(domain, unsignedDkimHeader, canonicalizedHeaders);
            Logger.LogDebug("Got signing header: " + signedDkimHeader);

            return signedDkimHeader;
        }