S/MIME Tutorial
Applies to: Rebex Total Pack, Rebex Mail Pack, Rebex IMAP, Rebex POP3, Rebex EWS, Rebex Graph, Rebex SMTP, Rebex MSG
Table of content
Namespaces and assemblies
To be able to sign, validate, encrypt or decrypt S/MIME e-mail messages, several assemblies
are needed: Rebex.Mail.dll, Rebex.Common.dll and
Rebex.Networking.dll.
Rebex.Mail.dll contains classes that enable you to create,
read, process and save e-mail messages in MIME format using the MailMessage
and related
classes that reside in Rebex.Mail
namespace. Also, it contains the Rebex.Mime.Headers
namespace with a number of classes that represent mail message headers.
To gain access to all described functionality, reference the Rebex.Mail.dll and Rebex.Common.dll assemblies from your project and import the following namespaces in your source files:
using Rebex.Mail; using Rebex.Mime.Headers; using Rebex.Security.Certificates;
Imports Rebex.Mail Imports Rebex.Mime.Headers Imports Rebex.Security.Certificates
Creating a signed mail message
Creating a signed mail message is simple. To be able to sign it, we need a X509 certificate and an associated private key. The certificate is issued by a certification authority and is bound to a specific person. It is not secret - quite the opposite in fact, because the recipient needs your certificate to be able to validate your signature. To validate a signature, only the certificate is needed. To sign a message, you need the private key as well.
To sign a message, we can either load the certificate from a PKCS#12 encrypted file (.pfx or .p12 extensions) that contains the certificate and its private key, or retrieve it from the Windows certificate store. Other files that might contain certificates are .cer or .der, but these only contain the certificate without the private key, and cannot be used to sign mail.
Please note that only the message content is signed - this does NOT include the top-level headers, which are regarded as an envelope information anyone can read and modify.
// load the certificate and associated private key from a file Certificate signer = Certificate.LoadPfx("hugo.pfx", "password"); // create an instance of MailMessage MailMessage message = new MailMessage(); // set its properties to desired values message.From = "hugo@example.com"; message.To = "joe@example.com"; message.Subject = "This is a simple message"; message.BodyText = "Hello, Joe!"; message.BodyHtml = "Hello, <b>Joe</b>!"; // and sign it using Hugo's certificate message.Sign(signer);
' load the certificate and associated private key from a file Dim signer As Certificate = Certificate.LoadPfx("hugo.pfx", "password") 'create an instance of MailMessage Dim message As New MailMessage 'and set its properties to desired values message.From = New MailAddressCollection("hugo@example.com") message.To = New MailAddressCollection("joe@example.com") message.Subject = "This is a simple message" message.BodyText = "Hello, Joe!" message.BodyHtml = "Hello, <b>Joe</b>!" ' and sign it using Hugo's certificate message.Sign(signer)
And what if you wanted to load the certificate from your personal store instead of a disk file? Check out the next section.
Certificate stores
There are several built-in and well-known certificate stores in Windows. They might already contain certificates (both with or without a private key) used by Internet Explorer, Outlook Express or other applications. These stores are per-user and can be managed by a MMC snap-in called "Certificates" or using Internet Explorer - just select "Tools->Internet Options...->Content->Certificates..." from its menu.
These stores are accessible using our CertificateStore
class from
Rebex.Security.Certificates
namespace.
// first, open the user's personal certificate store CertificateStore my = new CertificateStore(CertificateStoreName.My); // search the store for certificates matching the desired e-mail address // (and make sure the certificate has an associated private key and is not expired) CertificateFindOptions options = CertificateFindOptions.HasPrivateKey | CertificateFindOptions.IsTimeValid; Certificate[] certificates = my.FindCertificatesForMailAddress("hugo@example.com", options); // checking whether we actually found any suitable certificate is a good idea if (certificates.Length == 0) throw new ApplicationException("Hugo's certificate was not found."); // and if we did, use it to sign an e-mail message, as seen in the previous section Certificate signer = certificates[0];
' first, open the user's personal certificate store Dim my As New CertificateStore(CertificateStoreName.My) ' search the store for certificates matching the desired e-mail address ' (and make sure the certificate has an associated private key and is not expired) Dim options As CertificateFindOptions = _ CertificateFindOptions.HasPrivateKey Or _ CertificateFindOptions.IsTimeValid Dim certificates() As Certificate = _ my.FindCertificatesForMailAddress("hugo@example.com", options) ' checking whether we actually found any suitable certificate is a good idea If certificates.Length = 0 Then Throw New ApplicationException("Hugo's certificate was not found.") End If ' and if we did, use it to sign an e-mail message, as seen in the previous section Dim signer As Certificate = certificates(0)
Validating signatures
Once you receive a signed mail message, you should validate the signature
to make sure the message was indeed composed by the signer.
The MailMessage
class has a method called ValidateSignature
to facilitate this. Be sure to check whether the message is indeed signed
(using the IsSigned
property), because it is not possible to validate
a signature of a message that doesn't have any, obviously.
// create an instance of MailMessage MailMessage message = new MailMessage(); // load the message from a local disk file message.Load("c:\\message.eml"); // validate the signature if the message is signed if (message.IsSigned) { MailSignatureValidity result = message.ValidateSignature(); if (result.Valid) Console.WriteLine("The message is signed and the signature is valid."); else Console.WriteLine("The message is signed, but the signature is not valid."); } else { Console.WriteLine("The message is not signed."); }
'create an instance of MailMessage Dim message As New MailMessage 'load the message from a local disk file message.Load("c:\message.eml") 'validate the signature if the message is signed If message.IsSigned Then Dim result As MailSignatureValidity = message.ValidateSignature() If result.Valid Then Console.WriteLine("The message is signed and the signature is valid.") Else Console.WriteLine("The message is signed, but the signature is not valid.") End If Else Console.WriteLine("The message is not signed.") End If
The MailSignatureValidity
class returned by ValidateSignature
also contains various flags that can be used to determine why exactly the validation failed.
Consult the reference for more information.
Creating an encrypted mail message
Creating an encrypted message is similar to creating a signed message. However, to encrypt a message, only an X509 certificate is needed, and no private key. The private key is only needed to be able to decrypt the message. This means that anyone can enrypt a message for you, and only you are able to decrypt it.
To encrypt a message, we can either load the certificate from a certificate file (.cer or .der extensions), from a PKCS#12 encrypted file (.pfx or .p12 extensions) that also contains the private key (not needed for encryption though), or retrieve it from the Windows certificate store.
Please note that only the message content is encrypted - this does NOT include the top-level headers, which are regarded as an envelope information anyone can read and modify.
// load the certificates Certificate sender = Certificate.LoadDer("hugo.cer"); Certificate recipient = Certificate.LoadDer("joe.cer"); // create an instance of MailMessage MailMessage message = new MailMessage(); // set its properties to desired values message.From = "hugo@example.com"; message.To = "joe@example.com"; message.Subject = "This is a simple message"; message.BodyText = "Hello, Joe!"; message.BodyHtml = "Hello, <b>Joe</b>!"; // Encrypt it using both Joe's and Hugo's certificates. // When using an alternate message.Encrypt(recipient) call, // only the reciepient will be able to decrypt the message. // Following code will allow the sender to decrypt it later as well. message.Encrypt(recipient, sender);
' load the certificates and associated private key from a file Dim sender As Certificate = Certificate.LoadPfx("hugo.pfx", "password") Dim recipient As Certificate = Certificate.LoadPfx("joe.pfx", "password") 'create an instance of MailMessage Dim message As New MailMessage 'and set its properties to desired values message.From = New MailAddressCollection("hugo@example.com") message.To = New MailAddressCollection("joe@example.com") message.Subject = "This is a simple message" message.BodyText = "Hello, Joe!" message.BodyHtml = "Hello, <b>Joe</b>!" ' Encrypt it using both Joe's and Hugo's certificates. ' When using an alternate message.Encrypt(recipient) call, ' only the reciepient will be able to decrypt the message. ' Following code will allow the sender to decrypt it later as well. message.Encrypt(recipient, sender)
Once you encrypt the message using another person's certificate, you won't be able to decrypt it yourself. For this reason, encrypted messages are usually encrypted not only using the recipient's certificate, but the signer's certificate as well. Check out Signed and encrypted message section for more information
Decrypting an encrypted mail message
Once you receive an encrypted message, you have to decrypt it to be able to access its content.
The MailMessage
class has a Decrypt
method to facilitate this.
Be sure to check whether the message is indeed encrypted (using the IsEncrypted
property), because it is not possible to decrypt a message that has not been encrypted, obviously.
Also, you will only be able to decrypt the message if you have a private key for one of the certificates
for which the message was encrypted. Use the CanDecrypt
property to check whether decryption
is indeed possible. By default, MailMessage searches for the private key in your certificate store,
but you can make it use a .p12/.pfx file instead if needed.
Please note that you don't need to decrypt the message to be able to access its top-level headers, which are regarded as an envelope information anyone can read and modify.
// create an instance of MailMessage MailMessage message = new MailMessage(); // add this line if the private key to decrypt the message is not // in your certificate store, but in a .P12/.PFX file //Certificate certificate = Certificate.LoadPfx("PrivateKey.pfx", "password"); //message.CertificateFinder = CertificateFinder.CreateFinder(certificate); // load the message from a local disk file message.Load("c:\\message.eml"); // decrypt the message if it is encrypted if (message.IsEncrypted) { if (!message.CanDecrypt) throw new ApplicationException ( "Message cannot be decrypted. You do not have the private key." ); message.Decrypt(); } // you can access the message content now
'create an instance of MailMessage Dim message As New MailMessage 'add this line if the private key to decrypt the message is not 'in your certificate store, but in a .p12/.pfx file ' Dim certificate As Certificate = certificate.LoadPfx("c:\PrivateKey.pfx", "password") ' message.CertificateFinder = CertificateFinder.CreateFinder(Certificate) 'load the message from a local disk file message.Load("c:\message.eml") 'decrypt the message if it is encrypted If message.IsEncrypted Then If Not message.CanDecrypt Then Throw New ApplicationException _ ( _ "Message cannot be decrypted. You do not have the private key." _ ) End If message.Decrypt() End If 'you can access the message content now
Creating a message that is both encrypted and signed
Please read the Creating a signed message and Creating an encrypted message sections unless you are already familiar with these topics.
A message can be both encrypted and signed. This ensures to the sender that only the intended recipients are able to read the message content, and to the recipient that the message was indeed sent by the sender.
There are two ways to produce a message that is both encrypted and sign:
- Sign the message first, then encrypt the signed message.
- Encrypt the message first, then sign the encrypted message.
In the first case, no one but the recipient will be able to validate the signature and access the message content. In the second case, no one but the recipient will be able to access the message content, but anyone will be able to validate the signature. Both ways have their pros and cons, but there is a very strong reason to prefer the first way: Outlook Express does not handle the second way correctly, and reports the correct signature is not valid.
// load the sender's certificate and // associated private key from a file Certificate signer = Certificate.LoadPfx("hugo.pfx", "password"); // load the recipient's certificate Certificate recipient = Certificate.LoadDer("joe.cer"); // create an instance of MailMessage MailMessage message = new MailMessage(); // set its properties to desired values message.From = "hugo@example.com"; message.To = "joe@example.com"; message.Subject = "This is a simple message"; message.BodyText = "Hello, Joe!"; message.BodyHtml = "Hello, <b>Joe</b>!"; // sign the message using Hugo's certificate message.Sign(signer); // and encrypt it using Joe's certificate message.Encrypt(recipient); // if you wanted Hugo to be able to read the message later as well, // you can encrypt it for Hugo as well instead - comment out the previous // encrypt and uncomment this one: // message.Encrypt(recipient, signer)
'load the sender's certificate and 'associated private key from a file Dim signer As Certificate = Certificate.LoadPfx("hugo.pfg", "password") 'load the recipient's certificate Dim recipient As Certificate = Certificate.LoadDer("joe.cer") 'create an instance of MailMessage Dim message As New MailMessage 'and set its properties to desired values message.From = New MailAddressCollection("hugo@example.com") message.To = New MailAddressCollection("joe@example.com") message.Subject = "This is a simple message" message.BodyText = "Hello, Joe!" message.BodyHtml = "Hello, <b>Joe</b>!" 'sign the message using Hugo's certificate message.Sign(signer) 'and encrypt it using Joe's certificate message.Encrypt(recipient) 'if you wanted Hugo to be able to read the message later as well, 'you can encrypt it for Hugo as well instead - comment out the previous 'encrypt and uncomment this one: 'message.Encrypt(recipient, signer)
Once you receive a message that is both signed and encrypted, you can both decrypt it and validate the signature. Check out Decrypting an encrypted mail message and Validating signatures sections for details.
However, for a message that is signed first and then encrypted, you have to decrypt
it first to be able to validate the signature, because the signature is encrypted as well as
the message content.
In fact, unless a message is decrypted, we cannot know whether it is signed
as well or not, and the IsSigned
property is actually set to false
at this time.
Back to tutorial list...