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.
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.
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:
If String.Equals(origMessage.Attachments.First.ContentType, "multipart/signed",
StringComparison.OrdinalIgnoreCase) AndAlso
String.Equals(origMessage.Attachments.First.Name, "smime.p7m", StringComparison.OrdinalIgnoreCase) Then
Dim smimeFile As FileAttachment = origMessage.Attachments.First
smimeFile.Load()
Dim memoryStreamSigned As MemoryStream = New MemoryStream(smimeFile.Content)
Dim entity = MimeEntity.Load(memoryStreamSigned)
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
'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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With