C#: How to send emails

Sending a basic email message in a C# application is quite easy thanks to a class called SmptClient. We simply need an address to send to, an address to send from, the message we want to send and the address of an SMTP server, hand it all to the SMTP client, and you’re done:

var from = new MailAddress("me@example.com", "Me");
var to = new MailAddress("you@example.com", "You");

var message = new MailMessage(from, to)
{
    Subject = "Greetings!",
    Body = "How are you doing today?",
};

var client = new SmtpClient("smtp.example.com");

using (client)
{
    try
    {
        client.Send(message);
    }
    catch (SmtpException e)
    {
        Console.WriteLine(e.Message);
    }
}

That was pretty simple, wasn’t it? But what if we need to authenticate with our server? And what if we want to send our message in a more secure manner?

Authentication

You will quite often find that an SMTP server requires you to authenticate yourself before it will let you do anything. This is however very simple to do. All we need, is to provide our credentials to the client:

var client = new SmtpClient("smtp.example.com")
{
    Credentials = new NetworkCredential
    {
        UserName = "me@example.com",
        Password = "password",
    },
};

Now, when you try to send your message like before, it should be sent without problems ๐Ÿ™‚ But what if you want to send your email securely?

Using SSL to encrypt the connection

To send emails through an encrypted connection, all you have to do is to set the EnableSSL property of the SmtpClient to true:

var client = new SmtpClient("smtp.example.com")
{
    EnableSSL = true,
    Credentials = new NetworkCredential
    {
        UserName = "me@example.com",
        Password = "password",
    },
};

When you now try to send your email, it will use the Secure Socket Layer to encrypt the connection. Like me, you might run into a problem though; an AuthenticationException with the following message: The remote certificate is invalid according to the validation procedure.

Imperfect certificates

After reading about how these certificates work and some more digging around I found out that the certificate of the SMTP server I use is considered invalid for two reasons: Firstly it was not issued by a trusted Certification Authority (like VeriSign or Thawte), but by my host (Dreamhost) themselves. And finally there is a mismatch between the domain I’m connecting to (smtp.example.com) and the domain the certificate was issued to (mail.dreamhost.com).

Manual certificate validation

SSL has two purposes: security and authentication. And the security will actually work fine and all the traffic will be encrypted even if the authentication fails. In other words, as long as we can look at the certificate ourselves and be sure that we thrust it, we can go ahead and continue with our work.

To check the certificate ourselves we can provide a RemoteCertificate­ValidationCallback delegate to a class called the ServicePoint­Manager.

ServicePointManager.ServerCertificateValidationCallback
    = OurCertificateValidation;

But how can we decide if we should trust the certificate or not? Well, I have found three ways that works nicely. Well, actually just two since the first is just to cover your eyes and let anything pass…

Pretend validation

The following implementation is a way to simply say that I don’t care:

static bool OurCertificateValidation(object s, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
    return true;
}

This is of course not very good… we should at least make some effort and check if the certificate is the one we should be getting. We can for example…

Check the certificate fingerprint

Certificates have something called a fingerprint and if we know the fingerprint of the certificate we should get, then we can compare it with the fingerprint of the one we actually got.

static bool OurCertificateValidation(object s, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
    return certificate.GetCertHashString()
        == "6B8C79AB966D70277BA86E6F820859A2B5B8CCC0"; // SHA-1 fingerprint
}

If you don’t think this is enough, you can for example…

Check the whole certificate

If you can export the certificate to a file somehow, you can then later compare the certificate you get with the one you have stored and make sure they are equal.

static bool OurCertificateValidation(object s, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
    var actualCertificate = X509Certificate.CreateFromCertFile("example.cert");
    return certificate.Equals(actualCertificate);
}

I got a hold of the certificate file by sticking the following snippet inside my validation delegate, ran my code once, and then changed it back to normal again:

static bool OurCertificateValidation(object s, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
    using (var file = File.Create("example.cert"))
    {
        var cert = certificate.Export(X509ContentType.Cert);
        file.Write(cert, 0, cert.Length);
    }
    return false;
}

I also successfully exported it from Opera, which I use as an email client, by going to Preferences, Security, Manage Certificates, Approved and then export the one I want. This of course assumes you have have previously approved the certificate for use with Opera.

Wrap-up

That’s all for now! Found it kind of fun to get this working and thought I could share it in case someone else struggles with this. Please comment and let me know if I have misunderstood something or done a big blunder or something like that! I mostly just know that my code is working, so if you know more about how these security issues work and you know something you think i should know as well: Please share! I’d like to learn ๐Ÿ™‚

Here is my final, complete code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
using System;
using System.Net.Mail;
using System.Net;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.IO;
using System.Net.Security;

namespace Geekality.SecureEmail
{
    class Program
    {
        static bool OurCertificateValidation(object s, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            var actualCertificate = X509Certificate.CreateFromCertFile("example.com.cert");

            return certificate.Equals(actualCertificate);
        }

        static void Main(string[] args)
        {
            // Register our own certificate validation
            ServicePointManager.ServerCertificateValidationCallback = OurCertificateValidation;

            // Message
            var from = new MailAddress("me@example.com", "Me");
            var to = new MailAddress("you@example.com", "Myself");

            var message = new MailMessage(from, to)
            {
                Subject = "Greetings!",
                Body = "How are you doing today?",
            };

            // Create client
            var client = new SmtpClient("smtp.example.com")
            {
                EnableSsl = true,
                Credentials = new NetworkCredential
                {
                    UserName = "me@example.com",
                    Password = "password",
                },
            };

            // Try to send
            using (client)
            {
                try
                {
                    client.Send(message);
                    Console.WriteLine("Message sent!");
                }
                catch (AuthenticationException e)
                {
                    Console.WriteLine(e.Message);
                }
                catch (SmtpException e)
                {
                    Console.WriteLine(e.Message);
                }
            }

            Console.ReadKey(true);
        }
    }
}
  • Davi

    Hello, Torleif
    I just wanto to thank you for make things simple as it is. I was looking for how to send e-mail consideing the certificates and this article help me out with your hint “validation pretend”, as this will run over an internal network, so let is pass.
    Thanks a lot.

    • Good to hear it can help someone!

  • Thangaraj

    Hi torleif,

    Your article is too good and this is the one i am searching for the past few days.

    I have one query,
    Can i send a certificate(which is self signed by my company) with smtpclient in C# code.
    1. Do i need to give the path of the certificate?
    Please give some help on this

    Regards,
    Thangs

    • Not sure what you mean by “send a certificate with smtpclient”. You can of course check against whatever certificate or fingerprint you want. Self-signed or not. That’s what I did when I tried this out, I checked against a certificate that was self-signed by Dreamhost, which was the reason why it didn’t validate by default.

      You will need to give the path of the certificate if you use the X509Certificate.CreateFromCertFile method, which I did. But just have a look at that class and you will notice that it has for example a constructor that takes a byte array that probably can be used in some way if you don’t want to store the certificate as a file but rather a resource for example.

  • Trแป‹nh Tรนng Anh

    Thank you very much
    You saved my life ๐Ÿ™‚ ๐Ÿ™‚

  • Chris

    I am getting an error on the var keyword

    • The var keyword has been a standard C# keyword since C# 3.0 I believe.

  • Ziv

    Hi Torlerif,

    Thanks you the article.. Are you familiar with the issue posted in systemnetmail.com – http://www.systemnetmail.com/faq/5.3.aspx

    Thanks,
    Ziv