【问题标题】:Sign and Encrypt on MimeKit在 MimeKit 上签名和加密
【发布时间】:2015-09-08 03:16:51
【问题描述】:

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

我尝试过使用 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 也无法显示正确的德语字符(ÜüÖöÄäß),西班牙语 ñ

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

【问题讨论】:

    标签: mimekit


    【解决方案1】:

    听起来你现在大部分时间都在使用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 证书存储中。

    【讨论】:

    • 好的,感谢您的提示,我将全天更新问题,为可能有相同问题的人提供“最终”代码。
    • SecureMimeContext 的部分很难实现,我创建了一个子类 HanaSecureMimeContext : SecureMimeContext。我使用 WindowsSecureMimeContext 只是为了了解这些方法的作用并将其与我们的 DAO 模板相匹配。
    • 与 Triple wrap 相关,将由 MimeKit 解析,我已经测试了 3 个选项:在第一个解决方案中,我只有一个 BodyPart 并且是签名,我不是能够检索除签名以外的任何附件。
    • 第二种和第三种解决方案的工作方式相同我得到了两部分,一个签名一个在我解密时加密我得到了第三部分的签名,或者至少是我相信的以下结构 multipart/替代(签名)文本/纯多部分/替代(加密)文本/纯多部分/替代(签名)
    • 啊,是的,你是对的。这就是 SignAndEncrypt() 在内部所做的:return Encrypt (ctx, recipients, MultipartSigned.Create (ctx, signer, entity));
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多