Sign and encrypt e-mail with S/MIME

This guide describes using security certificates, certificate stores, public and private keys to sign and encrypt e-mails with S/MIME.


Introduction

To securely transfer an e-mail between the client and the server, SSL connection can be used. However, to protect the message from unauthorized access on all the steps of its lifecycle, you should encrypt the message itself.

Signing the message with S/MIME assures nobody has altered it during the transmission. You can additionally sign the message with DomainKeys/DKIM if you wish (but their certificates are different as S/MIME and DomainKeys/DKIM serve different purposes and use certificates of a different format).

S/MIME is all about certificates, so you'll usually need to purchase a certificate from a well-known security provider (Certification Authority, CA) unless you already have one.

Please note that MailBee.NET SMTP license does not include the license for S/MIME. You need the full MailBee.NET Objects license in order to send S/MIME signed or encrypted e-mails with MailBee.NET.

ASP.NET note. Many operations with certificates involve using Windows Registry so S/MIME functionality is very sensitive to the current user context. For instance, system-level certificate stores are practically useless for web applications as users have their certificates on their computers, in their own system-level stores. While a desktop application which runs on your computer under your user context can get your certificate, a web application running elsewhere can get access to this certificate only if you somehow upload the certificate file there.


Sign e-mail with S/MIME to prove authenticity of sender

Before sending S/MIME signed e-mail with MailBee.NET, make sure of the following:

The sample below signs the message with S/MIME and DKIM signatures (the private keys for both are taken from files), and sends the message:

Smtp mailer = new Smtp();

// Set the message properties.
mailer.Message.From.Email = "john.doe@company.com";
mailer.Message.To.Add("jane.doe@example.com");
mailer.Message.Subject = "Hello";
mailer.Message.BodyPlainText = "Hello, Jane, can we meet today?";

// Load the private key of john.doe@company.com from .PFX file.
Certificate signingCert = new Certificate(@"C:\Docs\private.pfx",
    CertFileType.Pfx, "secret");

// Sign the message with S/MIME.
Smime secureMime = new Smime();
mailer.Message = secureMime.Sign(mailer.Message, signingCert);

// Also sign the message with DKIM. It's not required for S/MIME,
// we just demonstrate you can use S/MIME and DKIM together.
// Comment the next two lines out if you don't need DKIM signature.
mailer.Message.DomainKeysSign(false, null, @"C:\Docs\rsa512.private",
    true, "dk", DomainKeysTypes.DKIM);

// Send the message via SMTP server (authentication is enabled).
mailer.SmtpServers.Add("mail.company.com", "john.doe", "secret");
mailer.Send();
Dim mailer As Smtp = New Smtp()

' Set the message properties.
mailer.Message.From.Email = "john.doe@company.com"
mailer.Message.To.Add("jane.doe@example.com")
mailer.Message.Subject = "Hello"
mailer.Message.BodyPlainText = "Hello, Jane, can we meet today?"

' Load the private key of john.doe@company.com from .PFX file.
Dim signingCert As Certificate = New Certificate("C:\Docs\private.pfx", _
    CertFileType.Pfx, "secret")

' Sign the message with S/MIME.
Dim secureMime As Smime = New Smime()
mailer.Message = secureMime.Sign(mailer.Message, signingCert)

' Also sign the message with DKIM. It's not required for S/MIME,
' we just demonstrate you can use S/MIME and DKIM together.
' Comment the next two lines out if you don't need DKIM signature.
mailer.Message.DomainKeysSign(False, Nothing, "C:\Docs\rsa512.private", _
    True, "dk", DomainKeysTypes.DKIM)

' Send the message via SMTP server (authentication is enabled).
mailer.SmtpServers.Add("mail.company.com", "john.doe", "secret")
mailer.Send()

We assume that MailBee, MailBee.Mime, MailBee.SmtpMail and MailBee.Security namespaces are already imported, MailBee.Global.LicenseKey is specified (directly in the code, or in app.config or web.config file). This license key must include MailBee.NET SMTP and MailBee.NET Security licenses (usually, MailBee.NET Objects unified key).

The sample above loaded the certificates from files. You can also load certificates from memory, or from certificate stores which in turn can be located in Windows Registry (also called System store), files or LDAP directory.

Note that in case of a web application, you cannot use System store unless you've added all the required certificates into the System store of the user whose context is used to run your ASP.NET application.

