I was required to send signed and encrypted mails to our customers, however, this is my first time with the fight of sign and encrypt (I want to highlight this point).
I have tried with OpaqueMail, and MimeKit. Because I really don't understand deeply OpaqueMail and I have my own clients to retrieve emails, I found far better to understand and implement MimeKit.
I know it is a rudimentary implementation what I did in the following lines, but it is just the first contact with it and just a test. I can send signed emails with encrypted body, the problem come with the attachments (we just sent empty bodies with the attachment file, that comes from a DB).
try
{
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection collection = store.Certificates.Find(X509FindType.FindBySubjectName, "senderEmail@something.com", false); //TODO Change to true after test
X509Certificate2 senderCertificate = collection[0];
store = new X509Store(StoreName.AddressBook, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
collection = store.Certificates.Find(X509FindType.FindBySubjectName, "recipientEmail@something.com", false); //TODO Change to true after test
X509Certificate2 recipientCertificate = collection[0];
MimeMessage mimeMessage = new MimeMessage
{
Date = DateTime.Now,
};
mimeMessage.From.Add(
new SecureMailboxAddress(
"senderEmail@gmail.com",
"senderEmail@gmail.com",
senderCertificate.Thumbprint));
mimeMessage.To.Add(
new SecureMailboxAddress(
"recipientEmail@gmail.com",
"recipientEmail@gmail.com",
recipientCertificate.Thumbprint));
mimeMessage.Subject = "S/MIME Test";
using (Stream stream = "TestAttachmentFile".ToStream())
{
//Attachment
MimePart attachment = new MimePart(new ContentType("text", "plain"))
{
ContentTransferEncoding =
ContentEncoding.Base64,
ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),
FileName = "TestAttachmentFileName.txt",
ContentObject = new ContentObject(stream)
};
Multipart multipart = new Multipart("mixed") { attachment};
mimeMessage.Body = multipart;
//Sign / Encryption
CmsSigner signer = new CmsSigner(senderCertificate);
CmsRecipientCollection colle = new CmsRecipientCollection();
X509Certificate bountyRecipientCertificate = DotNetUtilities.FromX509Certificate (recipientCertificate)
CmsRecipient recipient = new CmsRecipient(bountyRecipientCertificate);
colle.Add(recipient);
using (var ctx = new MySecureMimeContext ())
{
var signed = MultipartSigned.Create (ctx, signer, mimeMessage.Body);
var encrypted = ApplicationPkcs7Mime.Encrypt (ctx, colle, signed);
mimeMessage.Body = MultipartSigned.Create (ctx, signer, encrypted);
}
//Sending
using (SmtpClient smtpClient = InitSmtpClient(
"mail.smtp.com",
465,
"sender@something.com",
"Pwd",
true))
{
smtpClient.Send(mimeMessage);
}
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
Well here the questions:
Sign and body encrypt works. But when I try to add an attachment I can open it but always appear the BOM () I can see the attachment however thunderbird doesn't tell me it is an attachment, it's just like the body of the email. I don't know if it is a problem from the ToStream() I have implemented. Also Thunderbird was not able to show correct german characters (ÜüÖöÄäß) neither for spanish ñ
EDIT MimeKit.Decryption methods works pretty well too, and also I get the correct encoding of the message without BOM and the attachment is there. It could be a problem for Thunderbird clients.
Related to SecureMimeContext, we are using HanaDB and we want to store the certificates there, retrieve and use them, but I was not able to find the proper conversion for IX509CertificateDatabase, so, using WindowsStore for the moment.
EDIT, I solve the problem with the DB creating a WindowsSecureMimeContext and overriding the import to get the certificates from the DB. Quick and dirty.
EDIT 2, This was hard to implement, because our implementation with DAO templates, I've made subclass from SecureMimeContext, I look on the WindowsSecureMimeContext to understand what the methods exactly does and just change the code to fit with our DAO stuff.
How can I convert from X509Certificate2 to X509Certificate(BouncyCastle) as is the parameter CmsRecipient?
EDIT, DotNetUtilities.FromX509Certificate did the job.
Is it possible to make a "Tripple Wrap"? Sign, Encrypt, Sign again.
EDIT, Yes
using (var ctx = new MySecureMimeContext ()) {
var signed = MultipartSigned.Create (ctx, signer, mimeMessage.Body);
var encrypted = ApplicationPkcs7Mime.Encrypt (ctx, colle, signed);
mimeMessage.Body = MultipartSigned.Create (ctx, signer, encrypted);
}