/// <summary> /// Digitally sign the message using a DomainKeys Identified Mail (DKIM) signature. /// </summary> /// <remarks> /// Digitally signs the message using a DomainKeys Identified Mail (DKIM) signature. /// </remarks> /// <param name="signer">The DKIM signer.</param> /// <param name="headers">The headers to sign.</param> /// <param name="headerCanonicalizationAlgorithm">The header canonicalization algorithm.</param> /// <param name="bodyCanonicalizationAlgorithm">The body canonicalization algorithm.</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="signer"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="headers"/> is <c>null</c>.</para> /// </exception> /// <exception cref="System.ArgumentException"> /// <para><paramref name="headers"/> does not contain the 'From' header.</para> /// <para>-or-</para> /// <para><paramref name="headers"/> contains one or more of the following headers: Return-Path, /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature.</para> /// </exception> public void Sign (DkimSigner signer, IList<HeaderId> headers, DkimCanonicalizationAlgorithm headerCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple, DkimCanonicalizationAlgorithm bodyCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple) { Sign (FormatOptions.Default, signer, headers, headerCanonicalizationAlgorithm, bodyCanonicalizationAlgorithm); }
public void UpdateSettings(Settings config) { lock (settingsMutex) { // Load the list of domains domains.Clear(); DkimSignatureAlgorithm signatureAlgorithm; switch (config.SigningAlgorithm) { case DkimAlgorithmKind.RsaSha1: signatureAlgorithm = DkimSignatureAlgorithm.RsaSha1; break; case DkimAlgorithmKind.RsaSha256: signatureAlgorithm = DkimSignatureAlgorithm.RsaSha256; break; default: // ReSharper disable once NotResolvedInText throw new ArgumentOutOfRangeException("config.SigningAlgorithm"); } foreach (DomainElement domainElement in config.Domains) { string privateKey = domainElement.PrivateKeyPathAbsolute( Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)); if (String.IsNullOrEmpty(privateKey) || !File.Exists(privateKey)) { Logger.LogError("The private key for domain " + domainElement.Domain + " wasn't found: " + privateKey + ". Ignoring domain."); } //check if the private key can be parsed try { KeyHelper.ParseKeyPair(privateKey); } catch (Exception ex) { Logger.LogError("Couldn't load private key for domain " + domainElement.Domain + ": " + ex.Message); continue; } MimeKit.Cryptography.DkimSigner signer; try { AsymmetricKeyParameter key = KeyHelper.ParsePrivateKey(privateKey); signer = new MimeKit.Cryptography.DkimSigner(key, domainElement.Domain, domainElement.Selector) { SignatureAlgorithm = signatureAlgorithm }; } catch (Exception ex) { Logger.LogError("Could not initialize MimeKit DkimSigner for domain " + domainElement.Domain + ": " + ex.Message); continue; } domains.Add(domainElement.Domain, new DomainElementSigner(domainElement, signer)); } headerCanonicalization = config.HeaderCanonicalization == DkimCanonicalizationKind.Relaxed ? DkimCanonicalizationAlgorithm.Relaxed : DkimCanonicalizationAlgorithm.Simple; bodyCanonicalization = config.BodyCanonicalization == DkimCanonicalizationKind.Relaxed ? DkimCanonicalizationAlgorithm.Relaxed : DkimCanonicalizationAlgorithm.Simple; List <HeaderId> headerList = new List <HeaderId>(); foreach (string headerToSign in config.HeadersToSign) { HeaderId headerId; #if EX_2007_SP3 || EX_2010 || EX_2010_SP1 || EX_2010_SP2 || EX_2010_SP3 if (!TryParseHeader(headerToSign, out headerId) || (headerId == HeaderId.Unknown)) #else if (!Enum.TryParse(headerToSign, true, out headerId) || (headerId == HeaderId.Unknown)) #endif { Logger.LogWarning("Invalid value for header to sign: '" + headerToSign + "'. This header will be ignored."); } headerList.Add(headerId); } // The From header must always be signed according to the DKIM specification. if (!headerList.Contains(HeaderId.From)) { headerList.Add(HeaderId.From); } eligibleHeaders = headerList.ToArray(); } }
/// <summary> /// Digitally sign the message using a DomainKeys Identified Mail (DKIM) signature. /// </summary> /// <remarks> /// Digitally signs the message using a DomainKeys Identified Mail (DKIM) signature. /// </remarks> /// <param name="options">The formatting options.</param> /// <param name="signer">The DKIM signer.</param> /// <param name="headers">The list of header fields to sign.</param> /// <param name="headerCanonicalizationAlgorithm">The header canonicalization algorithm.</param> /// <param name="bodyCanonicalizationAlgorithm">The body canonicalization algorithm.</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="options"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="signer"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="headers"/> is <c>null</c>.</para> /// </exception> /// <exception cref="System.ArgumentException"> /// <para><paramref name="headers"/> does not contain the 'From' header.</para> /// <para>-or-</para> /// <para><paramref name="headers"/> contains one or more of the following headers: Return-Path, /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature.</para> /// </exception> void Sign (FormatOptions options, DkimSigner signer, IList<HeaderId> headers, DkimCanonicalizationAlgorithm headerCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple, DkimCanonicalizationAlgorithm bodyCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple) { if (options == null) throw new ArgumentNullException ("options"); if (signer == null) throw new ArgumentNullException ("signer"); if (headers == null) throw new ArgumentNullException ("headers"); if (!headers.Contains (HeaderId.From)) throw new ArgumentException ("The list of headers to sign MUST include the 'From' header."); var fields = new string[headers.Count]; for (int i = 0; i < headers.Count; i++) { if (DkimShouldNotInclude.Contains (headers[i])) throw new ArgumentException (string.Format ("The list of headers to sign SHOULD NOT include the '{0}' header.", headers[i].ToHeaderName ())); fields[i] = headers[i].ToHeaderName ().ToLowerInvariant (); } if (version == null && Body != null && Body.Headers.Count > 0) MimeVersion = new Version (1, 0); Prepare (EncodingConstraint.SevenBit, 78); var t = DateTime.Now - DateUtils.UnixEpoch; var value = new StringBuilder ("v=1"); byte[] signature, hash; Header dkim; options = options.Clone (); options.NewLineFormat = NewLineFormat.Dos; switch (signer.SignatureAlgorithm) { case DkimSignatureAlgorithm.RsaSha256: value.Append ("; a=rsa-sha256"); break; default: value.Append ("; a=rsa-sha1"); break; } value.AppendFormat ("; d={0}; s={1}", signer.Domain, signer.Selector); value.AppendFormat ("; c={0}/{1}", headerCanonicalizationAlgorithm.ToString ().ToLowerInvariant (), bodyCanonicalizationAlgorithm.ToString ().ToLowerInvariant ()); if (!string.IsNullOrEmpty (signer.QueryMethod)) value.AppendFormat ("; q={0}", signer.QueryMethod); if (!string.IsNullOrEmpty (signer.AgentOrUserIdentifier)) value.AppendFormat ("; i={0}", signer.AgentOrUserIdentifier); value.AppendFormat ("; t={0}", (long) t.TotalSeconds); using (var stream = new DkimSignatureStream (DkimGetDigestSigner (signer.SignatureAlgorithm, signer.PrivateKey))) { using (var filtered = new FilteredStream (stream)) { filtered.Add (options.CreateNewLineFilter ()); // write the specified message headers DkimWriteHeaders (options, fields, headerCanonicalizationAlgorithm, filtered); value.AppendFormat ("; h={0}", string.Join (":", fields.ToArray ())); hash = DkimHashBody (options, signer.SignatureAlgorithm, bodyCanonicalizationAlgorithm, -1); value.AppendFormat ("; bh={0}", Convert.ToBase64String (hash)); value.Append ("; b="); dkim = new Header (HeaderId.DkimSignature, value.ToString ()); Headers.Insert (0, dkim); switch (headerCanonicalizationAlgorithm) { case DkimCanonicalizationAlgorithm.Relaxed: DkimWriteHeaderRelaxed (options, filtered, dkim); break; default: DkimWriteHeaderSimple (options, filtered, dkim); break; } filtered.Flush (); } signature = stream.GenerateSignature (); dkim.Value += Convert.ToBase64String (signature); } }
public void UpdateSettings(Settings config) { lock (settingsMutex) { // Load the list of domains domains.Clear(); DkimSignatureAlgorithm signatureAlgorithm; switch (config.SigningAlgorithm) { case DkimAlgorithmKind.RsaSha1: signatureAlgorithm = DkimSignatureAlgorithm.RsaSha1; break; case DkimAlgorithmKind.RsaSha256: signatureAlgorithm = DkimSignatureAlgorithm.RsaSha256; break; default: // ReSharper disable once NotResolvedInText throw new ArgumentOutOfRangeException("config.SigningAlgorithm"); } foreach (DomainElement domainElement in config.Domains) { string privateKey = domainElement.PrivateKeyPathAbsolute( Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)); if (String.IsNullOrEmpty(privateKey) || !File.Exists(privateKey)) { Logger.LogError("The private key for domain " + domainElement.Domain + " wasn't found: " + privateKey + ". Ignoring domain."); } //check if the private key can be parsed try { KeyHelper.ParseKeyPair(privateKey); } catch (Exception ex) { Logger.LogError("Couldn't load private key for domain " + domainElement.Domain + ": " + ex.Message); continue; } MimeKit.Cryptography.DkimSigner signer; try { AsymmetricKeyParameter key = KeyHelper.ParsePrivateKey(privateKey); signer = new MimeKit.Cryptography.DkimSigner(key, domainElement.Domain, domainElement.Selector) { SignatureAlgorithm = signatureAlgorithm }; } catch (Exception ex) { Logger.LogError("Could not initialize MimeKit DkimSigner for domain " + domainElement.Domain + ": " + ex.Message); continue; } domains.Add(domainElement.Domain, new DomainElementSigner(domainElement, signer)); } headerCanonicalization = config.HeaderCanonicalization == DkimCanonicalizationKind.Relaxed ? DkimCanonicalizationAlgorithm.Relaxed : DkimCanonicalizationAlgorithm.Simple; bodyCanonicalization = config.BodyCanonicalization == DkimCanonicalizationKind.Relaxed ? DkimCanonicalizationAlgorithm.Relaxed : DkimCanonicalizationAlgorithm.Simple; List<HeaderId> headerList = new List<HeaderId>(); foreach (string headerToSign in config.HeadersToSign) { HeaderId headerId; #if EX_2007_SP3 || EX_2010 || EX_2010_SP1 || EX_2010_SP2 || EX_2010_SP3 if (!TryParseHeader(headerToSign, out headerId)) #else if (!Enum.TryParse(headerToSign, true, out headerId)) #endif { Logger.LogWarning("Invalid value for header to sign: '" + headerToSign + "'. This header will be ignored."); } headerList.Add(headerId); } // The From header must always be signed according to the // DKIM specification. if (!headerList.Contains(HeaderId.From)) { headerList.Add(HeaderId.From); } eligibleHeaders = headerList.ToArray(); } }