Sign and Encrypt on MimeKit

孤人 提交于 2019-12-24 03:14:14

问题


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);
}

回答1:


It sounds like you're most of the way there now that you figured out to use DotNetUtilities.FromX509Certificate().

Looks like your last remaining question is about how to "triple-wrap".

What I would recommend is this:

using (var ctx = new MySecureMimeContext ()) {
    var encrypted = ApplicationPkcs7Mime.SignAndEncrypt(ctx, signer, colle, mimeMessage.Body);
    mimeMessage.Body = ApplicationPkcs7Mime.Sign (ctx, signer, encrypted);
}

or:

using (var ctx = new MySecureMimeContext ()) {
    var encrypted = ApplicationPkcs7Mime.SignAndEncrypt(ctx, signer, colle, mimeMessage.Body);
    mimeMessage.Body = MultipartSigned.Sign (ctx, signer, encrypted);
}

or:

using (var ctx = new MySecureMimeContext ()) {
    var signed = MultipartSigned.Create (ctx, signer, mimeMessage.Body);
    var encrypted = ApplicationPkcs7Mime.Encrypt (ctx, colle, signed);
    mimeMessage.Body = MultipartSigned.Sign (ctx, signer, encrypted);
}

You might want to play around with all 3 of those options to see which one works best with the mail clients your customers use (Outlook, Thunderbird, Apple Mail?).

ApplicationPkcs7Mime.SignAndEncrypt() uses the application/pkcs7-mime; smime-type=signed-data format and then encrypts that which is different from encrypting a multipart/signed and different clients may handle those to various degrees of success.

A good reason for using multipart/signed is that the email will still be readable even if the user's client cannot decode S/MIME because it uses a detached signature which means that the original text of the message is not encapsulated within binary signature data. But... since you are encrypting as well, it might not make a difference.

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.

I would recommend taking a look at DefaultSecureMimeContext and implementing your own IX509CertificateDatabase.

If HanaDB is SQL-based, you could probably subclass SqlCertificateDatabase which is an abstract SQL-based certificate database implementation. You can take a look at SqliteCertificateDatabase.cs or NpgsqlCertificateDatabase.cs (PostgreSQL) to get a feel for how to do it.

Or you could take a look at X509CertificateDatabase.cs to see how to implement a generic version.

I would honestly avoid WindowsSecureMimeContext unless you are actually storing the certificates in the Windows certificate stores.



来源:https://stackoverflow.com/questions/30983883/sign-and-encrypt-on-mimekit

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!