我被要求向我们的客户发送签名和加密的邮件,但是,这是我第一次与签名和加密进行斗争(我想强调这一点)。
\n\n我尝试过使用 OpaqueMail 和 MimeKit。\n因为我确实不太了解 OpaqueMail 并且我有自己的客户端来检索电子邮件,所以我发现更好地理解和实现 MimeKit。
\n\n我知道这是我在以下几行中所做的一个基本实现,但这只是第一次接触它并且只是一个测试。我可以发送带有加密正文的签名电子邮件,问题出在附件上(我们刚刚发送了来自数据库的带有附件文件的空正文)。
\n\ntry\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 }\nRun 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与 SecureMimeContext 相关,我们正在使用 HanaDB,我们希望将证书存储在那里,检索和使用它们,但我无法找到 IX509CertificateDatabase 的正确转换,因此,暂时使用 WindowsStore。
\n\n编辑,我解决了数据库创建 WindowsSecureMimeContext 并覆盖导入以从数据库获取证书的问题。又快又脏。
\n\n编辑2,这很难实现,因为我们使用DAO模板实现,我已经从SecureMimeContext创建了子类,我查看WindowsSecureMimeContext以了解方法的确切用途,只需更改代码以适应我们的DAO内容。
\n\n如何像参数 CmsRecipient 一样从 X509Certificate2 转换为 X509Certificate(BouncyCastle)?
\n\n编辑,DotNetUtilities.FromX509Certificate 完成了这项工作。
\n\n是否可以制作“三重包装”?签名、加密、再次签名。
\n\n编辑,是的
\n\nusing (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}\nRun Code Online (Sandbox Code Playgroud)\n
听起来您现在已经知道如何使用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.cs或NpgsqlCertificateDatabase.cs(PostgreSQL) 来了解如何操作。
或者您可以查看X509CertificateDatabase.cs如何实现通用版本。
老实说,WindowsSecureMimeContext除非您实际上将证书存储在 Windows 证书存储中,否则我会避免这样做。
| 归档时间: |
|
| 查看次数: |
5498 次 |
| 最近记录: |