Sending Encrypted E-Mails in C#
So I was faced with the problem of sending an encrypted email to a group of people. Not really thinking, I responded with sure we can do that no problem. Getting back to my desk I started working on the project to find out that it turns out to be more difficult then I had thought.
Doing some internet searches, I found several commercial products that allow you to encrypt emails. I also found several discussions where people got close to what they were after, most were just after digital signatures without attachments. Nothing close to a full package.
After a few hours worth of digging I finally was able to come up with a solution that met my needs, multiple addressees and multiple attachments. Below is the code, note that you need to add a reference to System.Security in your Visual Studio Project to compile this code.
using System;
using System.Text;
using System.Net.Mail;
using System.IO;
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography.X509Certificates;
namespace CommonUtilities
{
//requires reference to System.Security
class EmailUtil
{
public static void SendEncryptedEmail(string[] to, string from, string subject, string body, string[] attachments)
{
MailMessage message = new MailMessage();
message.From = new MailAddress(from);
message.Subject = subject;
if (attachments != null && attachments.Length > 0)
{
StringBuilder buffer = new StringBuilder();
buffer.Append("MIME-Version: 1.0\r\n");
buffer.Append("Content-Type: multipart/mixed; boundary=unique-boundary-1\r\n");
buffer.Append("\r\n");
buffer.Append("This is a multi-part message in MIME format.\r\n");
buffer.Append("--unique-boundary-1\r\n");
buffer.Append("Content-Type: text/plain\r\n"); //could use text/html as well here if you want a html message
buffer.Append("Content-Transfer-Encoding: 7Bit\r\n\r\n");
buffer.Append(body);
if (!body.EndsWith("\r\n"))
buffer.Append("\r\n");
buffer.Append("\r\n\r\n");
foreach (string filename in attachments)
{
FileInfo fileInfo = new FileInfo(filename);
buffer.Append("--unique-boundary-1\r\n");
buffer.Append("Content-Type: application/octet-stream; file=" + fileInfo.Name + "\r\n");
buffer.Append("Content-Transfer-Encoding: base64\r\n");
buffer.Append("Content-Disposition: attachment; filename=" + fileInfo.Name + "\r\n");
buffer.Append("\r\n");
byte[] binaryData = File.ReadAllBytes(filename);
string base64Value = Convert.ToBase64String(binaryData, 0, binaryData.Length);
int position = 0;
while (position < base64Value.Length)
{
int chunkSize = 100;
if (base64Value.Length - (position + chunkSize) < 0)
chunkSize = base64Value.Length - position;
buffer.Append(base64Value.Substring(position, chunkSize));
buffer.Append("\r\n");
position += chunkSize;
}
buffer.Append("\r\n");
}
body = buffer.ToString();
}
else
{
body = "Content-Type: text/plain\r\nContent-Transfer-Encoding: 7Bit\r\n\r\n" + body;
}
byte[] messageData = Encoding.ASCII.GetBytes(body);
ContentInfo content = new ContentInfo(messageData);
EnvelopedCms envelopedCms = new EnvelopedCms(content);
CmsRecipientCollection toCollection = new CmsRecipientCollection();
foreach (string address in to)
{
message.To.Add(new MailAddress(address));
X509Certificate2 certificate = null; //Need to load from store or from file the client's cert
CmsRecipient recipient = new CmsRecipient(SubjectIdentifierType.SubjectKeyIdentifier, certificate);
toCollection.Add(recipient);
}
envelopedCms.Encrypt(toCollection);
byte[] encryptedBytes = envelopedCms.Encode();
//add digital signature:
SignedCms signedCms = new SignedCms(new ContentInfo(encryptedBytes));
X509Certificate2 signerCertificate = null; //Need to load from store or from file the signer's cert
CmsSigner signer = new CmsSigner(SubjectIdentifierType.SubjectKeyIdentifier, signerCertificate);
signedCms.ComputeSignature(signer);
encryptedBytes = signedCms.Encode();
//end digital signature section
MemoryStream stream = new MemoryStream(encryptedBytes);
AlternateView view = new AlternateView(stream, "application/pkcs7-mime; smime-type=signed-data;name=smime.p7m");
message.AlternateViews.Add(view);
SmtpClient client = new SmtpClient("your.smtp.mailhost");
//add authentication info if required by your smtp server etc...
//client.Credentials = CredentialCache.DefaultCredentials;
client.Send(message);
}
}
}
This should get you pretty much everything you need. The only thing left is to load the certificates from somewhere. I used a function to load them from the current users certificate store on the machine. I have a previous blog posting on how to load the certificates from the store. The trick comes in to fetching the certificates for unknown parties etc.
Comments
Anonymous
August 20, 2008
Link Listing - August 19, 2008Anonymous
January 08, 2015
Hi, Great blog! It`s quite a problem to find this topic so well documented. Even I was looking for Powershell script, this helped me a lot. Thank you.Anonymous
April 22, 2015
Same here. We were looking for some VB.net code to do the attachment part, and it's so simple to translate this complete example. Thank you.