Let's say your private certificate resides in Personal folder of System store (you can find it at Personal tab in "Internet Options/Content/Certificates" dialog of Internet Explorer). The sample below uses S/MIME to sign the message with the private key taken from System/Personal storage and sends the e-mail out:

// Open the System store (it actually resides in Windows Registry).
CertificateStore store = new CertificateStore(CertificateStore.Personal,
    CertStoreType.System, null);

// Find the certificates of john.doe@company.com
CertificateCollection certs = store.FindCertificates("john.doe@company.com",
    CertificateFields.EmailAddress);

// We can now close the certificate store (it's unmanaged resource,
// that's why we must do this manually to avoid having the resource
// opened until GC eventually frees it).
store.Dispose();

if (certs.Count == 0)
{
    Console.WriteLine("Certificate not found in the store");
    return;
}

Smtp mailer = new Smtp();

// Set the message properties.
mailer.Message.From = new EmailAddress("john.doe@company.com", "John Doe");
mailer.Message.To.Add("jane.doe@example.com", "Jane Doe");
mailer.Message.Subject = "Hello";
mailer.Message.BodyPlainText = "Hello, Jane, can we meet today?";

// Sign the message with S/MIME.
Smime secureMime = new Smime();
mailer.Message = secureMime.Sign(mailer.Message, certs[0]);

// Send the message via SMTP server (authentication is enabled).
mailer.SmtpServers.Add("mail.company.com", "john.doe", "secret");
mailer.Send();
' Open the System store (it actually resides in Windows Registry).
Dim store As CertificateStore = New CertificateStore(CertificateStore.Personal, _
    CertStoreType.System, Nothing)

' Find the certificates of john.doe@company.com
Dim certs As CertificateCollection = store.FindCertificates("john.doe@company.com", _
    CertificateFields.EmailAddress)

' We can now close the certificate store (it's unmanaged resource,
' that's why we must do this manually to avoid having the resource
' opened until GC eventually frees it).
store.Dispose()

If certs.Count = 0 Then
    Console.WriteLine("Certificate not found in the store")
    Return
End If

Dim mailer As Smtp = New Smtp()

' Set the message properties.
mailer.Message.From = New EmailAddress("john.doe@company.com", "John Doe")
mailer.Message.To.Add("jane.doe@example.com", "Jane Doe")
mailer.Message.Subject = "Hello"
mailer.Message.BodyPlainText = "Hello, Jane, can we meet today?"

' Sign the message with S/MIME.
Dim secureMime As Smime = New Smime()
mailer.Message = secureMime.Sign(mailer.Message, certs(0))

' Send the message via SMTP server (authentication is enabled).
mailer.SmtpServers.Add("mail.company.com", "john.doe", "secret")
mailer.Send()

Sign and encrypt e-mail with S/MIME to ensure privacy and security

In order to encrypt an e-mail, you need to make sure your recipients will be able to decrypt it. This makes encryption and decryption procedure quite different from the signing and verifying the signature.

When you sign the message, you use your private key. But when you encrypt it, you use the public key of your recipient. If there are many recipients, you'll need to obtain the public key of every recipient.

When the recipient receives a signed message, he/she uses the public key encapsulated into the message to verify it. In other words, signing does not require the opposite party to have any security keys in order to complete the signature verification process.

When the recipient receives an encrypted message, he/she uses one's private key to decrypt it. Of course, this private key must match the public key this person previously gave you to let you encrypt e-mails for this person.

No need to transfer the public key from the recipient to the sender only via a secure channel, you can even publish it on the web. Only private keys must be kept in safety.

The sample below creates an e-mail, signs it with your private key taken from Personal folder of System store, encrypts it with the public keys of two people, and sends it out. For demonstration purposes, we take one public key from a file and another public key from your Address Book that resides in System store:

// Open Personal folder of System store.
CertificateStore store = new CertificateStore(CertificateStore.Personal,
    CertStoreType.System, null);

// Find the certificates of john.doe@company.com (sender).
CertificateCollection certs = store.FindCertificates("john.doe@company.com",
    CertificateFields.EmailAddress);

// Close the certificate store to free unmanaged resources.
store.Dispose();

if (certs.Count == 0)
{
    Console.WriteLine("Sender's certificate not found in the store");
    return;
}

Certificate senderCert = certs[0];

