Encrypting and signing Mail in .Net part 3/5 (Building the content – with attachments)
Lets get startet with the real stuff on how to encrypt mail with c#
Now we have created the certificats and we have the methods for retrieving them. So before we can sign and encrypt the mail we need to prepare there content for security operations. When sending encrypted mail with attachment the content should be included as a part of the content. If add an attahcment to an email with using the normal “System.net.mail – message.attachments.add() ” breaks the encryption. To overcome this the attachments are added to the maincontent of message but seprated with boundries.
Since there are different ways to fetch filecontent and bodycontent i’ve have focused only on howto message data should be build, so there are left some work for you to do. Implementing functions for retrieving filecontent into byte[] fx.
To make the next couple of step easier to connecto to this one which is the backbone in encrypting the mail, I will make a simple builcontent() function that builds a simple text/plain mail and adds an attachment called snebar.jpg placed on the root of my c drive.
public string buildMessageContent() { string messageBoundry = "--PTBoundry=2"; StringBuilder message = new StringBuilder(); message.Append("\r\n"); message.Append("\r\n"); message.Append("--"); message.Append(messageBoundry + "\r\n"); message.Append("Content-Type: text/plain; charset=us-ascii\r\n"); //could use text/html as well here if you want a html message message.Append("Content-Transfer-Encoding: "); message.Append(TransferEncoding.QuotedPrintable); message.Append("\r\n\r\n"); message.Append("TEST AF kryptering")//BODY TEXT GOES HERE message.Append("\r\n"); //ADD file section //could be filename or whatever //foreach (string filename in attachments){ //Read file part implement your own byte[] buff = null; FileStream fs = new FileStream("c:\\snebaer.jpg", FileMode.Open, FileAccess.Read); BinaryReader br = new BinaryReader(fs); long numBytes = new FileInfo("c:\\snebaer.jpg").Length; buff = br.ReadBytes((int)numBytes); byte[] bytes = buff; //Setup filecontent String filecontent = Convert.ToBase64String(bytes,Base64FormattingOptions.InsertLineBreaks); message.Append("--"); message.Append(messageBoundry); message.Append("\r\n"); message.Append("Content-Type: "); message.Append("application/octet-stream;"); message.Append("name=c:\\snebaer.jpg"); message.Append("\r\n"); message.Append("Content-Transfer-Encoding: base64\r\n\r\n"); message.Append(filecontent); message.Append("\r\n\r\n"); //} //END FILSECTION message.Append("--"); message.Append(messageBoundry); message.Append("--\r\n"); return message.ToString(); }<br>
Note that there isout comment foreach loop which could added if you need to add multiple attachments. I will also be a good idee to ad at method for building unique boundaries that could be used you can use Guid or what ever you like. I use theese static one so it easier to refence them in the next post. the string returned here is now readyto be signed. Look
Thanks! Your code worked perfectly.
Here is my version of your design in VB.NET:
Friend Function sendInternalEncrypt(sRecipType As String, sAddr As String, sSubject As String, sText As String, attachmentList As List(Of String)) As Boolean
Dim EmailSender As String = “DoNotReply@va.gov”
Dim EmailRecipient As String = sAddr
Dim EmailSubject As String = sSubject
Dim EmailText As String = sText
Dim sAttachmentText As String = “”
Dim sAttachmentRichtext As String = “”
Dim sAtt As String = “”
Dim sAttachmentTextContent As String = “”
Dim sAttachmentRichtextContent As String = “”
Dim sAttachmentTextName As String = “”
Try
‘ldap servers
Dim ldapServerVBA As String = “LDAP://someServer.va.gov:389”
Dim ldapServerNCA As String = “LDAP://someServer:389”
Dim ldapServerVA As String = “LDAP://someServer.va.gov:389”
‘get certificate of recipient from 1 of 3 ldap servers
Dim cert As X509Certificate2
Dim zeroPtr As IntPtr = CType(0, IntPtr)
cert = GetRecipientCertificate(EmailRecipient, ldapServerVBA)
If cert.Handle = zeroPtr Then
cert = GetRecipientCertificate(EmailRecipient, ldapServerNCA)
If cert.Handle = zeroPtr Then
cert = GetRecipientCertificate(EmailRecipient, ldapServerVA)
End If
End If
If cert.Handle = zeroPtr Then
Throw New ArgumentException(EmailRecipient & “: Certificate not found.”)
Else
‘construct an instance of the CmsRecipient class
‘by using the specified recipient certificate.
Dim cmsRecipient As CmsRecipient = New CmsRecipient(cert)
‘build big string
Dim s As StringBuilder = New StringBuilder
s.AppendLine(“content-type: multipart/mixed; boundary=makeANewPart”)
s.AppendLine()
s.AppendLine(“–makeANewPart”) ‘— part1 contains the plain text body
s.AppendLine(“content-type: text/plain; charset=us-ascii”)
s.AppendLine(“Content-Transfer-Encoding: Quoted-Printable”)
s.AppendLine()
s.AppendLine(EmailText)
s.AppendLine()
‘add attachments to big string
For Each sAtt In attachmentList
If sAtt.Length > 0 Then
s.AppendLine(“–makeANewPart”) ‘ make a new part for each attachment
Dim filename As String = Path.GetFileName(sAtt)
If filename.IndexOf(“.txt”, StringComparison.InvariantCultureIgnoreCase) > -1 Then
‘it’s a text file
s.AppendLine(“content-type: text/plain; charset=us-ascii; name=” & filename)
s.AppendLine(“Content-Transfer-Encoding: Quoted-Printable”)
Using sr As StreamReader = New StreamReader(sAtt, True)
s.AppendLine()
s.AppendLine(sr.ReadToEnd())
s.AppendLine()
End Using
ElseIf filename.IndexOf(“.csv”, StringComparison.InvariantCultureIgnoreCase) > -1 Then
‘it’s a csv file
s.AppendLine(“content-type: text/csv; charset=us-ascii; name=” & filename)
s.AppendLine(“Content-Transfer-Encoding: Quoted-Printable”)
Using sr As StreamReader = New StreamReader(sAtt, True)
s.AppendLine()
s.AppendLine(sr.ReadToEnd())
s.AppendLine()
End Using
ElseIf filename.IndexOf(“.rtf”, StringComparison.InvariantCultureIgnoreCase) > -1 Then
‘it’s rich text format.
s.AppendLine(“content-type: application/rtf; name=” & filename)
s.AppendLine(“Content-Transfer-Encoding: Quoted-Printable”)
Using sr As StreamReader = New StreamReader(sAtt, True)
s.AppendLine()
s.AppendLine(sr.ReadToEnd())
s.AppendLine()
End Using
ElseIf filename.IndexOf(“.pdf”, StringComparison.InvariantCultureIgnoreCase) > -1 Then
‘it’s a pdf file
Dim buff() As Byte
Dim fs As FileStream
Dim br As BinaryReader
fs = New FileStream(sAtt, FileMode.Open, FileAccess.Read)
br = New BinaryReader(fs)
Dim numbytes As Long = New FileInfo(sAtt).Length
buff = br.ReadBytes(CInt(numbytes))
Dim bytes() As Byte = buff
Dim sPdfFileContent As String = Convert.ToBase64String(bytes, Base64FormattingOptions.InsertLineBreaks)
s.AppendLine(“content-type: application/pdf; name=” & filename)
s.AppendLine(“Content-Transfer-Encoding: base64”)
Using sr As StreamReader = New StreamReader(sAtt, True)
s.AppendLine()
s.Append(sPdfFileContent)
s.AppendLine()
End Using
Else
‘I don’t encrypt this type of attachment
writeToStatusFile(“sendInternalEncrypt()”, “Warning: I don’t encrypt this kind of attachment: ” & sAtt & ” So I’m skipping it.”)
End If
End If
Next
s.AppendLine()
s.AppendLine(“–makeANewPart–“)
s.AppendLine()
‘ convert big string to array of ASCII bytes
Dim sBytes As Byte() = Encoding.ASCII.GetBytes(s.ToString())
‘ Build the e-mail body bytes into a secure envelope
Dim ci As ContentInfo = New ContentInfo(sBytes)
Dim envelopedCms As New EnvelopedCms(ci)
‘encrypt!
envelopedCms.Encrypt(cmsRecipient)
Dim EncryptedBytes As Byte() = envelopedCms.Encode()
‘Attach the encrypted body to the email as and ALTERNATE VIEW
Dim sbldr As StringBuilder = New StringBuilder
sbldr.Append(“application/pkcs7-mime;”)
sbldr.Append(“smime-type=enveloped-data;”)
sbldr.Append(“name=smime.p7m;”)
sbldr.Append(“content-transfer-encoding=Base64;”)
sbldr.Append(“content-disposition=attachment;”)
sbldr.Append(“fileName=smime.p7m;”)
Dim avString As String = sbldr.ToString
Dim ms As New MemoryStream(EncryptedBytes)
Dim av As New AlternateView(ms, avString)
‘get ready to send
Dim oSmtpClient As SmtpClient = New SmtpClient(sSmtpServer, iSmtpPort)
oSmtpClient.UseDefaultCredentials = True
oSmtpClient.Credentials = CredentialCache.DefaultNetworkCredentials
‘build mail message
Dim mm As MailMessage = New MailMessage()
Dim maFrom As MailAddress = New MailAddress(EmailSender)
mm.From = maFrom
mm.To.Add(EmailRecipient)
mm.Subject = EmailSubject
mm.AlternateViews.Add(av)
‘send email
oSmtpClient.Send(mm)
blnEmailSent = True
‘clean up
ms.Dispose()
oSmtpClient.Dispose()
mm.Dispose()
ms.Dispose()
End If
Catch ex As Exception
blnEmailSent = False
Dim sError As String = makeErrorString(“SendEmailInternalEncrypt”, ex)
My.Computer.FileSystem.WriteAllText(sErrorFile, sError, True)
End Try
Return blnEmailSent
End Function