static DkimSigner CreateSigner (DkimSignatureAlgorithm algorithm) { return new DkimSigner (DkimKeys.Private, "example.com", "1433868189.example") { SignatureAlgorithm = algorithm, AgentOrUserIdentifier = "@eng.example.com", QueryMethod = "dns/txt", }; }
static DkimSigner CreateSigner (DkimSignatureAlgorithm algorithm) { return new DkimSigner (Path.Combine ("..", "..", "TestData", "dkim", "example.pem"), "example.com", "1433868189.example") { SignatureAlgorithm = algorithm, AgentOrUserIdentifier = "@eng.example.com", QueryMethod = "dns/txt", }; }
/// <summary> /// Initializes a new instance of the <see cref="MimeKit.Cryptography.DkimHashStream"/> class. /// </summary> /// <remarks> /// Creates a new <see cref="DkimHashStream"/>. /// </remarks> /// <param name="algorithm">The signature algorithm.</param> /// <param name="maxLength">The max length of data to hash.</param> public DkimHashStream (DkimSignatureAlgorithm algorithm, int maxLength = -1) { switch (algorithm) { case DkimSignatureAlgorithm.RsaSha256: digest = new Sha256Digest (); break; default: digest = new Sha1Digest (); break; } max = maxLength; }
static void VerifyDkimBodyHash (MimeMessage message, DkimSignatureAlgorithm algorithm, string expectedHash) { var value = message.Headers[HeaderId.DkimSignature]; var parameters = value.Split (';'); string hash = null; for (int i = 0; i < parameters.Length; i++) { var param = parameters[i].Trim (); if (param.StartsWith ("bh=", StringComparison.Ordinal)) { hash = param.Substring (3); break; } } Assert.AreEqual (expectedHash, hash, "The {0} hash does not match the expected value.", algorithm.ToString ().ToUpperInvariant ().Substring (3)); }
/// <summary> /// Verify the signature of the message headers. /// </summary> /// <remarks> /// Verifies the signature of the message headers. /// </remarks> /// <param name="options">The formatting options.</param> /// <param name="message">The signed MIME message.</param> /// <param name="dkimSignature">The DKIM-Signature or ARC-Message-Signature header.</param> /// <param name="signatureAlgorithm">The algorithm used to sign the message headers.</param> /// <param name="key">The public key used to verify the signature.</param> /// <param name="headers">The list of headers that were signed.</param> /// <param name="canonicalizationAlgorithm">The algorithm used to canonicalize the headers.</param> /// <param name="signature">The expected signature of the headers encoded in base64.</param> /// <returns><c>true</c> if the calculated signature matches <paramref name="signature"/>; otherwise, <c>false</c>.</returns> protected bool VerifySignature(FormatOptions options, MimeMessage message, Header dkimSignature, DkimSignatureAlgorithm signatureAlgorithm, AsymmetricKeyParameter key, string[] headers, DkimCanonicalizationAlgorithm canonicalizationAlgorithm, string signature) { using (var stream = new DkimSignatureStream(CreateVerifyContext(signatureAlgorithm, key))) { using (var filtered = new FilteredStream(stream)) { filtered.Add(options.CreateNewLineFilter()); WriteHeaders(options, message, headers, canonicalizationAlgorithm, filtered); // now include the DKIM-Signature header that we are verifying, // but only after removing the "b=" signature value. var header = GetSignedSignatureHeader(dkimSignature); switch (canonicalizationAlgorithm) { case DkimCanonicalizationAlgorithm.Relaxed: WriteHeaderRelaxed(options, filtered, header, true); break; default: WriteHeaderSimple(options, filtered, header, true); break; } filtered.Flush(); } return(stream.VerifySignature(signature)); } }
public ExampleArcSigner(Stream stream, string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) : base(stream, domain, selector, algorithm) { }
byte[] DkimHashBody (FormatOptions options, DkimSignatureAlgorithm signatureAlgorithm, DkimCanonicalizationAlgorithm bodyCanonicalizationAlgorithm, int maxLength) { using (var stream = new DkimHashStream (signatureAlgorithm, maxLength)) { using (var filtered = new FilteredStream (stream)) { filtered.Add (options.CreateNewLineFilter ()); if (bodyCanonicalizationAlgorithm == DkimCanonicalizationAlgorithm.Relaxed) filtered.Add (new DkimRelaxedBodyFilter ()); else filtered.Add (new DkimSimpleBodyFilter ()); if (Body != null) { try { Body.Headers.Suppress = true; Body.WriteTo (options, stream, CancellationToken.None); } finally { Body.Headers.Suppress = false; } } filtered.Flush (); } return stream.GenerateHash (); } }
static void TestUnicode (DkimSignatureAlgorithm signatureAlgorithm, DkimCanonicalizationAlgorithm bodyAlgorithm, string expectedHash) { var headers = new [] { HeaderId.From, HeaderId.To, HeaderId.Subject, HeaderId.Date }; var signer = CreateSigner (signatureAlgorithm); var message = new MimeMessage (); message.From.Add (new MailboxAddress ("", "*****@*****.**")); message.To.Add (new MailboxAddress ("", "*****@*****.**")); message.Subject = "This is a unicode message"; message.Date = DateTimeOffset.Now; var builder = new BodyBuilder (); builder.TextBody = " تست "; builder.HtmlBody = " <div> تست </div> "; message.Body = builder.ToMessageBody (); ((Multipart) message.Body).Boundary = "=-MultipartAlternativeBoundary"; ((Multipart) message.Body)[1].ContentId = null; message.Body.Prepare (EncodingConstraint.EightBit); message.Sign (signer, headers, DkimCanonicalizationAlgorithm.Simple, bodyAlgorithm); var dkim = message.Headers[0]; Console.WriteLine ("{0}", dkim.Value); VerifyDkimBodyHash (message, signatureAlgorithm, expectedHash); Assert.IsTrue (message.Verify (dkim, new DummyPublicKeyLocator (DkimKeys.Public)), "Failed to verify DKIM-Signature."); }
static void ValidateDkimSignatureParameters (IDictionary<string, string> parameters, out DkimSignatureAlgorithm algorithm, out DkimCanonicalizationAlgorithm headerAlgorithm, out DkimCanonicalizationAlgorithm bodyAlgorithm, out string d, out string s, out string q, out string[] headers, out string bh, out string b, out int maxLength) { bool containsFrom = false; string v, a, c, h, l, id; if (!parameters.TryGetValue ("v", out v)) throw new FormatException ("Malformed DKIM-Signature header: no version parameter detected."); if (v != "1") throw new FormatException (string.Format ("Unrecognized DKIM-Signature version: v={0}", v)); if (!parameters.TryGetValue ("a", out a)) throw new FormatException ("Malformed DKIM-Signature header: no signature algorithm parameter detected."); switch (a.ToLowerInvariant ()) { case "rsa-sha256": algorithm = DkimSignatureAlgorithm.RsaSha256; break; case "rsa-sha1": algorithm = DkimSignatureAlgorithm.RsaSha1; break; default: throw new FormatException (string.Format ("Unrecognized DKIM-Signature algorithm parameter: a={0}", a)); } if (!parameters.TryGetValue ("d", out d)) throw new FormatException ("Malformed DKIM-Signature header: no domain parameter detected."); if (parameters.TryGetValue ("i", out id)) { string ident; int at; if ((at = id.LastIndexOf ('@')) == -1) throw new FormatException ("Malformed DKIM-Signature header: no @ in the AUID value."); ident = id.Substring (at + 1); if (!ident.Equals (d, StringComparison.OrdinalIgnoreCase) && !ident.EndsWith ("." + d, StringComparison.OrdinalIgnoreCase)) throw new FormatException ("Invalid DKIM-Signature header: the domain in the AUID does not match the domain parameter."); } if (!parameters.TryGetValue ("s", out s)) throw new FormatException ("Malformed DKIM-Signature header: no selector parameter detected."); if (!parameters.TryGetValue ("q", out q)) q = "dns/txt"; if (parameters.TryGetValue ("l", out l)) { if (!int.TryParse (l, out maxLength)) throw new FormatException (string.Format ("Malformed DKIM-Signature header: invalid length parameter: l={0}", l)); } else { maxLength = -1; } if (parameters.TryGetValue ("c", out c)) { var tokens = c.ToLowerInvariant ().Split ('/'); if (tokens.Length == 0 || tokens.Length > 2) throw new FormatException (string.Format ("Malformed DKIM-Signature header: invalid canonicalization parameter: c={0}", c)); switch (tokens[0]) { case "relaxed": headerAlgorithm = DkimCanonicalizationAlgorithm.Relaxed; break; case "simple": headerAlgorithm = DkimCanonicalizationAlgorithm.Simple; break; default: throw new FormatException (string.Format ("Malformed DKIM-Signature header: invalid canonicalization parameter: c={0}", c)); } if (tokens.Length == 2) { switch (tokens[1]) { case "relaxed": bodyAlgorithm = DkimCanonicalizationAlgorithm.Relaxed; break; case "simple": bodyAlgorithm = DkimCanonicalizationAlgorithm.Simple; break; default: throw new FormatException (string.Format ("Malformed DKIM-Signature header: invalid canonicalization parameter: c={0}", c)); } } else { bodyAlgorithm = DkimCanonicalizationAlgorithm.Simple; } } else { headerAlgorithm = DkimCanonicalizationAlgorithm.Simple; bodyAlgorithm = DkimCanonicalizationAlgorithm.Simple; } if (!parameters.TryGetValue ("h", out h)) throw new FormatException ("Malformed DKIM-Signature header: no signed header parameter detected."); headers = h.Split (':'); for (int i = 0; i < headers.Length; i++) { if (headers[i].Equals ("from", StringComparison.OrdinalIgnoreCase)) { containsFrom = true; break; } } if (!containsFrom) throw new FormatException (string.Format ("Malformed DKIM-Signature header: From header not signed.")); if (!parameters.TryGetValue ("bh", out bh)) throw new FormatException ("Malformed DKIM-Signature header: no body hash parameter detected."); if (!parameters.TryGetValue ("b", out b)) throw new FormatException ("Malformed DKIM-Signature header: no signature parameter detected."); }
/// <summary> /// Initialize a new instance of the <see cref="ArcSigner"/> class. /// </summary> /// <remarks> /// Creates a new <see cref="ArcSigner"/>. /// </remarks> /// <param name="domain">The domain that the signer represents.</param> /// <param name="selector">The selector subdividing the domain.</param> /// <param name="algorithm">The signature algorithm.</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="domain"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="selector"/> is <c>null</c>.</para> /// </exception> protected ArcSigner(string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) : base(domain, selector, algorithm) { }
/// <summary> /// Initializes a new instance of the <see cref="MimeKit.Cryptography.ArcSigner"/> class. /// </summary> /// <remarks> /// Creates a new <see cref="ArcSigner"/>. /// </remarks> /// <param name="key">The signer's private key.</param> /// <param name="domain">The domain that the signer represents.</param> /// <param name="selector">The selector subdividing the domain.</param> /// <param name="algorithm">The signature algorithm.</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="key"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="domain"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="selector"/> is <c>null</c>.</para> /// </exception> /// <exception cref="System.ArgumentException"> /// <paramref name="key"/> is not a private key. /// </exception> protected ArcSigner(AsymmetricKeyParameter key, string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) : this(domain, selector, algorithm) { if (key == null) { throw new ArgumentNullException(nameof(key)); } if (!key.IsPrivate) { throw new ArgumentException("The key must be a private key.", nameof(key)); } PrivateKey = key; }
static void ValidateDkimSignatureParameters(IDictionary <string, string> parameters, out DkimSignatureAlgorithm algorithm, out DkimCanonicalizationAlgorithm headerAlgorithm, out DkimCanonicalizationAlgorithm bodyAlgorithm, out string d, out string s, out string q, out string[] headers, out string bh, out string b, out int maxLength) { bool containsFrom = false; if (!parameters.TryGetValue("v", out string v)) { throw new FormatException("Malformed DKIM-Signature header: no version parameter detected."); } if (v != "1") { throw new FormatException(string.Format("Unrecognized DKIM-Signature version: v={0}", v)); } ValidateCommonSignatureParameters("DKIM-Signature", parameters, out algorithm, out headerAlgorithm, out bodyAlgorithm, out d, out s, out q, out headers, out bh, out b, out maxLength); for (int i = 0; i < headers.Length; i++) { if (headers[i].Equals("from", StringComparison.OrdinalIgnoreCase)) { containsFrom = true; break; } } if (!containsFrom) { throw new FormatException("Malformed DKIM-Signature header: From header not signed."); } if (parameters.TryGetValue("i", out string id)) { string ident; int at; if ((at = id.LastIndexOf('@')) == -1) { throw new FormatException("Malformed DKIM-Signature header: no @ in the AUID value."); } ident = id.Substring(at + 1); if (!ident.Equals(d, StringComparison.OrdinalIgnoreCase) && !ident.EndsWith("." + d, StringComparison.OrdinalIgnoreCase)) { throw new FormatException("Invalid DKIM-Signature header: the domain in the AUID does not match the domain parameter."); } } }
byte[] DkimHashBody (FormatOptions options, DkimSignatureAlgorithm signatureAlgorithm, DkimCanonicalizationAlgorithm bodyCanonicalizationAlgorithm, int maxLength) { using (var stream = new DkimHashStream (signatureAlgorithm, maxLength)) { using (var filtered = new FilteredStream (stream)) { DkimBodyFilter dkim; if (bodyCanonicalizationAlgorithm == DkimCanonicalizationAlgorithm.Relaxed) dkim = new DkimRelaxedBodyFilter (); else dkim = new DkimSimpleBodyFilter (); filtered.Add (options.CreateNewLineFilter ()); filtered.Add (dkim); if (Body != null) { try { Body.EnsureNewLine = compliance == RfcComplianceMode.Strict; Body.Headers.Suppress = true; Body.WriteTo (options, filtered, CancellationToken.None); } finally { Body.Headers.Suppress = false; Body.EnsureNewLine = false; } } filtered.Flush (); if (!dkim.LastWasNewLine) stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length); } return stream.GenerateHash (); } }
public DummyArcSigner(AsymmetricKeyParameter key, string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) : base(key, domain, selector, algorithm) { }
public DummyArcSigner(string fileName, string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) : base(fileName, domain, selector, algorithm) { }
static void ValidateArcMessageSignatureParameters(IDictionary <string, string> parameters, out DkimSignatureAlgorithm algorithm, out DkimCanonicalizationAlgorithm headerAlgorithm, out DkimCanonicalizationAlgorithm bodyAlgorithm, out string d, out string s, out string q, out string[] headers, out string bh, out string b, out int maxLength) { ValidateCommonSignatureParameters("ARC-Message-Signature", parameters, out algorithm, out headerAlgorithm, out bodyAlgorithm, out d, out s, out q, out headers, out bh, out b, out maxLength); }
static void ValidateArcSealParameters(IDictionary <string, string> parameters, out DkimSignatureAlgorithm algorithm, out string d, out string s, out string q, out string b) { ValidateCommonParameters("ARC-Seal", parameters, out algorithm, out d, out s, out q, out b); if (parameters.TryGetValue("h", out string h)) { throw new FormatException(string.Format("Malformed ARC-Seal header: the 'h' parameter tag is not allowed.")); } }
/// <summary> /// Enable a DKIM signature algorithm. /// </summary> /// <remarks> /// <para>Enables the specified DKIM signature algorithm.</para> /// <note type="security">Due to the recognized weakness of the SHA-1 hash algorithm /// and the wide availability of the SHA-256 hash algorithm (it has been a required /// part of DKIM since it was originally standardized in 2007), it is recommended /// that <see cref="DkimSignatureAlgorithm.RsaSha1"/> NOT be enabled.</note> /// </remarks> /// <param name="algorithm">The DKIM signature algorithm.</param> public void Enable(DkimSignatureAlgorithm algorithm) { enabledSignatureAlgorithms |= 1 << (int)algorithm; }
/// <summary> /// Disable a DKIM signature algorithm. /// </summary> /// <remarks> /// <para>Disables the specified DKIM signature algorithm.</para> /// <note type="security">Due to the recognized weakness of the SHA-1 hash algorithm /// and the wide availability of the SHA-256 hash algorithm (it has been a required /// part of DKIM since it was originally standardized in 2007), it is recommended /// that <see cref="DkimSignatureAlgorithm.RsaSha1"/> NOT be enabled.</note> /// </remarks> /// <param name="algorithm">The DKIM signature algorithm.</param> public void Disable(DkimSignatureAlgorithm algorithm) { enabledSignatureAlgorithms &= ~(1 << (int)algorithm); }
/// <summary> /// Check whether a DKIM signature algorithm is enabled. /// </summary> /// <remarks> /// <para>Determines whether the specified DKIM signature algorithm is enabled.</para> /// <note type="security">Due to the recognized weakness of the SHA-1 hash algorithm /// and the wide availability of the SHA-256 hash algorithm (it has been a required /// part of DKIM since it was originally standardized in 2007), it is recommended /// that <see cref="DkimSignatureAlgorithm.RsaSha1"/> NOT be enabled.</note> /// </remarks> /// <returns><c>true</c> if the specified DKIM signature algorithm is enabled; otherwise, <c>false</c>.</returns> /// <param name="algorithm">The DKIM signature algorithm.</param> public bool IsEnabled(DkimSignatureAlgorithm algorithm) { return((enabledSignatureAlgorithms & (1 << (int)algorithm)) != 0); }
static void TestEmptyBody (DkimSignatureAlgorithm signatureAlgorithm, DkimCanonicalizationAlgorithm bodyAlgorithm, string expectedHash) { var headers = new [] { HeaderId.From, HeaderId.To, HeaderId.Subject, HeaderId.Date }; var signer = CreateSigner (signatureAlgorithm); var message = new MimeMessage (); message.From.Add (new MailboxAddress ("", "*****@*****.**")); message.To.Add (new MailboxAddress ("", "*****@*****.**")); message.Subject = "This is an empty message"; message.Date = DateTimeOffset.Now; message.Body = new TextPart ("plain") { Text = "" }; message.Body.Prepare (EncodingConstraint.SevenBit); message.Sign (signer, headers, DkimCanonicalizationAlgorithm.Simple, bodyAlgorithm); VerifyDkimBodyHash (message, signatureAlgorithm, expectedHash); var dkim = message.Headers[0]; Assert.IsTrue (message.Verify (dkim, new DummyPublicKeyLocator (DkimKeys.Public)), "Failed to verify DKIM-Signature."); }
internal static void ValidateCommonParameters(string header, IDictionary <string, string> parameters, out DkimSignatureAlgorithm algorithm, out string d, out string s, out string q, out string b) { if (!parameters.TryGetValue("a", out string a)) { throw new FormatException(string.Format("Malformed {0} header: no signature algorithm parameter detected.", header)); } switch (a.ToLowerInvariant()) { case "ed25519-sha256": algorithm = DkimSignatureAlgorithm.Ed25519Sha256; break; case "rsa-sha256": algorithm = DkimSignatureAlgorithm.RsaSha256; break; case "rsa-sha1": algorithm = DkimSignatureAlgorithm.RsaSha1; break; default: throw new FormatException(string.Format("Unrecognized {0} algorithm parameter: a={1}", header, a)); } if (!parameters.TryGetValue("d", out d)) { throw new FormatException(string.Format("Malformed {0} header: no domain parameter detected.", header)); } if (d.Length == 0) { throw new FormatException(string.Format("Malformed {0} header: empty domain parameter detected.", header)); } if (!parameters.TryGetValue("s", out s)) { throw new FormatException(string.Format("Malformed {0} header: no selector parameter detected.", header)); } if (s.Length == 0) { throw new FormatException(string.Format("Malformed {0} header: empty selector parameter detected.", header)); } if (!parameters.TryGetValue("q", out q)) { q = "dns/txt"; } if (!parameters.TryGetValue("b", out b)) { throw new FormatException(string.Format("Malformed {0} header: no signature parameter detected.", header)); } if (b.Length == 0) { throw new FormatException(string.Format("Malformed {0} header: empty signature parameter detected.", header)); } if (parameters.TryGetValue("t", out string t)) { if (!int.TryParse(t, NumberStyles.Integer, CultureInfo.InvariantCulture, out int timestamp) || timestamp < 0) { throw new FormatException(string.Format("Malformed {0} header: invalid timestamp parameter: t={1}.", header, t)); } } }
static void TestDkimSignVerify (MimeMessage message, DkimSignatureAlgorithm signatureAlgorithm, DkimCanonicalizationAlgorithm headerAlgorithm, DkimCanonicalizationAlgorithm bodyAlgorithm) { var headers = new HeaderId[] { HeaderId.From, HeaderId.Subject, HeaderId.Date }; var signer = CreateSigner (signatureAlgorithm); message.Sign (signer, headers, headerAlgorithm, bodyAlgorithm); var dkim = message.Headers[0]; Assert.IsTrue (message.Verify (dkim, new DummyPublicKeyLocator (DkimKeys.Public)), "Failed to verify DKIM-Signature."); message.Headers.RemoveAt (0); }
internal static void ValidateCommonSignatureParameters(string header, IDictionary <string, string> parameters, out DkimSignatureAlgorithm algorithm, out DkimCanonicalizationAlgorithm headerAlgorithm, out DkimCanonicalizationAlgorithm bodyAlgorithm, out string d, out string s, out string q, out string[] headers, out string bh, out string b, out int maxLength) { ValidateCommonParameters(header, parameters, out algorithm, out d, out s, out q, out b); if (parameters.TryGetValue("l", out string l)) { if (!int.TryParse(l, NumberStyles.Integer, CultureInfo.InvariantCulture, out maxLength) || maxLength < 0) { throw new FormatException(string.Format("Malformed {0} header: invalid length parameter: l={1}", header, l)); } } else { maxLength = -1; } if (parameters.TryGetValue("c", out string c)) { var tokens = c.ToLowerInvariant().Split('/'); if (tokens.Length == 0 || tokens.Length > 2) { throw new FormatException(string.Format("Malformed {0} header: invalid canonicalization parameter: c={1}", header, c)); } switch (tokens[0]) { case "relaxed": headerAlgorithm = DkimCanonicalizationAlgorithm.Relaxed; break; case "simple": headerAlgorithm = DkimCanonicalizationAlgorithm.Simple; break; default: throw new FormatException(string.Format("Malformed {0} header: invalid canonicalization parameter: c={1}", header, c)); } if (tokens.Length == 2) { switch (tokens[1]) { case "relaxed": bodyAlgorithm = DkimCanonicalizationAlgorithm.Relaxed; break; case "simple": bodyAlgorithm = DkimCanonicalizationAlgorithm.Simple; break; default: throw new FormatException(string.Format("Malformed {0} header: invalid canonicalization parameter: c={1}", header, c)); } } else { bodyAlgorithm = DkimCanonicalizationAlgorithm.Simple; } } else { headerAlgorithm = DkimCanonicalizationAlgorithm.Simple; bodyAlgorithm = DkimCanonicalizationAlgorithm.Simple; } if (!parameters.TryGetValue("h", out string h)) { throw new FormatException(string.Format("Malformed {0} header: no signed header parameter detected.", header)); } headers = h.Split(':'); if (!parameters.TryGetValue("bh", out bh)) { throw new FormatException(string.Format("Malformed {0} header: no body hash parameter detected.", header)); } }
static ISigner DkimGetDigestSigner (DkimSignatureAlgorithm algorithm, AsymmetricKeyParameter key) { DerObjectIdentifier id; if (algorithm == DkimSignatureAlgorithm.RsaSha256) id = PkcsObjectIdentifiers.Sha256WithRsaEncryption; else id = PkcsObjectIdentifiers.Sha1WithRsaEncryption; var signer = SignerUtilities.GetSigner (id); signer.Init (key.IsPrivate, key); return signer; }
/// <summary> /// Verify the hash of the message body. /// </summary> /// <remarks> /// Verifies the hash of the message body. /// </remarks> /// <param name="options">The formatting options.</param> /// <param name="message">The signed MIME message.</param> /// <param name="signatureAlgorithm">The algorithm used to sign the message.</param> /// <param name="canonicalizationAlgorithm">The algorithm used to canonicalize the message body.</param> /// <param name="maxLength">The max length of the message body to hash or <c>-1</c> to hash the entire message body.</param> /// <param name="bodyHash">The expected message body hash encoded in base64.</param> /// <returns><c>true</c> if the calculated body hash matches <paramref name="bodyHash"/>; otherwise, <c>false</c>.</returns> protected bool VerifyBodyHash(FormatOptions options, MimeMessage message, DkimSignatureAlgorithm signatureAlgorithm, DkimCanonicalizationAlgorithm canonicalizationAlgorithm, int maxLength, string bodyHash) { var hash = Convert.ToBase64String(message.HashBody(options, signatureAlgorithm, canonicalizationAlgorithm, maxLength)); return(hash == bodyHash); }
static void ValidateDkimSignatureParameters (IDictionary<string, string> parameters, out DkimSignatureAlgorithm algorithm, out DkimCanonicalizationAlgorithm headerAlgorithm, out DkimCanonicalizationAlgorithm bodyAlgorithm, out string d, out string s, out string q, out string h, out string bh, out string b, out int maxLength) { string v, a, c, l; if (!parameters.TryGetValue ("v", out v)) throw new FormatException ("Malformed DKIM-Signature header: no version parameter detected."); if (v != "1") throw new FormatException (string.Format ("Unrecognized DKIM-Signature version: v={0}", v)); if (!parameters.TryGetValue ("a", out a)) throw new FormatException ("Malformed DKIM-Signature header: no signature algorithm parameter detected."); switch (a.ToLowerInvariant ()) { case "rsa-sha256": algorithm = DkimSignatureAlgorithm.RsaSha256; break; case "rsa-sha1": algorithm = DkimSignatureAlgorithm.RsaSha1; break; default: throw new FormatException (string.Format ("Unrecognized DKIM-Signature algorithm parameter: a={0}", a)); } if (!parameters.TryGetValue ("d", out d)) throw new FormatException ("Malformed DKIM-Signature header: no domain parameter detected."); if (!parameters.TryGetValue ("s", out s)) throw new FormatException ("Malformed DKIM-Signature header: no selector parameter detected."); if (!parameters.TryGetValue ("q", out q)) q = "dns/txt"; if (parameters.TryGetValue ("l", out l)) { if (!int.TryParse (l, out maxLength)) throw new FormatException (string.Format ("Malformed DKIM-Signature header: invalid length parameter: l={0}", l)); } else { maxLength = -1; } if (parameters.TryGetValue ("c", out c)) { var tokens = c.ToLowerInvariant ().Split ('/'); if (tokens.Length == 0 || tokens.Length > 2) throw new FormatException (string.Format ("Malformed DKIM-Signature header: invalid canonicalization parameter: c={0}", c)); switch (tokens[0]) { case "relaxed": headerAlgorithm = DkimCanonicalizationAlgorithm.Relaxed; break; case "simple": headerAlgorithm = DkimCanonicalizationAlgorithm.Simple; break; default: throw new FormatException (string.Format ("Malformed DKIM-Signature header: invalid canonicalization parameter: c={0}", c)); } if (tokens.Length == 2) { switch (tokens[1]) { case "relaxed": bodyAlgorithm = DkimCanonicalizationAlgorithm.Relaxed; break; case "simple": bodyAlgorithm = DkimCanonicalizationAlgorithm.Simple; break; default: throw new FormatException (string.Format ("Malformed DKIM-Signature header: invalid canonicalization parameter: c={0}", c)); } } else { bodyAlgorithm = DkimCanonicalizationAlgorithm.Simple; } } else { headerAlgorithm = DkimCanonicalizationAlgorithm.Simple; bodyAlgorithm = DkimCanonicalizationAlgorithm.Simple; } if (!parameters.TryGetValue ("h", out h)) throw new FormatException ("Malformed DKIM-Signature header: no signed header parameter detected."); if (!parameters.TryGetValue ("bh", out bh)) throw new FormatException ("Malformed DKIM-Signature header: no body hash parameter detected."); if (!parameters.TryGetValue ("b", out b)) throw new FormatException ("Malformed DKIM-Signature header: no signature parameter detected."); }
/// <summary> /// Initializes a new instance of the <see cref="MimeKit.Cryptography.DkimSigner"/> class. /// </summary> /// <remarks> /// Creates a new <see cref="DkimSigner"/>. /// </remarks> /// <param name="key">The signer's private key.</param> /// <param name="domain">The domain that the signer represents.</param> /// <param name="selector">The selector subdividing the domain.</param> /// <param name="algorithm">The signature algorithm.</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="key"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="domain"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="selector"/> is <c>null</c>.</para> /// </exception> /// <exception cref="System.ArgumentException"> /// <paramref name="key"/> is not a private key. /// </exception> public DkimSigner(AsymmetricKeyParameter key, string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) { if (key == null) { throw new ArgumentNullException(nameof(key)); } if (domain == null) { throw new ArgumentNullException(nameof(domain)); } if (selector == null) { throw new ArgumentNullException(nameof(selector)); } if (!key.IsPrivate) { throw new ArgumentException("The key must be a private key.", nameof(key)); } SignatureAlgorithm = algorithm; Selector = selector; PrivateKey = key; Domain = domain; }