static void TestEmptyBody(DkimSignatureAlgorithm signatureAlgorithm, DkimCanonicalizationAlgorithm bodyAlgorithm, string expectedHash) { var signer = CreateSigner(signatureAlgorithm, DkimCanonicalizationAlgorithm.Simple, bodyAlgorithm); var headers = new [] { HeaderId.From, HeaderId.To, HeaderId.Subject, HeaderId.Date }; var verifier = new DkimVerifier(new DummyPublicKeyLocator(DkimKeys.Public)); 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.Prepare(EncodingConstraint.SevenBit); signer.Sign(message, headers); VerifyDkimBodyHash(message, signatureAlgorithm, expectedHash); var dkim = message.Headers[0]; if (signatureAlgorithm == DkimSignatureAlgorithm.RsaSha1) { Assert.IsFalse(verifier.Verify(message, dkim), "DKIM-Signature using rsa-sha1 should not verify."); // now enable rsa-sha1 to verify again, this time it should pass... verifier.Enable(DkimSignatureAlgorithm.RsaSha1); } Assert.IsTrue(verifier.Verify(message, dkim), "Failed to verify DKIM-Signature."); }
public void TestFormatExceptions() { var message = MimeMessage.Load(Path.Combine(TestHelper.ProjectDir, "TestData", "dkim", "gmail.msg")); var verifier = new DkimVerifier(new DummyPublicKeyLocator(DkimKeys.Public)); var index = message.Headers.IndexOf(HeaderId.DkimSignature); var dkim = message.Headers[index]; var original = dkim.Value; // first, remove the 'v' tag and its value dkim.Value = dkim.Value.Substring(4); Assert.Throws <FormatException> (() => verifier.Verify(message, dkim), "Expected FormatException for missing v=1;"); // add back a 'v' tag with an invalid value dkim.Value = "v=x; " + dkim.Value; Assert.Throws <FormatException> (() => verifier.Verify(message, dkim), "Expected FormatException for v=x;"); // remove "from:" dkim.Value = original.Replace("from:", ""); Assert.Throws <FormatException> (() => verifier.Verify(message, dkim), "Expected FormatException for missing from header"); // add an invalid i= value w/o an '@' dkim.Value = "i=1; " + original; Assert.Throws <FormatException> (() => verifier.Verify(message, dkim), "Expected FormatException for an invalid i= value (missing '@')"); // add an invalid i= value that does not match the domain dkim.Value = "i=user@domain; " + original; Assert.Throws <FormatException> (() => verifier.Verify(message, dkim), "Expected FormatException for an invalid i= that does not contain the domain"); // add an invalid l= value dkim.Value = "l=abc; " + original; Assert.Throws <FormatException> (() => verifier.Verify(message, dkim), "Expected FormatException for an invalid l= value"); // set an invalid body canonicalization algorithm dkim.Value = original.Replace("c=relaxed/relaxed;", "c=simple/complex;"); Assert.Throws <FormatException> (() => verifier.Verify(message, dkim), "Expected FormatException for an invalid body canonicalization value"); // set an invalid c= value dkim.Value = original.Replace("c=relaxed/relaxed;", "c=;"); Assert.Throws <FormatException> (() => verifier.Verify(message, dkim), "Expected FormatException for an invalid c= value (empty)"); // set an invalid c= value dkim.Value = original.Replace("c=relaxed/relaxed;", "c=relaxed/relaxed/extra;"); Assert.Throws <FormatException> (() => verifier.Verify(message, dkim), "Expected FormatException for an invalid c= value (3 values)"); }
public static void Main(string[] args) { if (args.Length == 0) { Help(); return; } for (int i = 0; i < args.Length; i++) { if (args[i] == "--help") { Help(); return; } } var locator = new DkimPublicKeyLocator(); var verifier = new DkimVerifier(locator); // RSA-SHA1 is disabled by default starting with MimeKit 2.2.0 verifier.Enable(DkimSignatureAlgorithm.RsaSha1); for (int i = 0; i < args.Length; i++) { if (!File.Exists(args[i])) { Console.Error.WriteLine("{0}: No such file.", args[i]); continue; } Console.Write("{0} -> ", args[i]); var message = MimeMessage.Load(args[i]); var index = message.Headers.IndexOf(HeaderId.DkimSignature); if (index == -1) { Console.WriteLine("NO SIGNATURE"); continue; } var dkim = message.Headers[index]; if (verifier.Verify(message, dkim)) { // the DKIM-Signature header is valid! Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("VALID"); Console.ResetColor(); } else { // the DKIM-Signature is invalid! Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("INVALID"); Console.ResetColor(); } } }
public void TestVerifyRfc8463Example() { var message = MimeMessage.Load(Path.Combine(TestHelper.ProjectDir, "TestData", "dkim", "rfc8463-example.msg")); var locator = new DkimPublicKeyLocator(); var verifier = new DkimVerifier(locator); int index; locator.Add("brisbane._domainkey.football.example.com", "v=DKIM1; k=ed25519; p=11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo="); locator.Add("test._domainkey.football.example.com", "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQiY/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqMKrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB"); // the last DKIM-Signature uses rsa-sha256 index = message.Headers.LastIndexOf(HeaderId.DkimSignature); Assert.IsTrue(verifier.Verify(message, message.Headers[index]), "Failed to verify rsa-sha256"); // the first DKIM-Signature uses ed25519-sha256 index = message.Headers.IndexOf(HeaderId.DkimSignature); Assert.IsTrue(verifier.Verify(message, message.Headers[index]), "Failed to verify ed25519-sha256"); }
public void TestVerifyGoogleMultipartWithoutEndBoundaryDkimSignature() { var message = MimeMessage.Load(Path.Combine(TestHelper.ProjectDir, "TestData", "dkim", "multipart-no-end-boundary.msg")); int index = message.Headers.IndexOf(HeaderId.DkimSignature); var locator = new DummyPublicKeyLocator(GMailDkimPublicKey); var verifier = new DkimVerifier(locator); Assert.IsTrue(verifier.Verify(message, message.Headers[index]), "Failed to verify GMail signature."); }
public void TestVerifyGoogleMultipartRelatedDkimSignature() { var message = MimeMessage.Load(Path.Combine("..", "..", "TestData", "dkim", "related.msg")); int index = message.Headers.IndexOf(HeaderId.DkimSignature); var locator = new DummyPublicKeyLocator(GMailDkimPublicKey); var verifier = new DkimVerifier(locator); Assert.IsTrue(verifier.Verify(message, message.Headers[index]), "Failed to verify GMail signature."); }
static void TestDkimSignVerify(MimeMessage message, DkimSignatureAlgorithm signatureAlgorithm, DkimCanonicalizationAlgorithm headerAlgorithm, DkimCanonicalizationAlgorithm bodyAlgorithm) { var headers = new HeaderId[] { HeaderId.From, HeaderId.Subject, HeaderId.Date }; var verifier = new DkimVerifier(new DummyPublicKeyLocator(DkimKeys.Public)); var signer = CreateSigner(signatureAlgorithm, headerAlgorithm, bodyAlgorithm); signer.Sign(message, headers); var dkim = message.Headers[0]; if (signatureAlgorithm == DkimSignatureAlgorithm.RsaSha1) { Assert.IsFalse(verifier.Verify(message, dkim), "DKIM-Signature using rsa-sha1 should not verify."); // now enable rsa-sha1 to verify again, this time it should pass... verifier.Enable(DkimSignatureAlgorithm.RsaSha1); } Assert.IsTrue(verifier.Verify(message, dkim), "Failed to verify DKIM-Signature."); message.Headers.RemoveAt(0); }
static void TestUnicode(DkimSignatureAlgorithm signatureAlgorithm, DkimCanonicalizationAlgorithm bodyAlgorithm, string expectedHash) { var signer = CreateSigner(signatureAlgorithm, DkimCanonicalizationAlgorithm.Simple, bodyAlgorithm); var headers = new [] { HeaderId.From, HeaderId.To, HeaderId.Subject, HeaderId.Date }; var verifier = new DkimVerifier(new DummyPublicKeyLocator(DkimKeys.Public)); 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.Prepare(EncodingConstraint.EightBit); signer.Sign(message, headers); var dkim = message.Headers[0]; VerifyDkimBodyHash(message, signatureAlgorithm, expectedHash); if (signatureAlgorithm == DkimSignatureAlgorithm.RsaSha1) { Assert.IsFalse(verifier.Verify(message, dkim), "DKIM-Signature using rsa-sha1 should not verify."); // now enable rsa-sha1 to verify again, this time it should pass... verifier.Enable(DkimSignatureAlgorithm.RsaSha1); } Assert.IsTrue(verifier.Verify(message, dkim), "Failed to verify DKIM-Signature."); }
public void TestSignRfc8463Example() { var message = MimeMessage.Load(Path.Combine(TestHelper.ProjectDir, "TestData", "dkim", "rfc8463-example.msg")); var signer = new DkimSigner(Ed25519PrivateKey, "football.example.com", "brisbane", DkimSignatureAlgorithm.Ed25519Sha256) { HeaderCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Relaxed, BodyCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Relaxed, AgentOrUserIdentifier = "@football.example.com" }; var headers = new string[] { "from", "to", "subject", "date", "message-id", "from", "subject", "date" }; signer.Sign(message, headers); int index = message.Headers.IndexOf(HeaderId.DkimSignature); var locator = new DkimPublicKeyLocator(); var verifier = new DkimVerifier(locator); var dkim = message.Headers[index]; locator.Add("brisbane._domainkey.football.example.com", "v=DKIM1; k=ed25519; p=11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo="); locator.Add("test._domainkey.football.example.com", "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQiY/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqMKrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB"); Assert.IsTrue(verifier.Verify(message, message.Headers[index]), "Failed to verify ed25519-sha256"); }
public void TestArgumentExceptions() { var locator = new DummyPublicKeyLocator(DkimKeys.Public); var verifier = new DkimVerifier(locator); var dkimHeader = new Header(HeaderId.DkimSignature, "value"); var arcHeader = new Header(HeaderId.ArcMessageSignature, "value"); var options = FormatOptions.Default; var message = new MimeMessage(); DkimSigner signer; Assert.Throws <ArgumentNullException> (() => new DkimSigner((AsymmetricKeyParameter)null, "domain", "selector")); Assert.Throws <ArgumentException> (() => new DkimSigner(DkimKeys.Public, "domain", "selector")); Assert.Throws <ArgumentNullException> (() => new DkimSigner(DkimKeys.Private, null, "selector")); Assert.Throws <ArgumentNullException> (() => new DkimSigner(DkimKeys.Private, "domain", null)); Assert.Throws <ArgumentNullException> (() => new DkimSigner((string)null, "domain", "selector")); Assert.Throws <ArgumentNullException> (() => new DkimSigner("fileName", null, "selector")); Assert.Throws <ArgumentNullException> (() => new DkimSigner("fileName", "domain", null)); Assert.Throws <ArgumentException> (() => new DkimSigner(string.Empty, "domain", "selector")); Assert.Throws <ArgumentNullException> (() => new DkimSigner((Stream)null, "domain", "selector")); using (var stream = File.OpenRead(Path.Combine(TestHelper.ProjectDir, "TestData", "dkim", "example.pem"))) { Assert.Throws <ArgumentNullException> (() => new DkimSigner(stream, null, "selector")); Assert.Throws <ArgumentNullException> (() => new DkimSigner(stream, "domain", null)); signer = new DkimSigner(stream, "example.com", "1433868189.example") { SignatureAlgorithm = DkimSignatureAlgorithm.RsaSha1, AgentOrUserIdentifier = "@eng.example.com", QueryMethod = "dns/txt", }; } Assert.Throws <ArgumentNullException> (() => signer.Sign(null, new HeaderId[] { HeaderId.From })); Assert.Throws <ArgumentNullException> (() => signer.Sign(message, (IList <HeaderId>)null)); Assert.Throws <ArgumentException> (() => signer.Sign(message, new HeaderId[] { HeaderId.Unknown, HeaderId.From })); Assert.Throws <ArgumentException> (() => signer.Sign(message, new HeaderId[] { HeaderId.Received, HeaderId.From })); Assert.Throws <ArgumentException> (() => signer.Sign(message, new HeaderId[] { HeaderId.ContentType })); Assert.Throws <ArgumentNullException> (() => signer.Sign(null, new string[] { "From" })); Assert.Throws <ArgumentNullException> (() => signer.Sign(message, (IList <string>)null)); Assert.Throws <ArgumentException> (() => signer.Sign(message, new string[] { "", "From" })); Assert.Throws <ArgumentException> (() => signer.Sign(message, new string[] { null, "From" })); Assert.Throws <ArgumentException> (() => signer.Sign(message, new string[] { "Received", "From" })); Assert.Throws <ArgumentException> (() => signer.Sign(message, new string[] { "Content-Type" })); Assert.Throws <ArgumentNullException> (() => signer.Sign(null, message, new HeaderId[] { HeaderId.From })); Assert.Throws <ArgumentNullException> (() => signer.Sign(options, null, new HeaderId[] { HeaderId.From })); Assert.Throws <ArgumentException> (() => signer.Sign(options, message, new HeaderId[] { HeaderId.From, HeaderId.Unknown })); Assert.Throws <ArgumentNullException> (() => signer.Sign(options, message, (IList <HeaderId>)null)); Assert.Throws <ArgumentNullException> (() => signer.Sign(null, message, new string[] { "From" })); Assert.Throws <ArgumentNullException> (() => signer.Sign(options, null, new string[] { "From" })); Assert.Throws <ArgumentException> (() => signer.Sign(options, message, new string[] { "From", null })); Assert.Throws <ArgumentNullException> (() => signer.Sign(options, message, (IList <string>)null)); Assert.Throws <ArgumentNullException> (() => new DkimVerifier(null)); Assert.Throws <ArgumentNullException> (() => verifier.Verify(null, dkimHeader)); Assert.Throws <ArgumentNullException> (() => verifier.Verify(message, null)); Assert.Throws <ArgumentNullException> (() => verifier.Verify(null, message, dkimHeader)); Assert.Throws <ArgumentNullException> (() => verifier.Verify(FormatOptions.Default, null, dkimHeader)); Assert.Throws <ArgumentNullException> (() => verifier.Verify(FormatOptions.Default, message, null)); Assert.Throws <ArgumentException> (() => verifier.Verify(FormatOptions.Default, message, arcHeader)); Assert.ThrowsAsync <ArgumentNullException> (async() => await verifier.VerifyAsync(null, dkimHeader)); Assert.ThrowsAsync <ArgumentNullException> (async() => await verifier.VerifyAsync(message, null)); Assert.ThrowsAsync <ArgumentNullException> (async() => await verifier.VerifyAsync(null, message, dkimHeader)); Assert.ThrowsAsync <ArgumentNullException> (async() => await verifier.VerifyAsync(FormatOptions.Default, null, dkimHeader)); Assert.ThrowsAsync <ArgumentNullException> (async() => await verifier.VerifyAsync(FormatOptions.Default, message, null)); Assert.ThrowsAsync <ArgumentException> (async() => await verifier.VerifyAsync(FormatOptions.Default, message, arcHeader)); Assert.Throws <ArgumentNullException> (() => message.Sign(null, new HeaderId[] { HeaderId.From })); Assert.Throws <ArgumentNullException> (() => message.Sign(signer, (IList <HeaderId>)null)); Assert.Throws <ArgumentException> (() => message.Sign(signer, new HeaderId[] { HeaderId.Unknown, HeaderId.From })); Assert.Throws <ArgumentException> (() => message.Sign(signer, new HeaderId[] { HeaderId.Received, HeaderId.From })); Assert.Throws <ArgumentException> (() => message.Sign(signer, new HeaderId[] { HeaderId.ContentType })); Assert.Throws <ArgumentNullException> (() => message.Sign(null, new string[] { "From" })); Assert.Throws <ArgumentNullException> (() => message.Sign(signer, (IList <string>)null)); Assert.Throws <ArgumentException> (() => message.Sign(signer, new string[] { "", "From" })); Assert.Throws <ArgumentException> (() => message.Sign(signer, new string[] { null, "From" })); Assert.Throws <ArgumentException> (() => message.Sign(signer, new string[] { "Received", "From" })); Assert.Throws <ArgumentException> (() => message.Sign(signer, new string[] { "Content-Type" })); Assert.Throws <ArgumentNullException> (() => message.Sign(null, signer, new HeaderId[] { HeaderId.From })); Assert.Throws <ArgumentNullException> (() => message.Sign(options, null, new HeaderId[] { HeaderId.From })); Assert.Throws <ArgumentException> (() => message.Sign(options, signer, new HeaderId[] { HeaderId.From, HeaderId.Unknown })); Assert.Throws <ArgumentNullException> (() => message.Sign(options, signer, (IList <HeaderId>)null)); Assert.Throws <ArgumentNullException> (() => message.Sign(null, signer, new string[] { "From" })); Assert.Throws <ArgumentNullException> (() => message.Sign(options, null, new string[] { "From" })); Assert.Throws <ArgumentException> (() => message.Sign(options, signer, new string[] { "From", null })); Assert.Throws <ArgumentNullException> (() => message.Sign(options, signer, (IList <string>)null)); Assert.Throws <ArgumentNullException> (() => message.Verify(null, locator)); Assert.Throws <ArgumentNullException> (() => message.Verify(dkimHeader, null)); Assert.Throws <ArgumentNullException> (() => message.Verify(null, dkimHeader, locator)); Assert.Throws <ArgumentNullException> (() => message.Verify(FormatOptions.Default, null, locator)); Assert.Throws <ArgumentNullException> (() => message.Verify(FormatOptions.Default, dkimHeader, null)); Assert.Throws <ArgumentException> (() => message.Verify(FormatOptions.Default, arcHeader, locator)); Assert.ThrowsAsync <ArgumentNullException> (async() => await message.VerifyAsync(null, locator)); Assert.ThrowsAsync <ArgumentNullException> (async() => await message.VerifyAsync(dkimHeader, null)); Assert.ThrowsAsync <ArgumentNullException> (async() => await message.VerifyAsync(null, dkimHeader, locator)); Assert.ThrowsAsync <ArgumentNullException> (async() => await message.VerifyAsync(FormatOptions.Default, null, locator)); Assert.ThrowsAsync <ArgumentNullException> (async() => await message.VerifyAsync(FormatOptions.Default, dkimHeader, null)); Assert.ThrowsAsync <ArgumentException> (async() => await message.VerifyAsync(FormatOptions.Default, arcHeader, locator)); }