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.
Related
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();
When using the System.Net.Mail namespace to send a e-mail with attachment to any Yahoo account the attachment is downloaded with 'untitled' name instead the file name.
In the Yahoo Mail interface the attachment looks with the correct name but when you download it the download name goes to 'untitled' for all attachments. The same e-mail message works fine with Gmail, Outlook.com, Windows Live Mail and other clients.
Looking the raw message it constains a content-type with name but without filename attribute. The Yahoo works fine if the filename attribute is set but C# library don't use this.
That's the header generated by C# for attachments:
Content-Type: application/octet-stream; name=test.pdf
That's the header that works with Yahoo:
Content-Type: application/octet-stream; name=file2; filename=test.pdf
Anyone get this problem so far? Is there a work arround for C# default mail sending?
using (var message = new MailMessage("from#domain", "to#yahoo.com.br", "Test with attachment", "Test with attachment"))
{
var attachment = new Attachment(#"c:\temp\test.pdf"); // Same result using stream instead path to file.
attachment.Name = "test.pdf"; // Same result without this line.
message.Attachments.Add(attachment);
using (var smtp = new SmtpClient("smtp.domain", 587))
{
smtp.Credentials = new NetworkCredential("from#domain", "password");
smtp.Send(message);
}
}
I found a solution:
attachment.ContentDisposition.FileName = "test.pdf";
This add the missing filename attribute in the raw e-mail message and solve the Yahoo limitation.
Have you tried explicitly specifying the content type?
var attachment = new Attachment(... , MediaTypeNames.Application.Octet);
I am attempting to add a calendar invite in iCal format to an email sent via the MailGun API. This is what i have so far:
var request = new RestRequest();
request.AddParameter("domain", this.domain, ParameterType.UrlSegment);
request.Resource = "{domain}/messages";
request.AddParameter("from", contactDetails.SenderAddress);
request.AddParameter("to", contactDetails.RecipientAddress);
request.AddParameter("subject", message.Subject);
request.AddParameter("text", message.TextBody);
request.AddParameter("html", message.HtmlBody);
if (!string.IsNullOrWhiteSpace(message.IcalAttachment))
{
request.AddFileBytes("attachment",
Encoding.UTF8.GetBytes(message.IcalAttachment),
"invite.ics",
"text/calendar");
}
request.Method = Method.POST;
return request;
This results in the calendar being included in the email as an attachment, not an alternative view of the email. The attachment works fine in gmail however in Outlook it appears as an attachment file that you must first click on, then agree to adding the calendar to the Outlook calendar. Is there another way to use the REST api so that the calendar invites are sent correctly, as alternative email views?
To be clear, this is how I would send a calendar invite using .Net SmtpClient:
var contentType = new ContentType("text/calendar");
if (contentType.Parameters != null)
{
contentType.Parameters.Add("method", "REQUEST");
contentType.CharSet = "UTF-8";
}
// this is the same way you add a html view to the message
request.AlternateViews.Add(
AlternateView.CreateAlternateViewFromString(
message.IcalAttachment,
contentType));
Special thanks to Mailgun support for pointing me in the right direction. The relevant part or their response was:
You can use the /message.mime endpoint to construct the MIME for the calendar invite:
https://documentation.mailgun.com/api-sending.html#sending
Creating a mime message isnt as easy as simply using their /message endpoint but there are several .net libraries available to do this. I used MimeKit in this example.
var request = new RestRequest();
request.AddParameter("domain", this.domain, ParameterType.UrlSegment);
request.Resource = "{domain}/messages.mime";
request.AddParameter("to", contactDetails.RecipientAddress);
request.AddFile(
"message",
Encoding.UTF8.GetBytes(BuildMimeContent(message)),
"message.mime");
request.Method = Method.POST;
return request;
The mime content that I want to create will contain a multipart/mixed body, which will in turn contain a multipart/alternative as well as every attachment. The calendar invite will actually be attached twice, as a alternative view and as an attachment. This is to aid in compatibilitiy across different email clients.
The implementation of BuildMimeContent(message) looks like the following:
// create the alternative views
var textBody = new TextPart("plain") { Text = message.TextBody };
var htmlBody = new TextPart("html") { Text = message.HtmlBody };
// add views to the multipart/alternative
var alternative = new Multipart("alternative");
alternative.Add(textBody);
alternative.Add(htmlBody);
if (!string.IsNullOrWhiteSpace(message.CalendarInvite))
{
// also add the calendar as an alternative view
// encoded as base64, but 7bit will also work
var calendarBody = new TextPart("calendar")
{
Text = message.CalendarInvite,
ContentTransferEncoding = ContentEncoding.Base64
};
// most clients wont recognise the alternative view without the
// method=REQUEST header
calendarBody.ContentType.Parameters.Add("method", "REQUEST");
alternative.Add(calendarBody);
}
// create the multipart/mixed that will contain the multipart/alternative
// and all attachments
var multiPart = new Multipart("mixed") { alternative };
if (!string.IsNullOrWhiteSpace(message.CalendarInvite))
{
// add the calendar as an attachment
var calAttachment = new MimePart("application", "ics")
{
ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),
ContentTransferEncoding = ContentEncoding.Base64,
FileName = "invite.ics",
ContentObject = new ContentObject(GenerateStreamFromString(message.CalendarInvite))
};
multiPart.Add(calAttachment);
}
// TODO: Add any other attachements to 'multipart' here.
// build final mime message
var mimeMessage = new MimeMessage();
mimeMessage.From.Add(GetMimeAddress(message.MessageInfo.SenderName, message.MessageInfo.SenderAddress));
mimeMessage.To.Add(GetMimeAddress(message.MessageInfo.RecipientName, message.MessageInfo.RecipientAddress));
mimeMessage.Subject = message.Subject;
mimeMessage.Body = multiPart;
// parse and return mime message
return mimeMessage.ToString();
Warning for people testing with Office 365
Office365 is extremely picky when it comes to validating calendar invites. In order to not get a message like the one below, you will need to ensure that the vCal's organizer email address matches the email's from address. This is not possible if you are using mailgun's sandbox test environment.
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;
I'm using C#, .NET 4.0 to send a digitally-signed email, like so:
private void SendMailMessage(string emailTo)
{
MailMessage message = new MailMessage();
message.From = new MailAddress(fromAddress);
message.To.Add(new MailAddress(emailTo));
message.Subject = "Regarding your lottery winnings";
message.IsBodyHtml = false;
string body = "Content-Type: text/plain;charset=\"iso-8859-1\"\nContent-Transfer-Encoding: quoted-printable\n\nThe URL to your secret is: " + url + "\nIt can only be viewed once.";
byte[] messageBytes = Encoding.ASCII.GetBytes(body);
ContentInfo content = new ContentInfo(messageBytes);
SignedCms signedCms = new SignedCms(content, false);
CmsSigner Signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, emailCert);
signedCms.ComputeSignature(Signer);
byte[] signedBytes = signedCms.Encode();
MemoryStream ms = new MemoryStream(signedBytes);
AlternateView av = new AlternateView(ms, "application/pkcs7-mime; smime-type=signed-data;name=smime.p7m");
message.AlternateViews.Add(av);
SmtpClient client = new SmtpClient(smtpServer, int.Parse(smtpServerPort));
client.DeliveryMethod = SmtpDeliveryMethod.Network;
client.Send(message);
message.Dispose();
client = null;
}
Notice that message.Body is left blank, and only the AlternateView is added to the email. When I send this email and view it in Outlook, it shows up perfectly, with a certificate icon on the email message, and the S/MIME Outlook extensions validate the signature successfully and automatically.
Aces.
(If I add anything to message.Body, it breaks. Outlook no longer recognizes it as a signed email and I only see the message.Body text, not the AlternateView.)
But if I send this email to a Gmail address, for instance, it shows up as a blank email with smime.p7m as an attachment, and inside it I see the text of the email, but it's surrounded by what looks like a ton of binary gibberish.
Is there a way to make this digitally signed email compatible with both an Outlook client and a Gmail web client?
When outlook generates a signed email it adds an alternate view with the signed message, another alternate view with the html version and then another alternate view with a plain text version. I think if you also do this then it will work in most all email clients.
there is a plain text alternate view that is not signed
Content-Type: text/plain;
charset="us-ascii"
Content-Transfer-Encoding: 7bit
there is an html version that is not signed
Content-Type: text/html;
charset="us-ascii"
Content-Transfer-Encoding: quoted-printable
There is a signed alternate view
Content-Type: application/pkcs7-signature;
name="smime.p7s"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="smime.p7s"