// Open Address Book.
store = new CertificateStore(CertificateStore.OtherPeople,
    CertStoreType.System, null);

// Find the certificates of jane.doe@example.com (one of recipients).
certs = store.FindCertificates("jane.doe@example.com",
    CertificateFields.EmailAddress);

// Close the certificate store to free unmanaged resources.
store.Dispose();

if (certs.Count == 0)
{
    Console.WriteLine("Recipient's certificate not found in the store");
    return;
}

// The public key of the recipient from Address Book.
Certificate recipientCert1 = certs[0];

// Load the public key of another recipient from .CER file.
Certificate recipientCert2 = new Certificate(@"C:\Docs\alice.cer",
    CertFileType.Cer, null);

// Make up the list of public certificates of all recipients.
certs.Clear();
certs.Add(recipientCert1);
certs.Add(recipientCert2);

Smtp mailer = new Smtp();

// Set the message properties (and demonstrate different methods
// of setting email addresses in From and To).
mailer.Message.From = new EmailAddress("john.doe@company.com", "John Doe");
mailer.Message.To.Add("jane.doe@example.com", "Jane Doe");
mailer.Message.To.AddFromString("Alice <alice@there.com>");
mailer.Message.Subject = "Hello";
mailer.Message.BodyPlainText = "Hello, Jane and Alice, can we meet today?";

// Sign and encrypt the message with S/MIME.
Smime secureMime = new Smime();
mailer.Message = secureMime.SignAndEncrypt(mailer.Message, senderCert, certs);

// Send the message via SMTP server (authentication is enabled).
mailer.SmtpServers.Add("mail.company.com", "john.doe", "secret");
mailer.Send();
' Open Personal folder of System store.
Dim store As CertificateStore = New CertificateStore(CertificateStore.Personal, _
    CertStoreType.System, Nothing)

' Find the certificates of john.doe@company.com (sender).
Dim certs As CertificateCollection = store.FindCertificates("john.doe@company.com", _
    CertificateFields.EmailAddress)

' Close the certificate store to free unmanaged resources.
store.Dispose()

If certs.Count = 0 Then
    Console.WriteLine("Sender's certificate not found in the store")
    Return
End If

Dim senderCert As Certificate = certs(0)

' Open Address Book.
store = New CertificateStore(CertificateStore.OtherPeople, _
    CertStoreType.System, Nothing)

' Find the certificates of jane.doe@example.com (one of recipients).
certs = store.FindCertificates("jane.doe@example.com", _
    CertificateFields.EmailAddress)

' Close the certificate store to free unmanaged resources.
store.Dispose()

If certs.Count = 0 Then
    Console.WriteLine("Recipient's certificate not found in the store")
    Return
End If

' The public key of the recipient from Address Book.
Dim recipientCert1 As Certificate = certs(0)

' Load the public key of another recipient from .CER file.
Dim recipientCert2 As Certificate = New Certificate("C:\Docs\alice.cer", _
    CertFileType.Cer, Nothing)

' Make up the list of public certificates of all recipients.
certs.Clear()
certs.Add(recipientCert1)
certs.Add(recipientCert2)

Dim mailer As Smtp = New Smtp()

' Set the message properties (and demonstrate different methods
' of setting email addresses in From and To).
mailer.Message.From = New EmailAddress("john.doe@company.com", "John Doe")
mailer.Message.To.Add("jane.doe@example.com", "Jane Doe")
mailer.Message.To.AddFromString("Alice <alice@there.com>")
mailer.Message.Subject = "Hello"
mailer.Message.BodyPlainText = "Hello, Jane and Alice, can we meet today?"

' Sign and encrypt the message with S/MIME.
Dim secureMime As Smime = New Smime()
mailer.Message = secureMime.SignAndEncrypt(mailer.Message, senderCert, certs)

' Send the message via SMTP server (authentication is enabled).
mailer.SmtpServers.Add("mail.company.com", "john.doe", "secret")
mailer.Send()

To view which certificates you have in Address Book, open Other People tab in "Internet Options/Content/Certificates" dialog of Internet Explorer. However, this does not make sense for a web application as it usually runs on another computer (web server). And even when you run the web application on your own computer, it's usually executed under the context of a system user, not your own user.


Send feedback to AfterLogic

Copyright © 2006-2017 AfterLogic Corporation. All rights reserved.