I need to digitally sign email using s/mime. I've created a detached signature for my attachment but outlook can't open it: "Cannot open this item. Your Digital ID name cannot be found by the underlying security system". Certificate is installed on this PC and there is smth need to be tweaked in this code:
const int CHARS_IN_LINE = 64;
StringBuilder message = new StringBuilder();
message.AppendLine("Content-Type: multipart/signed; protocol=\"application/pkcs7-signature\";");
message.AppendLine(" boundary=\"__multipart-signed-boundary__\"");
message.AppendLine("Content-Transfer-Encoding: 7bit");
message.AppendLine("MIME-Version: 1.0");
message.AppendLine("Subject: " + tbMessageSubject.Text);
message.AppendLine();
message.AppendLine("--__multipart-signed-boundary__");
message.AppendLine("Content-Type: text/xml; charset=\"windows - 1251\"");
message.AppendLine("Content-Disposition: attachment; ");
message.AppendLine(" filename=\"" + Path.GetFileName(filename) + "\"");
message.AppendLine("Content-Transfer-Encoding: base64");
message.AppendLine();
var dataToSign = File.ReadAllBytes(filePath);
var base64Data = Convert.ToBase64String(dataToSign, Base64FormattingOptions.None);
var base64DataSb = new StringBuilder(base64Data);
for (int i = CHARS_IN_LINE; i < base64DataSb.Length; i += CHARS_IN_LINE + 2) // \r\n
base64DataSb.Insert(i, "\r\n");
message.AppendLine(base64DataSb.ToString());
message.AppendLine("--__multipart-signed-boundary__");
message.AppendLine("Content-Type: application/pkcs7-signature; name=\"smime.p7m\"");
message.AppendLine("Content-Transfer-Encoding: base64");
message.AppendLine("Content-Disposition: attachment; filename=smime.p7s");
message.AppendLine();
CmsSigner signer = new CmsSigner(clientCert);
SignedCms cms = new SignedCms(new ContentInfo(dataToSign), true);
cms.ComputeSignature(signer);
var signature = cms.Encode();
var base64Signature = Convert.ToBase64String(signature, Base64FormattingOptions.None);
var base64Sb = new StringBuilder(base64Signature);
for (int i = CHARS_IN_LINE; i < base64Sb.Length; i += CHARS_IN_LINE + 2) // \r\n
base64Sb.Insert(i, "\r\n");
message.AppendLine(base64Sb.ToString());
message.AppendLine();
message.AppendLine("--__multipart-signed-boundary__--");
message.AppendLine(".");
var stream = new MemoryStream(Encoding.ASCII.GetBytes(message.ToString()));
MailMessage mail = new MailMessage();
mail.From = new MailAddress(tbEmailFrom.Text);
mail.To.Add(new MailAddress(tbEmailTo.Text));
AlternateView alternateView = new AlternateView(stream, "application/pkcs7-mime; smime-type=signed-data; name=smime.p7m");
alternateView.TransferEncoding = TransferEncoding.SevenBit;
mail.AlternateViews.Add(alternateView);
SmtpClient client = new SmtpClient(host, port);
client.EnableSsl = chbUseSSL.Checked;
client.Send(mail);
Finally, the issue was in specified mediaType in AlternateView, the correct way:
AlternateView alternateView = new AlternateView(stream, "multipart/signed; protocol=\"application/pkcs7-signature\"; boundary=\"__multipart-signed-boundary__\"");
alternateView.TransferEncoding = TransferEncoding.SevenBit;
and thanks to jdweng, I was need to sign not the attachment, but the attachment with it's headers (first __multipart-signed-boundary with attachment)
Related
I'm trying to send an email with a calendar invite, as an ics file attachment. I'm using Mailkit, as my understanding is the system.net.mail is deprecated.
After playing with it, I understand that the "bodybuilder" is looking for a filepath, but that's not what I'm trying to do here, and so I'm sort of lost.
Alot of the documentation on the web seems to be outdated...
public IActionResult ThrEmail(string sendto, string subject, string body)
{
if (!String.IsNullOrEmpty(sendto))
{
//construct mail
var message = new MimeMessage();
message.From.Add(new MailboxAddress("nick", "nic-fleetwood#behr.travel"));
message.To.Add(new MailboxAddress(sendto));
message.Subject = subject;
//message.Body = new TextPart("plain")
//{
// Text = body
//};
BodyBuilder emailBody = new BodyBuilder();
emailBody.TextBody = body;
//ics file -- use yyyMMddTHHmmssZ format
StringBuilder str = new StringBuilder();
//str.AppendLine()
str.AppendLine("BEGIN:VCALENDAR");
str.AppendLine("PRODID:-//RYNE MOVING//EN");
str.AppendLine("VERSION:2.0");
str.AppendLine("METHOD:PUBLISH");
//THE EVENT
str.AppendLine("BEGIN:VEVENT");
str.AppendLine("DTSTART:20210215T100000");
str.AppendLine("DTEND:20210215T110000");
str.AppendLine("DTSTAMP:" + DateTime.Now);
str.AppendLine("UID:" + Guid.NewGuid());
str.AppendLine("CREATED:" + DateTime.Now);
str.AppendLine("X-ALT-DESC;FMTTYPE=text/html:");
//sb.AppendLine("DESCRIPTION:" + res.Details);
str.AppendLine("LAST-MODIFIED:" + DateTime.Now);
str.AppendLine("LOCATION:NYC");
str.AppendLine("SEQUENCE:0");
str.AppendLine("STATUS:CONFIRMED");
str.AppendLine("SUMMARY:");
str.AppendLine("TRANSP:OPAQUE");
str.AppendLine("END:VEVENT");
str.AppendLine("END:VCALENDAR");
emailBody.Attachments.Add(str.ToString());
//send the email
using (var client = new SmtpClient())
{
client.Connect("smtp.office365.com", 587, false);
// Note: only needed if the SMTP server requires authentication
client.Authenticate("nic-fleetwood#behr.travel", "foobar");
client.Send(message);
client.Disconnect(true);
}
}
return View();
}
AttachmentCollection has several methods to add the specified attachment.
You need to use this one Add(String, Stream):
var emailBody = new BodyBuilder();
var ics = new StringBuilder();
/* initialize calendar options */
using (var stream = new MemoryStream())
{
using (var writer = new StreamWriter(stream))
{
writer.Write(ics.ToString());
writer.Flush();
stream.Position = 0;
emailBody.Attachments.Add("calendar.ics", stream);
}
}
or Add(string, byte[]):
emailBody.Attachments.Add("calendar.ics", Encoding.UTF8.GetBytes(ics.ToString()));
I need to send the qrcode generated from the email to his gmail account. I debugged the code and checked with html visualizer and the qrcode is displaying correctly but cannot see it in gmail message
public void generate_qrcode()
{
try
{
string imgurl;
string code = txtCode.Text;
QRCodeGenerator qrGenerator = new QRCodeGenerator();
QRCodeGenerator.QRCode qrCode = qrGenerator.CreateQrCode(code, QRCodeGenerator.ECCLevel.Q);
System.Web.UI.WebControls.Image imgBarCode = new System.Web.UI.WebControls.Image();
imgBarCode.Height = 150;
imgBarCode.Width = 150;
using (Bitmap bitMap = qrCode.GetGraphic(20))
{
using (MemoryStream ms = new MemoryStream())
{
bitMap.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
byte[] byteImage = ms.ToArray();
imgBarCode.ImageUrl = "data:image/png;base64," + Convert.ToBase64String(byteImage);
imgurl = "data:image/png;base64," + Convert.ToBase64String(byteImage);
}
//plBarCode.Controls.Add(imgBarCode);
}
SendMail(imgurl);
}
catch (Exception ex)
{
}
}
public void SendMail(String imgurl)
{
string body = "Hello ,<br /><br />Please find your QRcode below<br /><br /><img src=' " + imgurl + " ' height='100' width='100'/><br/><br/>Thanks...";
SmtpClient Smtp_Server = new SmtpClient();
MailMessage e_mail = new MailMessage();
Smtp_Server.UseDefaultCredentials = false;
Smtp_Server.Credentials = new System.Net.NetworkCredential("samplemail527#gmail.com", "Sample527");
Smtp_Server.Port = 587;
Smtp_Server.EnableSsl = true;
Smtp_Server.Host = "smtp.gmail.com";
e_mail = new MailMessage();
e_mail.From = new MailAddress("samplemail527#gmail.com");
e_mail.To.Add(txtCode.Text);
e_mail.Subject = "Email Sending";
e_mail.IsBodyHtml = true;
e_mail.Body = body;
Smtp_Server.Send(e_mail);
}
Base64 images are currently not supported by most email readers. Very unfortunate. You'll need to generate an actual image and attach it to the message with a unique id (like a GUID) and then use that ID as the image tag's src along with the CID prefix.
<img src="cid:GeneratedUniqueId" alt="Your QR Code" />
Here is the good reference to embed image in an email.
Send Email with embedded Images
Maybe you should try to use AlternateView. You have to assign an Id to a resource and use that id within HTML <img> tag. The src attribute should address this Id.Like so:
<img src="cid:ResourceId" />
Don't forget to add linked resource to alternate view.
Here is the full code I used:
Byte[] iconBytes = Convert.FromBase64String(#"iVBOR IMAGE BYTES Hy4vAAN==");
System.IO.MemoryStream iconBitmap = new System.IO.MemoryStream(iconBytes);
LinkedResource iconResource = new LinkedResource(iconBitmap, MediaTypeNames.Image.Jpeg);
iconResource.ContentId = "Icon";
MailMessage msg = new MailMessage();
msg.IsBodyHtml = true;
msg.To.Add(new MailAddress("recipient#domain.com", "Recipient Name"));
msg.From = new MailAddress("sender#domain.com", "Sender Name");
msg.Subject = "Attach image to mail";
string htmlBody = #"<html><head>";
htmlBody += #"<style>";
htmlBody += #"body{ font-family:'Calibri',sans-serif; }";
htmlBody += #"</style>";
htmlBody += #"</head><body>";
htmlBody += #"<h1>The attached image is here below</h1>";
htmlBody += #"<img src='cid:" + iconResource.ContentId + #"'/>";
htmlBody += #"</body></html>";
AlternateView alternativeView = AlternateView.CreateAlternateViewFromString(htmlBody, null, MediaTypeNames.Text.Html);
alternativeView.LinkedResources.Add(iconResource);
msg.AlternateViews.Add(alternativeView);
SmtpClient client = new SmtpClient();
client.UseDefaultCredentials = true;
client.Port = 25; // You can use Port 25 if 587 is blocked
client.Host = "smtp.yourhost.com";
client.Send(msg);
I how can I have email encrypted and signed ?
Seem that I can either encrypt or signed but not both.
Below is my code
public void SendEncryptedEmail04(
//string SigningCertPath, string EncryptingCertPath,
String sender, String To,
string Subject, string Body,
// string SmtpServer, int SmtpPort, bool HTML)
bool HTML)
{
X509Certificate2 SignCert = new X509Certificate2("D:\\certwithprivatekey.pfx", "password"));
X509Certificate2 EncryptCert = new X509Certificate2("abc#gmail.crt", "");
StringBuilder Message = new StringBuilder();
Message.AppendLine("Content-Type: text/" + ((HTML) ? "html" : "plain") +
"; charset=\"iso-8859-1\"");
Message.AppendLine("Content-Transfer-Encoding: 7bit");
Message.AppendLine();
Message.AppendLine(Body);
byte[] BodyBytes = Encoding.ASCII.GetBytes(Message.ToString());
EnvelopedCms ECms = new EnvelopedCms(new ContentInfo(BodyBytes));
CmsRecipient Recipient = new CmsRecipient(
SubjectIdentifierType.IssuerAndSerialNumber, EncryptCert);
ECms.Encrypt(Recipient);
byte[] EncryptedBytes = ECms.Encode();
SignedCms Cms = new SignedCms(new ContentInfo(EncryptedBytes));
CmsSigner Signer = new CmsSigner
(SubjectIdentifierType.IssuerAndSerialNumber, SignCert);
Cms.ComputeSignature(Signer);
byte[] SignedBytes = Cms.Encode();
MailMessage Msg = new MailMessage();
Msg.To.Add(new MailAddress(To));
Msg.From = new MailAddress(sender);
Msg.Subject = Subject;
//MemoryStream ms = new MemoryStream(EncryptedBytes);
MemoryStream ms = new MemoryStream(SignedBytes);
AlternateView av = new AlternateView(ms,
"application/pkcs7-mime; smime-type=signed-data;name=smime.p7m");
//"application/pkcs7-mime; smime-type=enveloped-data;name=smime.p7m");
Msg.AlternateViews.Add(av);
SmtpClient smtp = new SmtpClient(emailServer, 25);
smtp.UseDefaultCredentials = false;
smtp.Credentials = new System.Net.NetworkCredential(sender,"xxxx"));
smtp.Send(Msg);
}
The email received by ThunderBird has the following
However, I changed to
MemoryStream ms = new MemoryStream(EncryptedBytes);
I have
How can I have encrypted and signed ?
Thanks
I have method, which sign my email by certificate. This work fine if the line with adding an attachment is commented out.
private static void mailer()
{
MailAddress from = new MailAddress("test#email.cz");
MailAddress to = new MailAddress("test#email.cz");
MailMessage message = new MailMessage(from, to);
message.Subject = "Test subject email";
message.IsBodyHtml = true;
string body = "Content-Type: text/html; charset=utf-8 \r\n Content-Transfer-Encoding: 7bit \r\n\r\n";
body += " Email body signed by certificate ";
byte[] messageData = Encoding.ASCII.GetBytes(body);
SignedCms Cms = new SignedCms(new ContentInfo(messageData));
RSACryptoServiceProvider csp = null;
X509Certificate2 cert = new X509Certificate2(#"C:\TFS\cert_test.p12", "password");
if (cert != null)
{
csp = (RSACryptoServiceProvider)cert.PrivateKey;
}
else
{
throw new Exception("Valid certificate was not found");
}
CmsSigner Signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, cert);
Cms.ComputeSignature(Signer);
byte[] SignedBytes = Cms.Encode();
MemoryStream signedStream = new MemoryStream(SignedBytes);
AlternateView signedView = new AlternateView(signedStream, "application/pkcs7-mime; smime-type=signed-data; name=sig.p7m");
message.AlternateViews.Add(signedView);
ContentType contentType = new ContentType();
contentType.MediaType = MediaTypeNames.Application.Octet;
contentType.Name = "test.xml";
MemoryStream ms = new MemoryStream(fileData);
ms.Position = 0;
Attachment attachment = new Attachment(ms, contentType);
//message.Attachments.Add(attachment);
SmtpClient client = new SmtpClient("smtp.server.net");
client.UseDefaultCredentials = false;
client.Credentials = new System.Net.NetworkCredential("smtp.server.net", "password", "user");
try
{
client.Send(message);
}
catch (Exception ex)
{
throw ex;
}
}
But I must add attachments (3 or more) to email. When I add an attachment (just one attachment yet), the email no longer shows as signed and the body of email doesn't show up. I find some samples of my problem, but it useless for me.
I don't encrypt the body of email and an attachment. I need only sign an email by certificate.
Thanks for help.
EDIT: Sorry, I had commented out the line with adding signed AlternateView. Right commented out the line is with adding attachment to the email.
I need to send a metting request for Outlook 2013 with a file attachment in C#.
I have already working code to send a request but i really have no idea how to add an attachment to this.
MailMessage message = new MailMessage();
message.To.Add("someMail");
message.Subject = "Test";
message.From = new MailAddress("myMail");
message.Body = "Test Test";
SmtpClient smtp = new SmtpClient("mySmtp");
StringBuilder str = new StringBuilder();
str.AppendLine("BEGIN:VCALENDAR");
str.AppendLine("VERSION:2.0");
str.AppendLine("METHOD:REQUEST");
str.AppendLine("BEGIN:VEVENT");
str.AppendLine(string.Format("DTSTAMP:{0:yyyyMMddTHHmmZ}", DateTime.UtcNow));
str.AppendLine(string.Format("DTSTART:{0}", startTime));
str.AppendLine(string.Format("DTEND:{0}", endTime));
str.AppendLine("LOCATION: Zürich");
str.AppendLine(string.Format("UID:{0}", Guid.NewGuid()));
str.AppendLine(string.Format("DESCRIPTION:{0}", message.Body));
str.AppendLine(string.Format("X-ALT-DESC;FMTTYPE=text/html:{0}", message.Body));
str.AppendLine(string.Format("SUMMARY:{0}", message.Subject));
str.AppendLine(string.Format("ORGANIZER:MAILTO:{0}", message.From.Address));
str.AppendLine(string.Format("ATTENDEE;CN=\"{0}\";RSVP=TRUE:mailto:{1}", message.To[0].DisplayName, message.To[0].Address));
str.AppendLine("BEGIN:VALARM");
str.AppendLine("TRIGGER:-PT15M");
str.AppendLine("ACTION:DISPLAY");
str.AppendLine("DESCRIPTION:Reminder");
str.AppendLine("END:VALARM");
str.AppendLine("END:VEVENT");
str.AppendLine("END:VCALENDAR");
System.Net.Mime.ContentType ct = new System.Net.Mime.ContentType("text/calendar");
ct.Parameters.Add("method", "REQUEST");
AlternateView avCal = AlternateView.CreateAlternateViewFromString(str.ToString(), ct);
message.AlternateViews.Add(avCal);
smtp.Send(message);
I hope someone can help
Thanks Ego
You can try Below code
MailMessage msg = new MailMessage();
byte[] byteArray = Encoding.UTF8.GetBytes(str.ToString());
Stream contentStream = new MemoryStream(byteArray);
Attachment attachment = new Attachment(contentStream, "calendar.ics", "text/calendar");
attachment.TransferEncoding = System.Net.Mime.TransferEncoding.Base64;
msg.Attachments.Add(attachment);