I'm trying to get the attachments of a signed mail via the microsoft-graph-api.
I use a GET-Request on this URL:
https://graph.microsoft.com/v1.0/me/messages/AAMkAG.../attachments
This should return a list of objects for the specified mail. Every object contains metadata like "name" and "contentType" of one attachment as well as the attribute "contentBytes" which contains the content of the attachment as a base64-string
.
If the mail has no attachments this list is empty.
This works fine so far with every mail that is not signed via S/MIME
.
However, if the mail is signed with S/MIME
, I get strange results in the response list.
No matter how many attachments the mail has, the response list only contains one element. This element then comes with the name "smime.p7m" and the contentType "multipart/signed" while the contentBytes attribute contains almost the entire MIME of the mail instead of the content of a single attachment.
I can't imagine that this is desired behaviour, so I'm asking:
Is this a bug in the microsoft-graph-api or am I doing something wrong in the request and if so, how can I fix this?
This is not a bug but the expected behavior in case of a 'multipart/signed' message.
From RFC5751 (bottom of page 26):
The multipart/signed media type has two parts. The first part
contains the MIME entity that is signed; the second part contains the
"detached signature" CMS SignedData object in which the
encapContentInfo eContent field is absent.
So the signed content, including any attachments, is stored inside the one smime.p7m attachment. It is up to you to extract it.
Assuming you are using .Net, you can use the SignedCms class to validate the signature and retrieve the content using the ContentInfo property.
This might be a bit unrelated to your question but I spent last 3 days trying extract attachments from signed but unencrypted email. Hope this helps someone in similar situation. Here are steps which worked for me in vb.net:
- Install Mimekit Nuget Package
- 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
- 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)
- 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
- 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!