4

我被要求向我们的客户发送签名和加密的邮件,然而,这是我第一次与签名和加密的斗争(我想强调这一点)。

我尝试过使用 OpaqueMail 和 MimeKit。因为我对 OpaqueMail 的理解并不深入,而且我有自己的客户端来检索电子邮件,所以我发现理解和实施 MimeKit 要好得多。

我知道这是我在以下几行中所做的基本实现,但这只是第一次接触它,只是一个测试。我可以发送带有加密正文的签名电子邮件,问题来自附件(我们只是发送了带有附件文件的空正文,来自数据库)。

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

那么这里的问题:

签名和正文加密工作。但是当我尝试添加附件时,我可以打开它,但总是出现 BOM () 我可以看到附件,但是 Thunderbird 并没有告诉我它是一个附件,它就像电子邮件的正文一样。我不知道这是否是我实施的 ToStream() 的问题。此外,Thunderbird 无法显示正确的德语字符 (ÜüÖöÄäß),西班牙语 ñ

编辑 MimeKit.Decryption 方法也工作得很好,而且我得到了没有 BOM 的消息的正确编码并且附件在那里。对于 Thunderbird 客户端来说,这可能是个问题。


与 SecureMimeContext 相关,我们正在使用 HanaDB,我们希望将证书存储在那里、检索和使用它们,但我无法找到 IX509CertificateDatabase 的正确转换,所以暂时使用 WindowsStore。

编辑,我解决了数据库创建 WindowsSecureMimeContext 并覆盖导入以从数据库获取证书的问题。又快又脏。

编辑 2,这很难实现,因为我们使用 DAO 模板实现,我从 SecureMimeContext 创建了子类,我查看 WindowsSecureMimeContext 以了解这些方法的确切作用,并更改代码以适应我们的 DAO 内容。


我如何将 X509Certificate2 转换为 X509Certificate(BouncyCastle),就像参数 CmsRecipient 一样?

编辑,DotNetUtilities.FromX509Certificate 完成了这项工作。


是否可以制作“三重包裹”?签名,加密,再次签名。

编辑,是的

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

1 回答 1

3

听起来你现在大部分时间都在使用DotNetUtilities.FromX509Certificate().

看起来你最后剩下的问题是关于如何“三重包装”。

我推荐的是这样的:

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

或者:

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

或者:

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

您可能想尝试所有这 3 个选项,看看哪一个最适合您的客户使用的邮件客户端(Outlook、Thunderbird、Apple Mail?)。

ApplicationPkcs7Mime.SignAndEncrypt()使用该application/pkcs7-mime; smime-type=signed-data格式,然后对不同于加密的内容进行加密multipart/signed,不同的客户端可以处理不同程度的成功。

使用的一个很好的理由multipart/signed是即使用户的客户端无法解码 S/MIME,电子邮件仍然是可读的,因为它使用分离的签名,这意味着消息的原始文本没有封装在二进制签名数据中。但是......因为你也在加密,它可能没有什么区别。

与 SecureMimeContext 相关,我们正在使用 HanaDB,我们希望将证书存储在那里、检索和使用它们,但我无法找到 IX509CertificateDatabase 的正确转换,所以暂时使用 WindowsStore。

我建议您查看DefaultSecureMimeContext并实施您自己的IX509CertificateDatabase.

如果 HanaDB 是基于 SQL 的,您可能可以继承SqlCertificateDatabase它是一个抽象的基于 SQL 的证书数据库实现。您可以查看SqliteCertificateDatabase.csNpgsqlCertificateDatabase.cs(PostgreSQL) 以了解如何操作。

或者你可以X509CertificateDatabase.cs看看如何实现一个通用版本。

WindowsSecureMimeContext除非您实际上将证书存储在 Windows 证书存储中,否则我会诚实地避免。

于 2015-06-23T22:01:49.937 回答