Send Encrypted Email From C#

Wednesday, 01 July 2009 01:21 PM
by Coose

Email is by-nature not secure.  Network administrators can view mail, and it can easily be seen on the wire.  In order to protect email, there are many methods out there, (PGP, etc).  What I want to address today is encrypting an email message using Asymmetric X.509 Cryptography.  What this means is that the sender will have the public key of the recipient.  This X.509 certificate is used to encrypt the email message body.  All senders will have the same public key.  But, by nature of asymmetric cryptography, only the holder of the private key can decrypt the message.  So, once something is encrypted with a public key, even the sender cannot decrypt the message.  Only the holder of the “other part” of the key (the private key) can decrypt the message.

Now, in order for this to work, a certificate capable of encrypting must be issued.  I won’t address that here.  So, given the subject of an installed X.509 certificate public key, the following class encrypts and sends the message.

(I actually used this class in a WPF sample application, so it is written with INotifyPropertyChanged events for data binding…but that part of the code is not necessary, obviously.)

    1 using System;

    2 using System.Collections.Generic;

    3 using System.Linq;

    4 using System.Web;

    5 using System.ComponentModel;

    6 using System.Security.Cryptography.X509Certificates;

    7 using System.Text;

    8 using System.Security.Cryptography.Pkcs;

    9 using System.IO;

   10 using System.Net.Mail;

   11 

   12 namespace Whatever

   13 {

   14   public class MailMessage : INotifyPropertyChanged

   15   {

   16     private string _to;

   17     private string _from;

   18     private string _subject;

   19     private string _body;

   20     private string _signingCertSubject;

   21     private string _encryptingCertSubject;

   22 

   23     public MailMessage()

   24     {

   25 

   26     }

   27 

   28     #region INotifyPropertyChanged Members

   29 

   30     public event PropertyChangedEventHandler PropertyChanged;

   31 

   32     protected void OnPropertyChanged(string propertyName)

   33     {

   34       if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

   35     }

   36 

   37     #endregion

   38 

   39     public string To

   40     {

   41       get { return _to; }

   42       set { _to = value; OnPropertyChanged("To"); }

   43     }

   44 

   45     public string From

   46     {

   47       get { return _from; }

   48       set { _from = value; OnPropertyChanged("From"); }

   49     }

   50 

   51     public string Subject

   52     {

   53       get { return _subject; }

   54       set { _subject = value; OnPropertyChanged("Subject"); }

   55     }

   56 

   57     public string Body

   58     {

   59       get { return _body; }

   60       set { _body = value; OnPropertyChanged("Body"); }

   61     }

   62 

   63     public string SigningCertSubject

   64     {

   65       get { return _signingCertSubject; }

   66       set { _signingCertSubject = value; OnPropertyChanged("SigningCertSubject"); }

   67     }

   68 

   69     public string EncryptingCertSubject

   70     {

   71       get { return _encryptingCertSubject; }

   72       set { _encryptingCertSubject = value; OnPropertyChanged("EncryptingCertSubject"); }

   73     }

   74 

   75     public void Send()

   76     {

   77       X509Certificate2 signing = null;

   78       X509Certificate2 encrypting = null;

   79 

   80       X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);

   81       store.Open(OpenFlags.ReadOnly);

   82       if (!string.IsNullOrEmpty(this.SigningCertSubject))

   83       {

   84         signing = store

   85           .Certificates

   86           .Find(X509FindType.FindBySubjectName,

   87             this.SigningCertSubject,

   88             false)

   89           .OfType<X509Certificate2>()

   90           .FirstOrDefault();

   91       }

   92       if (!string.IsNullOrEmpty(this.EncryptingCertSubject))

   93       {

   94         encrypting = store

   95           .Certificates

   96           .Find(X509FindType.FindBySubjectName,

   97             this.EncryptingCertSubject,

   98             false)

   99           .OfType<X509Certificate2>()

  100           .FirstOrDefault();

  101       }

  102 

  103       StringBuilder msg = new StringBuilder();

  104       msg.AppendLine("Content-Type: text/plain; charset=\"iso-8859-1\"");

  105       msg.AppendLine("Content-Transfer-Encoding: 7bit");

  106       msg.AppendLine();

  107       msg.AppendLine(this.Body);

  108       byte[] buffer = null;

  109       if (encrypting != null)

  110       {

  111         buffer = Encoding.ASCII.GetBytes(msg.ToString());

  112         EnvelopedCms cms = new EnvelopedCms(new ContentInfo(buffer));

  113         CmsRecipient recip = new CmsRecipient(SubjectIdentifierType.IssuerAndSerialNumber, encrypting);

  114         cms.Encrypt(recip);

  115         buffer = cms.Encode();

  116 

  117         if (signing != null)

  118         {

  119           SignedCms scms = new SignedCms(new ContentInfo(buffer));

  120           CmsSigner signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, signing);

  121 

  122           scms.ComputeSignature();

  123           buffer = scms.Encode();

  124         }

  125       }

  126       store.Close();

  127 

  128       System.Net.Mail.MailMessage mail = new System.Net.Mail.MailMessage();

  129       mail.To.Add(new System.Net.Mail.MailAddress(this.To));

  130       mail.From = new System.Net.Mail.MailAddress(this.From);

  131       mail.Subject = this.Subject;

  132 

  133       if (buffer != null)

  134       {

  135         MemoryStream ms = new MemoryStream(buffer);

  136         AlternateView v = new AlternateView(ms,

  137           "application/pkcs7-mime; smime-type=signed-data;name=smime.p7m");

  138         mail.AlternateViews.Add(v);

  139       }

  140       else

  141       {

  142         mail.Body = this.Body;

  143       }

  144 

  145       SmtpClient client = new SmtpClient();

  146       client.Send(mail);

  147     }

  148   }

  149 }

Now, the recipient of the email (who has the private X.509 key installed) can open this message in Outlook, or other capable email clients.  The private key must be accessible to the email client.

[Todo: insert Outlook screen shots]

Comment on this
Development
|

Add comment

  Country flag

biuquote
  • Comment
  • Preview
Loading