EWS:从签名的电子邮件中检索附件

时间:2014-02-07 13:38:19

标签: c# email attachment ews-managed-api smime

我有一个C#程序,它通过检索附件并将电子邮件分类到子文件夹来管理资源邮箱。最近出现了一个问题,即客户希望向我们发送已签名的电子邮件,因此当程序检索其附件时,将保存名为“smime.p7m”的文件而不是文件附件。查看Outlook中的电子邮件时,此文件不存在,只有我们想要的附件。但是,在单步执行代码时,Email对象中列出的附件仅包含此.p7m文件。

我从电子邮件中检索了mime内容,但它只是字节。当我在文本编辑器中查看.p7m文件时,我会在底部文件中看到我想要的文件的内容(最终的挑逗)!如何获取原始附件而无需解析.p7m文件中的感兴趣内容?

Exchange服务器是2010 SP2,这都是通过使用EWS托管API的C#程序实现的。

5 个答案:

答案 0 :(得分:3)

您可以使用EnvelopedCMS类从加密附件中获取MIME。假设您的安全上下文可以访问密钥。

byte[] content = ...The byte[] from the smime.p7m attachment ...
var encrypted = new EnvelopedCms();
encrypted.Decode(content);
encrypted.Decrypt();
byte[] unencryptedButRawMimeEntity = encrypted.ContentInfo.Content;

这将允许您获取未加密的MIME实体(没有传输标头的原始电子邮件)。

注意如果邮件已签名,则解密的MIME实体将是另一个SMIME Type标头等于signed-data的单个附件。您可以使用SignedCMS类重复上述过程,以显示其内容。 {@ 1}}调用应该省略。

然后,您必须解析/解码MIME以提取其正文和附件。

执行此操作的代码显然位于Decrypt命名空间,但Microsoft无论出于何种原因都不提供公共入口点。我在其他地方读过你可以用反射来访问它。这样做的缺点是,它不受支持,并且非公共接口在更高版本的框架中可能会发生变化。 this question中的代码向您展示了如何处理System.Net.Mime传输编码。

或者,您可以像我一样编写或借用自己的MIME解析器。不幸的是,由于IP,我无法给你代码。

当时我无法找到一个简单的替代方案。现在我很想尝试下面链接的NuGet包,为自己省点痛苦。从OpenPOP.Net开始。


您可以使用code in this project获取灵感,查看this question中的第三方选项,或在NuGet上尝试these packages

答案 1 :(得分:2)

您应该查看MimeKit(MIME解析器+ S / MIME和PGP支持)和MailKit(如果您需要SMTP,POP3和/或IMAP)。

我已经写了一些关于如何在GitHub项目主页上的文档中使用MimeKit的解密和签名验证API的例子。

答案 2 :(得分:1)

使用可通过NuGet和EWS托管API访问的OpenPOP.NET,我能够检索嵌入在某个感兴趣的电子邮件的mime内容中的附件。我用了

System.Exchange.WebServices.Data.Item email = new System.Exchange.WebServices.Data.Item(myEmail);
OpenPop.Mime.Message message = new OpenPop.Mime.Message(email.MimeContent.Content);
List<OpenPop.Mime.MessagePart> validMessageParts = message.FindAllAttachments().Where(x => x.FileName.Contains(".csv") == true || x.FileName.Contains(".xlsx") == true || x.FileName.Contains(".xls") == true).ToList<MessagePart>();

foreach (MessagePart messagePart in validMessageParts)
{
  if (messagePart != null)
  {
    using (FileStream fileStream = new FileStream(savingPath + messagePart.ContentDisposition.FileName, FileMode.Create, FileAccess.ReadWrite))
    {
        messagePart.Save(fileStream);
    }
  }
}

将特定电子邮件中的所有csv,xlsx和xls保存到我选择的目录中。

答案 3 :(得分:0)

最近3天,我尝试从已签名但未加密的电子邮件中提取附件。。我们的项目位于vb.net中,但应该很容易将其重写为c#。 以下是对我有用的步骤:

  1. 安装Mimekit Nuget Package
  2. 通过查看其内容类型和附件名称正确识别S / Mime签名的电子邮件(S / Mime签名的电子邮件始终始终附有smime.p7m文件)
If String.Equals(origMessage.Attachments.First.ContentType, "multipart/signed", 
StringComparison.OrdinalIgnoreCase) AndAlso
String.Equals(origMessage.Attachments.First.Name, "smime.p7m", StringComparison.OrdinalIgnoreCase) Then
  1. 将smime文件加载为EWS FileAttachment,并从中创建新的memoryStream。然后创建此流的MimeKit.MimeEntity。现在,您正在使用MimeKit库,该库非常适合这些东西
Dim smimeFile As FileAttachment = origMessage.Attachments.First
smimeFile.Load()
Dim memoryStreamSigned As MemoryStream = New MemoryStream(smimeFile.Content)
Dim entity = MimeEntity.Load(memoryStreamSigned)
  1. 遍历您的MimeEntity实例以获取所有附件
If TypeOf entity Is Cryptography.MultipartSigned Then
    Dim mltipart As Multipart = entity
    Dim attachments As MimeEntity = mltipart(0)
    If TypeOf attachments Is Multipart Then
        Dim mltipartAttachments As Multipart = attachments
        For i As Integer = 0 To mltipartAttachments.Count - 1
            If mltipartAttachments(i).IsAttachment Then
                **'BOOM, now you're looping your attachment files one by one**
                **'Call your decode function to read your attachment as array of Bytes**
            End If
        Next
    End If
End If
  1. 将附件读取为字节数组。在上一步的for中执行此操作。
'Read and decode content stream
Dim fileStrm = New MemoryStream()
mltipartAttachments(i).Content.DecodeTo(fileStrm)

Dim decodedBytes(0 To fileStrm.Length - 1) As Byte
fileStrm.Position = 0  'This is important because .DecodeTo set the position to the end!!
fileStrm.Read(decodedBytes, 0, Convert.ToInt32(fileStrm.Length))

现在,您已将附件文件解码为字节数组,您可以保存它或做任何您想做的事情:)希望对您有所帮助!

答案 4 :(得分:0)

MimeKit。

我对此有误:

mltipartAttachments(i).Content.DecodeTo(fileStrm)
  

“内容”不是“ MimeEntity”的成员

我通过以下方法解决了此问题:

Dim mp As MimePart = mltipartAttachments(i)
mp.Content.DecodeTo(fileStrm)