在 MimeKit 上签名和加密
Sign and Encrypt on MimeKit
我被要求向我们的客户发送签名和加密的邮件,然而,这是我第一次进行签名和加密的斗争(我想强调这一点)。
我试过使用 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 () 我可以看到附件但是雷鸟没有告诉我它是附件,它就像电子邮件的正文一样。我不知道这是否是我实施的 ToStream() 的问题。此外,Thunderbird 无法显示正确的德语字符 (ÜüÖöÄäß),也无法显示西班牙语 ñ
EDIT MimeKit.Decryption 方法也很有效,而且我得到了没有 BOM 的正确消息编码,附件也在那里。这可能是 Thunderbird 客户端的问题。
与 SecureMimeContext 相关,我们正在使用 HanaDB,我们想在那里存储证书、检索和使用它们,但我找不到 IX509CertificateDatabase 的正确转换,因此,使用 WindowsStore那一刻。
编辑,我解决 数据库创建 WindowsSecureMimeContext 并覆盖导入以从数据库获取证书的问题。又快又脏。
编辑 2,这很难实现,因为我们使用 DAO 模板实现,我从 SecureMimeContext 创建了子类,我查看 WindowsSecureMimeContext 以了解这些方法的确切作用,只需更改代码以适应我们的需求DAO 的东西。
如何将 X509Certificate2 转换为 X509Certificate(BouncyCastle) 以及参数 CmsRecipient?
编辑,DotNetUtilities.FromX509Certificate 完成了这项工作。
可以做一个"Tripple Wrap"吗?签名,加密,再签名。
编辑,是
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);
}
既然你想出了使用 DotNetUtilities.FromX509Certificate()
。
听起来你已经完成了大部分工作
看来你剩下的最后一个问题是关于如何 "triple-wrap"。
我推荐的是:
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 因为它使用了分离签名,这意味着邮件的原始文本未封装在二进制签名数据中。但是......因为你也在加密,它可能没有什么不同。
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.
我建议您查看 DefaultSecureMimeContext
并实施您自己的 IX509CertificateDatabase
。
如果 HanaDB 是基于 SQL 的,您可能可以将 SqlCertificateDatabase
子类化,这是一个基于 SQL 的抽象证书数据库实现。您可以查看 SqliteCertificateDatabase.cs
或 NpgsqlCertificateDatabase.cs
(PostgreSQL) 以了解如何操作。
或者您可以查看 X509CertificateDatabase.cs
以了解如何实现通用版本。
老实说,我会避免 WindowsSecureMimeContext
除非您实际上将证书存储在 Windows 证书存储中。
我被要求向我们的客户发送签名和加密的邮件,然而,这是我第一次进行签名和加密的斗争(我想强调这一点)。
我试过使用 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 () 我可以看到附件但是雷鸟没有告诉我它是附件,它就像电子邮件的正文一样。我不知道这是否是我实施的 ToStream() 的问题。此外,Thunderbird 无法显示正确的德语字符 (ÜüÖöÄäß),也无法显示西班牙语 ñ
EDIT MimeKit.Decryption 方法也很有效,而且我得到了没有 BOM 的正确消息编码,附件也在那里。这可能是 Thunderbird 客户端的问题。
与 SecureMimeContext 相关,我们正在使用 HanaDB,我们想在那里存储证书、检索和使用它们,但我找不到 IX509CertificateDatabase 的正确转换,因此,使用 WindowsStore那一刻。
编辑,我解决 数据库创建 WindowsSecureMimeContext 并覆盖导入以从数据库获取证书的问题。又快又脏。
编辑 2,这很难实现,因为我们使用 DAO 模板实现,我从 SecureMimeContext 创建了子类,我查看 WindowsSecureMimeContext 以了解这些方法的确切作用,只需更改代码以适应我们的需求DAO 的东西。
如何将 X509Certificate2 转换为 X509Certificate(BouncyCastle) 以及参数 CmsRecipient?
编辑,DotNetUtilities.FromX509Certificate 完成了这项工作。
可以做一个"Tripple Wrap"吗?签名,加密,再签名。
编辑,是
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);
}
既然你想出了使用 DotNetUtilities.FromX509Certificate()
。
看来你剩下的最后一个问题是关于如何 "triple-wrap"。
我推荐的是:
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 因为它使用了分离签名,这意味着邮件的原始文本未封装在二进制签名数据中。但是......因为你也在加密,它可能没有什么不同。
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.
我建议您查看 DefaultSecureMimeContext
并实施您自己的 IX509CertificateDatabase
。
如果 HanaDB 是基于 SQL 的,您可能可以将 SqlCertificateDatabase
子类化,这是一个基于 SQL 的抽象证书数据库实现。您可以查看 SqliteCertificateDatabase.cs
或 NpgsqlCertificateDatabase.cs
(PostgreSQL) 以了解如何操作。
或者您可以查看 X509CertificateDatabase.cs
以了解如何实现通用版本。
老实说,我会避免 WindowsSecureMimeContext
除非您实际上将证书存储在 Windows 证书存储中。