解析签名和加密的电子邮件
parsing signed and encrypted email
我正在编写一个应用程序,它必须能够读取签名和加密的电子邮件并解析其内容。对于仅加密的电子邮件,我能够使一切正常工作,但是当我收到一封也已签名的电子邮件时,我不知道该怎么办。一旦我解密了这封电子邮件,而不是在 Multipart 对象中有预期的 4 个部分,我在文件名为 smime.p7m 的 MimePart 对象中只有 1 个部分。我不知道如何分解这个文件或验证签名。我找到了有关验证签名 (http://www.mimekit.net/docs/html/Working-With-SMime.htm#Verify) 的文档,但我看不出这是如何做的。显然,目前我只是不理解某些事情。
下面是我正在使用的代码示例。请注意,这将在我弄清楚所有内容后进行重构,但到目前为止,这段代码对于我迄今为止测试过的所有未签名(可能加密也可能未加密)的电子邮件都可以正常工作。
public void decryptAndSendEmails()
{
List<EmailMessage> emails = getEmails();
foreach (var email in emails)
{
var decryptedEmailMessage = new EmailMessage(service);
MimeMessage message;
using (var stream = new MemoryStream(email.MimeContent.Content, false))
{
message = MimeMessage.Load(stream);
}
var pkcs7 = message.BodyParts.OfType<ApplicationPkcs7Mime>().FirstOrDefault();
if (pkcs7 != null)
{
//If the SecureMimeType has not been set as it should, set it to EnvelopedData
if (pkcs7.SecureMimeType == SecureMimeType.Unknown)
{
var content = new MemoryStream();
pkcs7.Content.DecodeTo(content);
content.Position = 0;
pkcs7 = new ApplicationPkcs7Mime(SecureMimeType.EnvelopedData, content);
}
using (var ctx = new TemporarySecureMimeContext())
{
using (var stream = File.OpenRead(ConfigurationManager.AppSettings["certLocation"]))
{
ctx.Import(stream, ConfigurationManager.AppSettings["certPassword"]);
}
var decrypted = pkcs7.Decrypt(ctx);
var decryptedParts = new List<MimePart>();
if (decrypted is Multipart)
{
decryptedParts = breakMultiPart((Multipart)decrypted);
}
else if (decrypted is MimePart)
{
decryptedParts.Add((MimePart)decrypted);
}
else
{
throw new InvalidOperationException("Unknown Mime part found");
}
var textParts = decryptedParts.Where(r => r is TextPart);
var htmlParts = textParts.Where(x => ((TextPart)x).IsHtml);
var textBodyParts = textParts.Where(x => !((TextPart)x).IsHtml);
var attachmentParts = decryptedParts.Where(r => !(r is TextPart));
if (htmlParts.Any())
{
if (htmlParts.Count() > 1)
{
throw new InvalidOperationException("multiple html body parts.");
}
var htmlPart = (TextPart)htmlParts.First();
decryptedEmailMessage.Body = new MessageBody(BodyType.HTML, htmlPart.Text);
}
else
{
//Text body
if (textBodyParts.Count() > 1)
{
throw new InvalidOperationException("multiple text body parts.");
}
var textPart = (TextPart)textBodyParts.First();
decryptedEmailMessage.Body = new MessageBody(BodyType.Text, textPart.Text);
}
foreach (var part in attachmentParts)
{
var content = new MemoryStream();
part.Content.DecodeTo(content);
content.Position = 0;
decryptedEmailMessage.Attachments.AddFileAttachment(part.FileName, content);
if (!part.IsAttachment)
{
decryptedEmailMessage.Attachments.First(r => r.Name == part.FileName).IsInline = true;
decryptedEmailMessage.Attachments.First(r => r.Name == part.FileName).ContentId = part.ContentId;
}
}
}
////do stuff with decrypted Email
}
else
{
//The email is not encrypted
decryptedEmailMessage = email;
//do stuff with decrypted Email
}
}
}
结合@jstedfast 的评论和我在 Unable to decrypt p7m using MimeKit 中找到的信息,我终于弄明白了这一点。以下是解决此问题的结果代码:
public void decryptAndSendEmails()
{
List<EmailMessage> emails = getEmails();
foreach (var email in emails)
{
var decryptedEmailMessage = new EmailMessage(service);
MimeMessage message;
using (var stream = new MemoryStream(email.MimeContent.Content, false))
{
message = MimeMessage.Load(stream);
}
var pkcs7 = message.BodyParts.OfType<ApplicationPkcs7Mime>().FirstOrDefault();
if (pkcs7 != null)
{
using (var ctx = new TemporarySecureMimeContext())
{
using (var stream = File.OpenRead(ConfigurationManager.AppSettings["certLocation"]))
{
ctx.Import(stream, ConfigurationManager.AppSettings["certPassword"]);
}
var decrypted = pkcs7.Decrypt(ctx);
if (decrypted != null && decrypted is MimePart && ((MimePart)decrypted).FileName == "smime.p7m")
{
//We need to verify the signature
var signedDecryptedEntity = decrypted as ApplicationPkcs7Mime;
signedDecryptedEntity.Verify(ctx, out decrypted); //the real decrypted data
}
var decryptedParts = new List<MimePart>();
if (decrypted is Multipart)
{
decryptedParts = breakMultiPart((Multipart)decrypted);
}
else if (decrypted is MimePart)
{
decryptedParts.Add((MimePart)decrypted);
}
else
{
throw new InvalidOperationException("Unknown Mime part found");
}
var textParts = decryptedParts.Where(r => r is TextPart);
var htmlParts = textParts.Where(x => ((TextPart)x).IsHtml);
var textBodyParts = textParts.Where(x => !((TextPart)x).IsHtml);
var attachmentParts = decryptedParts.Where(r => !(r is TextPart));
if (htmlParts.Any())
{
if (htmlParts.Count() > 1)
{
throw new InvalidOperationException("multiple html body parts.");
}
var htmlPart = (TextPart)htmlParts.First();
decryptedEmailMessage.Body = new MessageBody(BodyType.HTML, htmlPart.Text);
}
else
{
//Text body
if (textBodyParts.Count() > 1)
{
throw new InvalidOperationException("multiple text body parts.");
}
var textPart = (TextPart)textBodyParts.First();
decryptedEmailMessage.Body = new MessageBody(BodyType.Text, textPart.Text);
}
foreach (var part in attachmentParts)
{
var content = new MemoryStream();
part.Content.DecodeTo(content);
content.Position = 0;
decryptedEmailMessage.Attachments.AddFileAttachment(part.FileName, content);
if (!part.IsAttachment)
{
decryptedEmailMessage.Attachments.First(r => r.Name == part.FileName).IsInline = true;
decryptedEmailMessage.Attachments.First(r => r.Name == part.FileName).ContentId = part.ContentId;
}
}
}
//Do Something with email (decryptedEmailMessage)
}
else
{
//The email is not encrypted
decryptedEmailMessage = email;
//Do Something with email (decryptedEmailMessage)
}
}
}
我正在编写一个应用程序,它必须能够读取签名和加密的电子邮件并解析其内容。对于仅加密的电子邮件,我能够使一切正常工作,但是当我收到一封也已签名的电子邮件时,我不知道该怎么办。一旦我解密了这封电子邮件,而不是在 Multipart 对象中有预期的 4 个部分,我在文件名为 smime.p7m 的 MimePart 对象中只有 1 个部分。我不知道如何分解这个文件或验证签名。我找到了有关验证签名 (http://www.mimekit.net/docs/html/Working-With-SMime.htm#Verify) 的文档,但我看不出这是如何做的。显然,目前我只是不理解某些事情。
下面是我正在使用的代码示例。请注意,这将在我弄清楚所有内容后进行重构,但到目前为止,这段代码对于我迄今为止测试过的所有未签名(可能加密也可能未加密)的电子邮件都可以正常工作。
public void decryptAndSendEmails()
{
List<EmailMessage> emails = getEmails();
foreach (var email in emails)
{
var decryptedEmailMessage = new EmailMessage(service);
MimeMessage message;
using (var stream = new MemoryStream(email.MimeContent.Content, false))
{
message = MimeMessage.Load(stream);
}
var pkcs7 = message.BodyParts.OfType<ApplicationPkcs7Mime>().FirstOrDefault();
if (pkcs7 != null)
{
//If the SecureMimeType has not been set as it should, set it to EnvelopedData
if (pkcs7.SecureMimeType == SecureMimeType.Unknown)
{
var content = new MemoryStream();
pkcs7.Content.DecodeTo(content);
content.Position = 0;
pkcs7 = new ApplicationPkcs7Mime(SecureMimeType.EnvelopedData, content);
}
using (var ctx = new TemporarySecureMimeContext())
{
using (var stream = File.OpenRead(ConfigurationManager.AppSettings["certLocation"]))
{
ctx.Import(stream, ConfigurationManager.AppSettings["certPassword"]);
}
var decrypted = pkcs7.Decrypt(ctx);
var decryptedParts = new List<MimePart>();
if (decrypted is Multipart)
{
decryptedParts = breakMultiPart((Multipart)decrypted);
}
else if (decrypted is MimePart)
{
decryptedParts.Add((MimePart)decrypted);
}
else
{
throw new InvalidOperationException("Unknown Mime part found");
}
var textParts = decryptedParts.Where(r => r is TextPart);
var htmlParts = textParts.Where(x => ((TextPart)x).IsHtml);
var textBodyParts = textParts.Where(x => !((TextPart)x).IsHtml);
var attachmentParts = decryptedParts.Where(r => !(r is TextPart));
if (htmlParts.Any())
{
if (htmlParts.Count() > 1)
{
throw new InvalidOperationException("multiple html body parts.");
}
var htmlPart = (TextPart)htmlParts.First();
decryptedEmailMessage.Body = new MessageBody(BodyType.HTML, htmlPart.Text);
}
else
{
//Text body
if (textBodyParts.Count() > 1)
{
throw new InvalidOperationException("multiple text body parts.");
}
var textPart = (TextPart)textBodyParts.First();
decryptedEmailMessage.Body = new MessageBody(BodyType.Text, textPart.Text);
}
foreach (var part in attachmentParts)
{
var content = new MemoryStream();
part.Content.DecodeTo(content);
content.Position = 0;
decryptedEmailMessage.Attachments.AddFileAttachment(part.FileName, content);
if (!part.IsAttachment)
{
decryptedEmailMessage.Attachments.First(r => r.Name == part.FileName).IsInline = true;
decryptedEmailMessage.Attachments.First(r => r.Name == part.FileName).ContentId = part.ContentId;
}
}
}
////do stuff with decrypted Email
}
else
{
//The email is not encrypted
decryptedEmailMessage = email;
//do stuff with decrypted Email
}
}
}
结合@jstedfast 的评论和我在 Unable to decrypt p7m using MimeKit 中找到的信息,我终于弄明白了这一点。以下是解决此问题的结果代码:
public void decryptAndSendEmails()
{
List<EmailMessage> emails = getEmails();
foreach (var email in emails)
{
var decryptedEmailMessage = new EmailMessage(service);
MimeMessage message;
using (var stream = new MemoryStream(email.MimeContent.Content, false))
{
message = MimeMessage.Load(stream);
}
var pkcs7 = message.BodyParts.OfType<ApplicationPkcs7Mime>().FirstOrDefault();
if (pkcs7 != null)
{
using (var ctx = new TemporarySecureMimeContext())
{
using (var stream = File.OpenRead(ConfigurationManager.AppSettings["certLocation"]))
{
ctx.Import(stream, ConfigurationManager.AppSettings["certPassword"]);
}
var decrypted = pkcs7.Decrypt(ctx);
if (decrypted != null && decrypted is MimePart && ((MimePart)decrypted).FileName == "smime.p7m")
{
//We need to verify the signature
var signedDecryptedEntity = decrypted as ApplicationPkcs7Mime;
signedDecryptedEntity.Verify(ctx, out decrypted); //the real decrypted data
}
var decryptedParts = new List<MimePart>();
if (decrypted is Multipart)
{
decryptedParts = breakMultiPart((Multipart)decrypted);
}
else if (decrypted is MimePart)
{
decryptedParts.Add((MimePart)decrypted);
}
else
{
throw new InvalidOperationException("Unknown Mime part found");
}
var textParts = decryptedParts.Where(r => r is TextPart);
var htmlParts = textParts.Where(x => ((TextPart)x).IsHtml);
var textBodyParts = textParts.Where(x => !((TextPart)x).IsHtml);
var attachmentParts = decryptedParts.Where(r => !(r is TextPart));
if (htmlParts.Any())
{
if (htmlParts.Count() > 1)
{
throw new InvalidOperationException("multiple html body parts.");
}
var htmlPart = (TextPart)htmlParts.First();
decryptedEmailMessage.Body = new MessageBody(BodyType.HTML, htmlPart.Text);
}
else
{
//Text body
if (textBodyParts.Count() > 1)
{
throw new InvalidOperationException("multiple text body parts.");
}
var textPart = (TextPart)textBodyParts.First();
decryptedEmailMessage.Body = new MessageBody(BodyType.Text, textPart.Text);
}
foreach (var part in attachmentParts)
{
var content = new MemoryStream();
part.Content.DecodeTo(content);
content.Position = 0;
decryptedEmailMessage.Attachments.AddFileAttachment(part.FileName, content);
if (!part.IsAttachment)
{
decryptedEmailMessage.Attachments.First(r => r.Name == part.FileName).IsInline = true;
decryptedEmailMessage.Attachments.First(r => r.Name == part.FileName).ContentId = part.ContentId;
}
}
}
//Do Something with email (decryptedEmailMessage)
}
else
{
//The email is not encrypted
decryptedEmailMessage = email;
//Do Something with email (decryptedEmailMessage)
}
}
}