This seems like a tough one to find a good answer to. I want to create a mail message, add attachments to it, encrypt it using a X509Certificate2 certificate, and then send it. Sounds simple enough, right?! I use asp.net mvc and C#.
This is what I have so far.
string sMTPClient = ConfigurationManager.AppSettings.Get("SMTPClient");
using (var smtpClient = new SmtpClient(sMTPClient))
{
var attachments = MethodToCreateMailAttachments(......);
X509Certificate2 certificate = MethodToGetCertificateBySerialNumber("xxxxxxx");
using (var finalMailmessage = new MailMessage())
{
var encryptedMailMessage = new MailMessage();
var encryptCert = new X509Certificate2(certificate);
encryptedMailMessage.Subject = mailsubject;
encryptedMailMessage.Body = mailBody;
if (attachments.Any())
{
foreach (var item in attachments)
encryptedMailMessage.Attachments.Add(item);
}
byte[] encryptedBodyBytes = Encoding.ASCII.GetBytes(encryptedMailMessage.ToString());
EnvelopedCms Envelope = new EnvelopedCms(new ContentInfo(encryptedBodyBytes));
CmsRecipient Recipient = new CmsRecipient(SubjectIdentifierType.IssuerAndSerialNumber, encryptCert);
Envelope.Encrypt(Recipient);
byte[] EncryptedBytes = Envelope.Encode();
//Attach the encrypted message as an alternate view.
MemoryStream ms = new MemoryStream(EncryptedBytes);
AlternateView av = new AlternateView(ms, "application/pkcs7-mime; smime-type=signed-data;name=smime.p7m");
finalMailmessage.AlternateViews.Add(av);
finalMailmessage.From = new MailAddress(mailFrom);
foreach (var address in mailTo.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries))
{
finalMailmessage.To.Add(address);
}
var smtp = new SmtpClient(sMTPClient);
smtp.Send(finalMailmessage);
finalMailmessage.Dispose();
ErrorLogging.log.Debug("Mailmessage sent");
return "";
}
}
What this does is create two MailMessages, one for the things that need to be encrypted, attachments, body and subject. Then I create the message that will be sent. To this I add the first message as an alternate view. This works so far as to encrypt and send the email, and on the recieving end, I get an email with a padlock icon in Outlook.
I can then open the message in Outlook, by importing the certificate. This works. However, next to the padlock icon, in Outlook, I get the attachment paperclip icon, which suggests that there is something attached to the message. But the message is empty. So nothing gets attached apparently. I suspect the adding of the encrypted MailMessage as an alternate view to the other MailMessage, is where I have gone wrong.
I've tried a lot of other things with no luck, and this is the closest I have come to a working solution. I need some input, so does anyone have any suggestions?
I found a working solution. The problem was this part:
byte[] encryptedBodyBytes = Encoding.ASCII.GetBytes(encryptedMailMessage.ToString());
I had to create a memorystream, which can then be converted into a byte array. I used a tool called "MimeKit", which can be installed as a nuget package.
So, instead I have:
var memStream = new MemoryStream();
var mimeMessage = MimeMessage.CreateFromMailMessage(encryptedMailMessage);
mimeMessage.WriteTo(memStream);
var messageString = Encoding.UTF8.GetString(memoryStream.ToArray());
byte[] encryptedBodyBytes = Encoding.ASCII.GetBytes(messageString);
The rest is the same.