Question

I have a C# program that manages a resource mailbox by retrieving attachments and categorizing emails into sub-folders. An issue came up recently where the client wishes to send us signed emails, so when the program retrieves their attachments a file named "smime.p7m" is saved instead of the file attachments. This file is not present when reviewing the email in Outlook, only the attachments we want. However, when stepping through the code, the attachments listed in the Email object only contains this .p7m file.

I have retrieved the mime content from the email, but it's just bytes. When I look at the .p7m file in a text editor, I see the contents of the file(s) I want at the bottom file (the ultimate tease)! How do I get the original attachments without having to parse the .p7m file for the content of interest?

The exchange server is 2010 SP2, and this is all happening via a C# program utilizing the EWS Managed API.

Was it helpful?

Solution

You can use the EnvelopedCMS class to get the MIME from the encrypted attachment. Assuming your security context has access to the key.

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

This will allow you to get the unencrypted MIME Entity (the original email without transport headers).

Note if the message is signed, the decrypted MIME entity will be another single attachment with an SMIME Type Header equal to signed-data. You can repeat the process above using the SignedCMS class, to expose its content. The Decrypt call should be omitted.

You then have to parse/decode the MIME to extract its body and attachments.

The code to do this obviously resides the System.Net.Mime namespace but Microsoft, for whatever reason, offer no public entry point to it. I have read elsewhere than you can use reflection to access it. This disadvantages of this is, its not supported and the non-public interface is subject to change in later versions of the framework. The code in this question shows you how to deal with a quoted-printable Transfer Encoding.

Alternatively, you can write or borrow your own MIME parser, as I did. Unfortunately, I can't give you the code because of IP.

At the time I was unable to find a simple alternative. Now I'd be tempted to try out the NuGet packages linked below and save myself some pain. Starting with OpenPOP.Net.


You could use the code in this project for inspiration, look at the third-party options in this question, or try these packages on NuGet.

OTHER TIPS

You should check out MimeKit (MIME parser + S/MIME and PGP support) and MailKit (if you need SMTP, POP3, and/or IMAP).

I've written some examples on how to use MimeKit's decryption and signature verification APIs in the documentation on the main page of the GitHub project.

I spent last 3 days trying extract attachments from signed but unencrypted email. Our project is in vb.net but it should be easy to rewrite it to c#. Here are steps which worked for me:

  1. Install Mimekit Nuget Package
  2. Correctly identify the S/Mime signed email by looking at its content type and attachment name (S/Mime signed emails always have smime.p7m file attached to it)
If String.Equals(origMessage.Attachments.First.ContentType, "multipart/signed", 
StringComparison.OrdinalIgnoreCase) AndAlso
String.Equals(origMessage.Attachments.First.Name, "smime.p7m", StringComparison.OrdinalIgnoreCase) Then
  1. Load smime file as EWS FileAttachment and create new memoryStream out of it. Then create MimeKit.MimeEntity of this stream. Now you're using MimeKit library which is great for this stuff
Dim smimeFile As FileAttachment = origMessage.Attachments.First
smimeFile.Load()
Dim memoryStreamSigned As MemoryStream = New MemoryStream(smimeFile.Content)
Dim entity = MimeEntity.Load(memoryStreamSigned)
  1. Iterate over your MimeEntity instance for all attachments
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. Read your attachment as array of Bytes. Do this inside the for of previous step.
'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))

Now you have your attachment file decoded as an array of Bytes and you can just save it or do whatever you want :) Hope this helped!

Using OpenPOP.NET, which is accessible through NuGet, and EWS Managed API I was able to retrieve the attachments embedded within the mime content of a certain email of interest. I used

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(".xml") == 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);
    }
  }
}

to save all csv, xlsx, and xls in a particular email into a directory of my choice.

MimeKit.

I get an error with this :

mltipartAttachments(i).Content.DecodeTo(fileStrm)

'Content' is not a member of 'MimeEntity'

I solved it with this :

Dim mp As MimePart = mltipartAttachments(i)
mp.Content.DecodeTo(fileStrm)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top