在 MimeKit 上签名和加密

Nek*_*ehl 5 mimekit

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

\n\n

我尝试过使用 OpaqueMail 和 MimeKit。\n因为我确实不太了解 OpaqueMail 并且我有自己的客户端来检索电子邮件,所以我发现更好地理解和实现 MimeKit。

\n\n

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

\n\n
try\n    {\n            X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);\n            store.Open(OpenFlags.ReadOnly);\n\n            X509Certificate2Collection collection = store.Certificates.Find(X509FindType.FindBySubjectName, "senderEmail@something.com", false); //TODO Change to true after test\n            X509Certificate2 senderCertificate = collection[0];\n\n            store = new X509Store(StoreName.AddressBook, StoreLocation.CurrentUser);\n            store.Open(OpenFlags.ReadOnly);\n\n            collection = store.Certificates.Find(X509FindType.FindBySubjectName, "recipientEmail@something.com", false); //TODO Change to true after test\n            X509Certificate2 recipientCertificate = collection[0];\n\n            MimeMessage mimeMessage = new MimeMessage\n            {\n                Date = DateTime.Now,\n            };\n\n            mimeMessage.From.Add(\n                new SecureMailboxAddress(\n                    "senderEmail@gmail.com",\n                    "senderEmail@gmail.com",\n                    senderCertificate.Thumbprint));\n\n            mimeMessage.To.Add(\n                new SecureMailboxAddress(\n                    "recipientEmail@gmail.com",\n                    "recipientEmail@gmail.com",\n                    recipientCertificate.Thumbprint));\n\n            mimeMessage.Subject = "S/MIME Test";\n\n            using (Stream stream = "TestAttachmentFile".ToStream())\n            {\n                //Attachment\n                MimePart attachment = new MimePart(new ContentType("text", "plain"))\n                                      {\n                                          ContentTransferEncoding =\n                                              ContentEncoding.Base64,\n                                          ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),\n                                          FileName = "TestAttachmentFileName.txt",\n                                          ContentObject = new ContentObject(stream)\n                                      };\n\n                Multipart multipart = new Multipart("mixed") { attachment};\n\n                mimeMessage.Body = multipart;\n\n                //Sign / Encryption\n                CmsSigner signer = new CmsSigner(senderCertificate);\n\n                CmsRecipientCollection colle = new CmsRecipientCollection();\n                X509Certificate bountyRecipientCertificate = DotNetUtilities.FromX509Certificate (recipientCertificate)\n\n                CmsRecipient recipient = new CmsRecipient(bountyRecipientCertificate);\n                colle.Add(recipient);\n\n                using (var ctx = new MySecureMimeContext ()) \n                {\n                      var signed = MultipartSigned.Create (ctx, signer, mimeMessage.Body);        \n                      var encrypted = ApplicationPkcs7Mime.Encrypt (ctx, colle, signed);           \n                      mimeMessage.Body = MultipartSigned.Create (ctx, signer, encrypted);\n                }\n\n                //Sending\n                using (SmtpClient smtpClient = InitSmtpClient(\n                    "mail.smtp.com",\n                    465,\n                    "sender@something.com",\n                    "Pwd",\n                    true))\n                {\n                    smtpClient.Send(mimeMessage);\n                }\n            }\n        }\n        catch (Exception e)\n        {\n            Console.WriteLine(e);\n        }\n
Run Code Online (Sandbox Code Playgroud)\n\n

那么问题来了:

\n\n

签名和正文加密有效。但是当我尝试添加附件时,我可以打开它,但总是出现 BOM (\xc3\xaf\xc2\xbb\xc2\xbf) 我可以看到附件,但是雷鸟并没有告诉我它是附件,它\ 就像电子邮件的正文一样。我不知道这是否是我实现的 ToStream() 的问题。Thunderbird 也无法显示正确的德语字符 (\xc3\x9c\xc3\xbc\xc3\x96\xc3\xb6\xc3\x84\xc3\xa4\xc3\x9f),也无法显示西班牙语 \xc3\xb1

\n\n

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

\n\n
\n\n

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

\n\n

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

\n\n

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

\n\n
\n\n

如何像参数 CmsRecipient 一样从 X509Certificate2 转换为 X509Certificate(BouncyCastle)?

\n\n

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

\n\n
\n\n

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

\n\n

编辑,是的

\n\n
using (var ctx = new MySecureMimeContext ()) {\n    var signed = MultipartSigned.Create (ctx, signer, mimeMessage.Body);\n    var encrypted = ApplicationPkcs7Mime.Encrypt (ctx, colle, signed);\n    mimeMessage.Body = MultipartSigned.Create (ctx, signer, encrypted);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

jst*_*ast 4

听起来您现在已经知道如何使用DotNetUtilities.FromX509Certificate().

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

我推荐的是这样的:

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

或者:

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

或者:

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);
}
Run Code Online (Sandbox Code Playgroud)

您可能想要尝试所有这 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 证书存储中,否则我会避免这样做。