I have a client that is receiving email incorrectly encoded. I am using the System.Net.Mail class and setting the body encoding to UTF-8. I have done a bit of reading and since I have to set the body of the email as a string encoding the data to a UTF-8 byte array really does nothing for me since I have to convert is back to a string that is UTF-16. Correct?
when I send:
Il s'agit d'un message de test pour déterminer comment le système va gérer les messages envoyés à l'aide des caractères français.
Merci et bonne journée.
They see:
*Il s'agit d'un message de test pour déterminer comment le système va gérer les messages envoyés à l'aide des caractères français.
Merci et bonne journée.*
I have tried different encodings on the email, but I am really suspecting that the client is incorrectly decoding the email since my email client displays this correctly. Am I missing something? Is there something else that can be done?
code below
SmtpMailService.SendMail(string.Empty, toAddress, "emailaddress#emai.com", "", subject, sb.ToString(), false);
public static bool SendMail(string fromAddress, string toAddress, string ccAddress, string bccAddress, string subject, string body, bool isHtmlBody)
{
if (String.IsNullOrEmpty(toAddress)) return false;
var toMailAddress = new MailAddress(toAddress);
var fromMailAddress = new MailAddress(String.IsNullOrEmpty(fromAddress) ? DefaultFromAddress : fromAddress);
var mailMessage = new MailMessage(fromMailAddress, toMailAddress);
if (!String.IsNullOrEmpty(ccAddress))
{
mailMessage.CC.Add(new MailAddress(ccAddress));
}
if (!String.IsNullOrEmpty(bccAddress))
{
mailMessage.Bcc.Add(new MailAddress(bccAddress));
}
if (!string.IsNullOrEmpty(fromAddress)) mailMessage.Headers.Add("Reply-To", fromAddress);
mailMessage.Subject = subject;
mailMessage.IsBodyHtml = isHtmlBody;
mailMessage.Body = body;
mailMessage.BodyEncoding = System.Text.Encoding.UTF8;
var enableSslCfg = ConfigurationManager.AppSettings["Email.Ssl"];
var enableSsl = string.IsNullOrEmpty(enableSslCfg) || bool.Parse(enableSslCfg);
var client = new SmtpClient {EnableSsl = enableSsl};
client.Send(mailMessage);
return true;
}
What finally solved my problem was setting the contenttype on the alternate view. Just setting the msg.BodyEncoding didn't work in (among others) Outlook although Gmail displayed the email correctly.
var msg = new MailMessage()
msg.BodyEncoding = Encoding.GetEncoding(1252);
msg.IsBodyHtml = true;
//htmlBody is a string containing the entire body text
var htmlView = AlternateView.CreateAlternateViewFromString(htmlBody, new ContentType("text/html"));
//This does the trick
htmlView.ContentType.CharSet = Encoding.UTF8.WebName;
msg.AlternateViews.Add(htmlView);
Your example clearly shows that the text you send has UTF-8 encoding, which is interpreted as Windows-1252 or ISO-8859-1 encoding (see the results of several encoding errors, especially the last row in the table here).
So your code seems to be alright, the problem might be that client side just ignores the encoding of the mail body (for example because they embed it into an HTML page and the wrapper HTML uses the default ISO-8859-1 encoding).
Try to send the mail to another provider (eg. Gmail) and check the results. Well configured providers should handle the body encoding.
Solution 1
If the assumptions above are true the error must be fixed on client side. They should either process the body encoding or if it is always UTF-8, they should simply add the following tag into HTML header:
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
Solution 2
If you cannot affect the client side at any cost you might want to try to use another encoding:
mailMessage.BodyEncoding = System.Text.Encoding.GetEncoding("ISO-8859-1");
Please note that your French example will be correct this way; however, non-Western European languages will not be displayed correctly.
The only way is to replace the character with suitable character as you are using character map and the html is encoding your code to different garbage character
you can modify your code like :
WebClient myClient = new WebClient();
byte[] requestHTML;
string currentPageUrl = HttpContext.Current.Request.Url.AbsoluteUri;
UTF8Encoding utf8 = new UTF8Encoding();
requestHTML = myClient.DownloadData(currentPageUrl);
var str = utf8.GetString(requestHTML);"
str = str.Replace("’", "'");
Try:
mailMessage.BodyEncoding = UTF8Encoding.UTF8;
instead of:
mailMessage.BodyEncoding = System.Text.Encoding.UTF8;
I had a similar problem with the accents in a mail. My solution was:
mail.BodyEncoding = System.Text.Encoding.UTF8;
Related
I am mentioning my problem in a simple test scenario. I am sending a message using a WebAPI.
Here is code:
string subject = "Test message for German special characters (äÄöÖüÜß)";
var utf8 = Encoding.UTF8;
byte[] utfBytes = utf8.GetBytes(subject);
subject = utf8.GetString(utfBytes, 0, utfBytes.Length);
string aktz_sender = "test Absender";
string aktz_rec = "Test Empfänger";
string receiverIdent = "Test_Identity";
string att_type = "Test_Type";
string result = baseClass.SendMessage(subject, aktz_sender, aktz_rec,
receiverIdent, att_type, listOfAttachment);
if (result == "true")
MessageBox.Show("Message send successfully");
listOfAttachment.Clear();
The message is sent successfully. But when the recipient receives the message in his inbox, the subject is shown as:
Test message for German special characters (???????)
i.e. each German special character is replaced by a ? character. I have contacted with Web API vendor and got a reply that I have to make sure that each character is encoded in UTF-8 before sending the message, because the API is working entirely with UTF-8 encoding.
Please guide me where am I missing something.
I am trying to set up a forgot password feature, but I can't get the body of the email formatted with links. I think the email is not set up for Html.
void sendMail(IdentityMessage message)
{
#region formatter
string text = string.Format("", message.Subject, message.Body);
string html = "";
html += HttpUtility.HtmlEncode(#"" + message.Body);
#endregion
MailMessage msg = new MailMessage();
msg.From = new MailAddress(ConfigurationManager.AppSettings["Email"].ToString());
msg.To.Add(new MailAddress(message.Destination));
msg.IsBodyHtml = true;
msg.Subject = message.Subject;
msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(html, null, MediaTypeNames.Text.Html));
msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(html, null, MediaTypeNames.Text.Html));
SmtpClient smtpClient = new SmtpClient("smtp-mail.outlook.com", Convert.ToInt32(587));
System.Net.NetworkCredential credentials = new System.Net.NetworkCredential(ConfigurationManager.AppSettings["Email"].ToString(), ConfigurationManager.AppSettings["Password"].ToString());
smtpClient.Credentials = credentials;
smtpClient.EnableSsl = true;
smtpClient.Send(msg);
}
How I use it:
await UserManager.SendEmailAsync(user.Id, "Holiday Tracker Reset Password", "Please reset your password by using the following link. here ");
I would like the code to work so the email has a clickable link instead of just the long string of the url.
You haven't provided any sample of your message body data, or sample source markup of the generated emails, so it's hard to be 100% of the root cause, but here are a few little issues here which might be contributing:
1) You never actually set the main body text of your email. You set alternative views, but not the base body. So even though you set the IsBodyHtml option, there's nothing for it to act upon.
2) You've added two identical alternative views. It probably doesn't matter, but it's clearly redundant. If you've set the body to HTML, and then actually included body text, you shouldn't need these alternative views anyway.
3) The above are niggles, but I think this is the clincher: I strongly suspect you have misunderstood what HttpUtility.HtmlEncode() does.
Consider the following example:
string myString = "<b>Hello World</b>";
// Encode the string.
string myEncodedString = HttpUtility.HtmlEncode(myString);
Console.WriteLine($"HTML Encoded string is: {myEncodedString}");
StringWriter myWriter = new StringWriter();
// Decode the encoded string.
HttpUtility.HtmlDecode(myEncodedString, myWriter);
string myDecodedString = myWriter.ToString();
Console.Write($"Decoded string of the above encoded string is: {myDecodedString}");
This will output:
HTML Encoded string is: <b>Hello World</b>
Decoded string of the above encoded string is: <b>Hello World</b>
Live Demo: https://dotnetfiddle.net/9j46WN
As you can see, encoding a string containing HTML will convert the HTML control characters into their encoded representation. This is generally used to ensure the characters are not treated as HTML when viewed (i.e. they are seen as pieces of text to be displayed on screen, not interpreted as part of the HTML document's markup). This is the exact opposite of what you want.
e.g. when I put those two strings into this HTML page as raw markup (rather than in a "code" block), it comes out as:
HTML Encoded string is: <b>Hello World</b>
Decoded string of the above encoded string is: Hello World
Notice how the encoded one displays the HTML rather than using it?
So I think you can drop the Html-encoding. Assuming you are already supplying HTML markup to your method in message.Body, then you can use it as-is. And if you set your message body in the normal way, you should be able to dispense with the alternative views as well. Here's what I think your code should be changed to:
void sendMail(IdentityMessage message)
{
MailMessage msg = new MailMessage();
msg.From = new MailAddress(ConfigurationManager.AppSettings["Email"].ToString());
msg.To.Add(new MailAddress(message.Destination));
msg.IsBodyHtml = true;
msg.Body = message.Body;
msg.Subject = message.Subject;
SmtpClient smtpClient = new SmtpClient("smtp-mail.outlook.com", Convert.ToInt32(587));
System.Net.NetworkCredential credentials = new System.Net.NetworkCredential(ConfigurationManager.AppSettings["Email"].ToString(), ConfigurationManager.AppSettings["Password"].ToString());
smtpClient.Credentials = credentials;
smtpClient.EnableSsl = true;
smtpClient.Send(msg);
}
I am trying to use the Gmail API to send a HTML email from C#. The email gets sent but Gmail refuses to acknowledge that it should be an HTML email.
This is the code I am using:
var template = #"from: {1}{4}to: {0}{4}subject: {2}{4}MIME-Version: 1.0{4}Content-Type: text/html; charset=UTF-8{4}Content-Transfer-Encoding: base64{4}{4}{3}";
body = HttpUtility.HtmlEncode(body);
var result = string.Format(template, to, from, subject, body, "\r\n");
result = Convert.ToBase64String(Encoding.UTF8.GetBytes(result));
var gMessage = new Message()
{
Raw = result
};
service.Users.Messages.Send(gMessage, "me").Execute();
This is what the result string looks like before encoding to base64:
from: test#test.com
to: test#test2.com
subject: testSubject
MIME-Version: 1.0
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: base64
<html><head>
<title>Push Email</title>
</head>
<body> blah
</body></html>
(The app actually uses real email addresses which I have replaced with "test#..." in the above example for privacy.)
I tried every possible combination of header arrangement, content-transfer-encoding (base64, 7bit, 8bit, etc), content-type charset (ascii, utf8, etc), I tried using UrlEncode instead of HtmlEncode but the email body is either just displayed as unrendered HTML or it is displayed as an encoded url string (depending on whether I use html encode or url encode and what contet transfer encoding I specify).
The point is, the mail is working, the body is being sent but it just stubbornly refuses to render the HTML. I either get this:
<html><head> <title>Push Email</title> </head> <body> blah </body></html>
Or this:
%3chtml%3e%3chead%3e%0d%0a%0d%0a%3ctitle%3ePush+Email%3c%2ftitle%3e+++%0d%0a+%0d%0a%3c%2fhead%3e++++%0d%0a%3cbody%3e+blah++%0d%0a++++%0d%0a%3c%2fbody%3e%3c%2fhtml%3e
Or this:
<html><head> <title>Push Email</title> </head> <body> blah </body></html>
I would just send an SMTP email but, probably for security, Google won't allow it if you have 2 factor auth for the account (which I have and don't plan on disabling).
Also, I am just building my MIME message as a regular string. This may have something to do with it but I don't know. I don't plan on using any third party nuget packages / libraries such as MimeKit. I just want a C# solution only.
Finally, I need to be able to send HTML emails so that I may send links as per my app business logic.
Any advice?
I finally got it. First of all, the body of the mail must not be escaped but the whole MIME string should. But, as mentioned previously, if I leave the body unencoded the API complains about an invalid byte string.
The problem is that the resulting base64 string should be encoded in a URL safe manner. The python code on the Gmail API guide uses a method called urlsafe_b64encode which is different from the normal base 64 method in that the resulting string is URL safe.
I thought I could replicate this in C# using HTML or URL encoding and then using the standard Convert.ToBase64String method to convert the MIME string to base64 but I was wrong. After searching the MSDN website I finally found the HttpServerUtility.UrlTokenEncode method which does just what the urlsafe_b64encode python method does which is to encode the string in a URL safe variant and also convert it to base64. The final code thus becomes:
// Template for the MIME message string (with text/html content type)
var template = #"from: {1}{4}to: {0}{4}subject: {2}{4}MIME-Version: 1.0{4}Content-Type: text/html; charset=UTF-8{4}Content-Transfer-Encoding: base64{4}{4}{3}";
// Fill in MIME message fields
var result = string.Format(template, to, from, subject, body, "\r\n");
// Get the bytes from the string and convert it to a URL safe base64 string
result = HttpServerUtility.UrlTokenEncode(Encoding.UTF8.GetBytes(result));
// Instantiate a Gmail API message and assign it the encoded MIME message
var gMessage = new Message()
{
Raw = result
};
// Use the Gmail API Service to send the email
service.Users.Messages.Send(gMessage, "me").Execute();
Your body looks like passed by HtmlEncode twice, i.e. < once passed become < twice passed become < which is what you have. As you didn't posted the original code from where body is being setted before the enconding, all I can say is you have to have this as your result string before enconding to base64:
from: test#test.com
to: test#test2.com
subject: testSubject
MIME-Version: 1.0
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: base64
<html><head>
<title>Push Email</title>
</head>
<body> blah
</body></html>
To get this, you can replace this line
body = HttpUtility.HtmlEncode(body);
by this
body = HttpUtility.HtmlDecode(body);
Really just need the option "IsBodyHtml=true":
using System.Net;
using System.Net.Mail;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
var html = "<html><head><body><h1>hello world!</h1></body><head></html>";
var from = "me#here.com";
var to = "them#there.com";
var msg = new MailMessage(from, to)
{
Subject = "My Subject",
Body = html,
IsBodyHtml = true
};
SendGmail("myUserName", "myPassword", msg);
}
public static void SendGmail(string username, string password, MailMessage msg)
{
var client = new SmtpClient("smtp.gmail.com", 587);
client.EnableSsl = true;
client.DeliveryMethod = SmtpDeliveryMethod.Network;
client.Credentials = new NetworkCredential(username, password);
client.Send(msg);
}
}
}
thanks to #user1969903 give the direction, but i'm using .Net6, this work for me
// Template for the MIME message string (with text/html content type)
var mimeString = $"from: {from#gmail.com}\r\n" +
$"to: {to#gmail.com}\r\n" +
$"subject: Test Edm\r\n" +
$"MIME-Version: 1.0\r\n" +
$"Content-Type: text/html; charset=UTF-8\r\n" +
$"Content-Transfer-Encoding: base64\r\n\r\n" +
$"{html_here}";
var inputBytes = Encoding.UTF8.GetBytes(mimeString);
var gMessage = new Google.Apis.Gmail.v1.Data.Message()
{
Raw = Convert.ToBase64String(inputBytes).Replace("+", "-").Replace("/", "_").Replace("=", "")
};
service.Users.Messages.Send(gMessage, "me").Execute();
I'm having some trouble with email encoding. I am reading an HTML file from disk and sending in through Gmail. When I open the HTML in the browser it looks great. When I copy the HTML string from Visual Studio and save it as an HTML file, it looks great. When I receive the email it contains a bunch of invalid characters. Even the list bullets are messed up! I'm sure this is an issue with encoding, but the file is encoded as UTF-8 and looks good until it's converted to RAW and sent through Gmail.
Here is the process. We read from a docx using the OpenXML SDK then we use the HtmlConverter to save the document as HTML. Later the HTML is read in from the file, converted to RAW formatting and sent through the GMail API.
Here are some relevant code snips:
This is where we save our HTML file using HtmlConverter.
HtmlConverterSettings settings = new HtmlConverterSettings()
{
AdditionalCss = "body { margin: 1cm auto; max-width: 20cm; padding: 0; }",
FabricateCssClasses = true,
RestrictToSupportedLanguages = false,
RestrictToSupportedNumberingFormats = false,
};
XElement htmlElement = HtmlConverter.ConvertToHtml( wdWordDocument, settings );
var html = new XDocument(
new XDocumentType( "html", null, null, null ),
htmlElement );
var htmlString = html.ToString( SaveOptions.DisableFormatting );
File.WriteAllText( destFileName.FullName, htmlString, Encoding.UTF8 );
This is where we read the stored HTMl and convert it for sending via Gmail. (We use Mimekit for the conversion.)
// Create the message using MimeKit/System.Net.Mail.MailMessage
MailMessage msg = new MailMessage();
msg.Subject = strEmailSubject; // Subject
msg.From = new MailAddress( strUserEmail ); // Sender
msg.To.Add( new MailAddress( row.email ) ); // Recipient
msg.BodyEncoding = Encoding.UTF8;
msg.IsBodyHtml = true;
// We need to loop through our HTML Document and replace the images with a CID so that they will display inline
var vHtmlDoc = new HtmlAgilityPack.HtmlDocument();
vHtmlDoc.Load( row.file ); // Read the body, from HTML file
...
msg.Body = vHtmlDoc.DocumentNode.OuterHtml;
// Convert our System.Net.Mail.MailMessage to RAW with Base64 encoding for Gmail
MimeMessage mimeMessage = MimeMessage.CreateFromMailMessage( msg );
Google.Apis.Gmail.v1.Data.Message message = new Google.Apis.Gmail.v1.Data.Message();
message.Raw = Base64UrlEncode( mimeMessage.ToString() );
var result = vGMailService.Users.Messages.Send( message, "me" ).Execute();
And this is how we are base64 encoding:
private static string Base64UrlEncode( string input )
{
var inputBytes = System.Text.Encoding.UTF8.GetBytes( input );
// Special "url-safe" base64 encode.
return Convert.ToBase64String( inputBytes )
.Replace( '+', '-' )
.Replace( '/', '_' )
.Replace( "=", "" );
}
The email ends up as "Content-Type: multipart/mixed" with two alternatives. One is
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
and the other is
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
The both the plain text and the HTML contain strings like =C3=A2=E2=82=AC=E2=84=A2 for an apostrophe and the HTML portion contains an HTML header that contains weird "3D" characters in it.
<meta charset=3D"UTF-8"><title></title><meta name=3D"Generator"=
content=3D"PowerTools for Open XML">
None of this weirdness was in the HTML prior to converting to Base64 and sending.
Any ideas what the problem could be? Does this have anything to do with UTF8 and Mimekit?
This is what your code should look like to get the "raw" message data for use with Google's API's:
using (var stream = new MemoryStream ()) {
message.WriteTo (stream);
var buffer = stream.ToArray ();
var base64 = Convert.ToBase64String (buffer)
.Replace( '+', '-' )
.Replace( '/', '_' )
.Replace( "=", "" );
message.Raw = base64;
}
As brandon927 pointed out, the content of the text/html mime part has been quoted-printable encoded. This is a MIME encoding used for transport to make sure that it fits within the 7bit ascii range.
You will need to decode this in order to get the original HTML.
With MimeKit, this is done for you if you either use mimeMessage.HtmlBody or if you cast the MimeEntity representing the text/html part into a TextPart and access the Text property.
The answer to your question is: there is no problem. This is simply how Raw is presented, with quoted-printable encoding. This is how Gmail also presented it if you send and email and look at the source of it.
I have to send an EDI message to a government body, which is signed/encrypted in a particular way.
According to https://docs.google.com/document/d/1xOxsZG7nCXdd3ucFKJObheW4G6kFwflGFkzURS_haTY/edit?usp=sharing
I am trying this code, but the encrypted S/MIME isn’t correctly formatted according to the government gateway.
Email reply from them:
The Error Code I am getting is a decryption failure.
You should have signed your EDI message using your Gatekeeper Certificate first.
This produces an S/MIME blob. We call this the “signed” S/MIME .
Then, you take your signed blob and encrypt it using the Customs Gateway Certificate downloaded from our cargo web site.
This produces another S/MIME, we call the “encrypted” S/MIME.
I am signing and encrypting using the correct encryption certificates.
Have also tried 3rd party libraries ActiveUp and Chilkat to no avail so far.
Any help in interpreting the Customs Spec and adjusting where I might have gone wrong much appreciated. I have been working on this issue for more than a week.
public static void SendEmail(string ediMsg, string clientCertificatePath,
string clientCertificatePassword, string sender, string receiver, string subject, SmtpClient smtp,
string customsCertificatePath)
{
//Load the certificate
X509Certificate2 EncryptCert = new X509Certificate2(customsCertificatePath);
X509Certificate2 SignCert =
new X509Certificate2(clientCertificatePath, clientCertificatePassword);
//Build the body into a string
StringBuilder Message = new StringBuilder();
ediMsg = "UNB+IATB:1+6XPPC+LHPPC+940101:0950+1' ...";
/*The EDI document is first formatted as a MIME message [MIME],
* as the EDI Document may contain special characters, non-printable ASCII and binary data. */
byte[] arrayToEncode = System.Text.Encoding.UTF8.GetBytes(ediMsg);
ediMsg = Convert.ToBase64String(arrayToEncode);
/*Within the MIME message, the Content-Transfer-Encoding header must be either “quoted-printable”
* or “base64”, and the Content-Type header should be set to “Application/EDIFACT”. */
Message.AppendLine("Content-Type: Application/EDIFACT");
Message.AppendLine("Content-Transfer-Encoding: base64");
//The file name of the attachment for inbound e-mails (to Customs) must be the same as the Subject Line
//(section 3.3) with the .edi suffix.
Message.AppendLine("Content-Disposition: attachment; filename=\"" + subject + ".edi\"");
/*I have tried this with
* (a) the raw ediMsg,
* (b) the base64 version of (a)
* (c) quoted-printable version of (a)
* (d) base64 version of (c)
*/
Message.AppendLine(ediMsg);
//Text must not be included in the body of the e-mail. EDI documents must be sent as an attachment.
//Convert the body to bytes
byte[] BodyBytes = Encoding.UTF8.GetBytes(Message.ToString());
//sign
var signedBytes = SignMsg(BodyBytes, SignCert);
//Build the e-mail body bytes into a secure envelope
EnvelopedCms Envelope = new EnvelopedCms(new ContentInfo(signedBytes));
CmsRecipient Recipient = new CmsRecipient(
SubjectIdentifierType.IssuerAndSerialNumber, EncryptCert);
Envelope.Encrypt(Recipient);
byte[] EncryptedBytes = Envelope.Encode();
//Create the mail message
MailMessage Msg = new MailMessage();
Msg.To.Add(new MailAddress(receiver));
Msg.From = new MailAddress(sender);
Msg.Subject = subject;
//Attach the encrypted body to the email as and ALTERNATE VIEW
MemoryStream ms = new MemoryStream(EncryptedBytes);
AlternateView av =
new AlternateView(ms,
"application/pkcs7-mime; smime-type=signed-data;name=smime.p7m");
Msg.AlternateViews.Add(av);
//SmtpClient smtp = new SmtpClient(MailServer, 25);
//send the email
smtp.Send(Msg);
}
I'm not sure the issues I'm about to point out are the problem, but they might be worth looking into...
First, Convert.ToBase64String(arrayToEncode); does not wrap lines as needed in MIME. What you'll need to use is this variant with Base64FormattingOptions.InsertLineBreaks.
Secondly, I don't know what SignMsg() does, but make sure that you prepend the proper Content-Type, Content-Transfer-Encoding, and (possibly) Content-Disposition headers as well. The Content-Type should be application/pkcs7-mime; smime-type=signed-data; name=smime.p7s and Content-Transfer-Encoding should be base64 once you've base64 encoded the data.
Thirdly, the Content-Type header you gave to the encrypted outer part is wrong. It should be application/pkcs7-mime; smime-type=enveloped-data; name=smime.p7m.
Fourthly, Make sure that the encrypted data gets base64 encoded and that the AlternativeView gets a Content-Transfer-Encoding of base64.
I'm not sure if adding it as an alternative view will necessarily work or not, I'd have to see the generated MIME to be sure.
Something you might consider using instead of IP*Works which is payware is my Open Source library called MimeKit which already supports generating S/MIME messages. I also have a library called MailKit which supports SMTP which you seem to be using.
Both libraries are easily available via NuGet: MimeKit and MailKit
What you would do is something like this:
// Note: if the email addresses do not match the certificates, you can
// use a SecureMailboxAddress instead, which allows you to specify the
// Fingerprint (aka Thumbprint) of the certificate to use for signing
// or encrypting.
var recipient = new MailboxAddress ("Receiver Name", "receiver#example.com");
var sender = new MailboxAddress ("Sender Name", "sender#example.com");
var message = new MimeMessage ();
message.To.Add (recipient);
message.From.Add (sender);
message.Subject = subject;
// create the application/edifact MIME part
var edifact = new MimePart ("application", "edifact");
// set the filename of the MIME part (adds a Content-Disposition header
// if not already present)
edifact.FileName = subject + ".edi";
// create the content stream of the MIME part
var content = new MemoryStream (Encoding.UTF8.GetBytes (ediMsg), false);
// set the content of the MIME part (we use ContentEncoding.Default because
// it is not encoded... yet)
edifact.ContentObject = new ContentObject (content, ContentEncoding.Default);
// encode the content using base64 *and* set the Content-Transfer-Encoding header
edifact.ContentTransferEncoding = ContentEncoding.Base64;
using (var ctx = new TemporarySecureMimeContext ()) {
ctx.Import (clientCertificatePath, clientCertificatePassword);
ctx.Import (customsCertificatePath);
// sign and then encrypt the edifact part and then set the result as the
// message body.
message.Body = ApplicationPkcs7Mime.SignAndEncrypt (ctx, sender,
DigestAlgorithm.Sha1, new [] { recipient }, edifact);
}
// MailKit's SMTP API is very similar to System.Net.Mail's SmtpClient API,
// so that shouldn't pose a problem.