locked
Sending S/MIME encrypted email using C#

    Question

  • Hi All,

    Can someone let me know or provide me with sample implementation of how to send S/MIME encryption to send Emails using C#?

    ---Thanks

    RNilesh

    Tuesday, November 21, 2006 7:09 AM

Answers

  • We don't have a fully supported way of sending S/MIMe messages.
    However I was curious about this and cooked up a way to do this.
    Let me know how this works out

    1. First you need a Secure Email certificate issued to the email address you want to use.
    Lets say thisis mymail.somecompany.com. Your cert should have this in the subject name
    and should be enabled for secure email.

    2. Next you need to programatically get the certificate or load from a pfx file like
          X509Store store = new X509Store(StoreLocation.CurrentUser);
          store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
          X509Certificate2Collection certs = store.Certificates;
          X509Certificate2 certificate = null;
          foreach (X509Certificate2 cert in certs)
          {
              if (cert.Subject.IndexOf("
    mymail@somecompany.com") >= 0)
              {
                  certificate = cert;
                  break;
              }
          }

    3. Next you need to have an entity that you want to sign and send.
          string strbody = @"Content-Type: text/plain;charset=""iso-8859-1""
    Content-Transfer-Encoding: quoted-printable

    This is a test s/mime message";
    This is where it gets a little not very intuitive since there is no programatic way of creating the email entity you want to send
    Note the headers and a series of two \r\n before the entity body "this is a test s/mime" message begins

    3. Next you need to generate a signed envelope for this content

          byte[] data = Encoding.ASCII.GetBytes(strbody);
          ContentInfo content = new ContentInfo(data);
          SignedCms signedCms = new SignedCms(content, false);
          CmsSigner signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, certificate);     
          signedCms.ComputeSignature(signer);
          byte[] signedbytes = signedCms.Encode();     

    4. Now that you have the content you want to send is signed with the certificate of your choice you need to
    create a mail message object and create an alternate view and add this to your alternate view collection

          MailMessage msg = new MailMessage();
          msg.From = new MailAddress("
    <from>");
          msg.To.Add(new MailAddress("
    <to>"));
          msg.Subject = "test s/mime";
         
          MemoryStream ms = new MemoryStream(signedbytes);
          AlternateView av = new AlternateView(ms, "application/pkcs7-mime; smime-type=signed-data;name=smime.p7m");
          msg.AlternateViews.Add(av);

    5. Now that you have the message prepared you can just send it
          SmtpClient client = new SmtpClient("smtphost", 25);
          client.UseDefaultCredentials = true;
          client.Send(msg);

    This is a kind of hack at this time and requires some manual preparation of the entity body
    you want to sign. I need to do some more research on this and find out if there is a better way of doing this.

     

    Monday, November 27, 2006 9:14 PM

All replies

  • We don't have a fully supported way of sending S/MIMe messages.
    However I was curious about this and cooked up a way to do this.
    Let me know how this works out

    1. First you need a Secure Email certificate issued to the email address you want to use.
    Lets say thisis mymail.somecompany.com. Your cert should have this in the subject name
    and should be enabled for secure email.

    2. Next you need to programatically get the certificate or load from a pfx file like
          X509Store store = new X509Store(StoreLocation.CurrentUser);
          store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
          X509Certificate2Collection certs = store.Certificates;
          X509Certificate2 certificate = null;
          foreach (X509Certificate2 cert in certs)
          {
              if (cert.Subject.IndexOf("
    mymail@somecompany.com") >= 0)
              {
                  certificate = cert;
                  break;
              }
          }

    3. Next you need to have an entity that you want to sign and send.
          string strbody = @"Content-Type: text/plain;charset=""iso-8859-1""
    Content-Transfer-Encoding: quoted-printable

    This is a test s/mime message";
    This is where it gets a little not very intuitive since there is no programatic way of creating the email entity you want to send
    Note the headers and a series of two \r\n before the entity body "this is a test s/mime" message begins

    3. Next you need to generate a signed envelope for this content

          byte[] data = Encoding.ASCII.GetBytes(strbody);
          ContentInfo content = new ContentInfo(data);
          SignedCms signedCms = new SignedCms(content, false);
          CmsSigner signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, certificate);     
          signedCms.ComputeSignature(signer);
          byte[] signedbytes = signedCms.Encode();     

    4. Now that you have the content you want to send is signed with the certificate of your choice you need to
    create a mail message object and create an alternate view and add this to your alternate view collection

          MailMessage msg = new MailMessage();
          msg.From = new MailAddress("
    <from>");
          msg.To.Add(new MailAddress("
    <to>"));
          msg.Subject = "test s/mime";
         
          MemoryStream ms = new MemoryStream(signedbytes);
          AlternateView av = new AlternateView(ms, "application/pkcs7-mime; smime-type=signed-data;name=smime.p7m");
          msg.AlternateViews.Add(av);

    5. Now that you have the message prepared you can just send it
          SmtpClient client = new SmtpClient("smtphost", 25);
          client.UseDefaultCredentials = true;
          client.Send(msg);

    This is a kind of hack at this time and requires some manual preparation of the entity body
    you want to sign. I need to do some more research on this and find out if there is a better way of doing this.

     

    Monday, November 27, 2006 9:14 PM
  • Thank your for the eMail. I Know how to sign and encrypt a "simple email".  ( see dotnetpro 2006

  • Sicherheit mit Zertifikaten - Signatur und Verschlüsselung mit VB.NET 2.0
    Die Grundlagen von Signatur und Verschlüsselung waren in den letzten Jahren Gegenstand zahlreicher Veröffentlichung.  Den meisten interessierten Lesern ist daher vertraut, dass mit privaten und öffentlichen Schlüsseln gearbeitet wird und dass letztere über Zertifikate verteilt werden. Wie man jedoch diese Zertifikate erhält und wie sie genutzt werden können, ist nur noch wenigen Personen bekannt. In diesem Beitrag soll gezeigt werden, wie ausschließlich mit VB.NET 2.0 diese Sicherheitstechnik genutzt werden kann.
    dotnetpro  07/2006 Seite 90 ff.
     
  • Streng vertraulich -  eMails  signieren und verschlüsseln
    Für eMails steht das Datenformat MIME ( Multipurpose Internet Mail Extension ) zur Verfügung, das keine Absicherung von Nachrichten durch Zertifikate unterstützt. Hierfür wurde eine Erweiterung zu S/MIME ( Secure MIME) vorgenommen. beschrieben ist. Das Outlook-Objekt Modell ist seit Erscheinen mehrmals ergänzt worden, doch fehlt immer noch die Unterstützung von S/MIME. Unter Nutzung von Collaboration Data Objects ( CDO )und ActiveX Data Objects ( ADO ) können jedoch signierte und/oder verschlüsselte eMails erzeugt werden. Nach einer kurzen Einführung in die Grundlagen von MIME wird hierfür die Programmierung gezeigt, die beispielsweise die Realisierung eines 4-Augenprinzips derart gestattet, dass eMails 2-fach signiert oder verschlüsselt werden müssen, so dass für den Versand 2 oder das Lesen 2 verschiedene Zertifikate und damit 2 unterschiedliche Chipkarten erforderlich sind.
    dotnetpro  08/2006 Seite 118 ff.
  • )

    But how to add for examples an excel file and to sign and encrypt the message and the file `??

    Thanks

     

    Klaus-Dieter Brinkmann

Monday, February 19, 2007 4:07 PM
  • Hi Prasad,

     

    It worked for me, but my requirement is bit different.

    i need to encrypt body as well as attachment while sending the email.

    i tried attaching the file, but without proper certificate installed in client's computer i am able to see the attachment but not the body of the email..

    i mean, i can open the attachment file without certificate..

    please help me in this..

    Thanks,

    Rajesh.

    Monday, March 05, 2007 10:55 PM
  • Hi Rajesh!

    Did you get the solution for this (encrypt body and attachment as well?)
    I need to use this solution too.

    Thanks,

    Felipe
    Sunday, July 29, 2007 8:37 PM
  • I know I'm late incoming to this thread but your code sample above appears to be an example of signing an email message, not encrypting message content. Furthermore, the certificate required for signing is the senders' certificate which must contains the senders' private key which better not be included in the email payload, i.e. not included in the "certificate" object. I don't see where you do include "certificate" in the payload though. However, with the original message text and signature, you have include the sender's public key via the sender's public-key-only certificate and optionally, any and all intermediate and rooting CA certs. Right? I don't  think you got the sample code right.

     

    Can I get sample code of s/MIME signing only using C# .NET 1.0/1.1 FCL?

     

    Wednesday, August 22, 2007 7:47 PM
  • Yes, sorry about that Martin, I meant to hang my previous reply from Gorti's initial sample code post. But "Rebex.net" is a great help. Thanks!

     

    Monday, August 27, 2007 4:03 PM
  •  

    Hi there,

     

    I am having some hassle sending encrypted mails with attachments.

     

    I can send emails encrypted with a digital cert with no attachments fine by using the following code:

     

    //Build the basic message headers

    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);

    //Convert the body to bytes

    byte[] BodyBytes = Encoding.ASCII.GetBytes(Message.ToString());

    //Encrypt the body

    EnvelopedCms ECms = new EnvelopedCms(new ContentInfo(BodyBytes));

    CmsRecipient Recipient = new CmsRecipient(

    SubjectIdentifierType.IssuerAndSerialNumber, EncryptCert);

    ECms.Encrypt(Recipient);

    byte[] EncryptedBytes = ECms.Encode();

     

    MailMessage Msg = new MailMessage();

    Msg.To.Add(new MailAddress("rcpt@domain.com"));

    Msg.From = new MailAddress("sender@domain.com");

    Msg.Subject = Subject;

     

    MemoryStream ms = new MemoryStream(EncryptedBytes);

    AlternateView av = new AlternateView(ms,

    "application/pkcs7-mime; smime-type=enveloped-data;name=smime.p7m; content-transfer-encoding=Base64; content-disposition=attachment; fileName=smime.p7m;");

    Msg.AlternateViews.Add(av);

     

    The problem is that as soon as I add an attachment,

    Msg.Attachments.Add(new Attachment("C:/Test Attachment.txt", MediaTypeNames.Application.Octet));

    the email no longer shows as an encrypted mail. You can open the attachment fine. Although the body of the attachment does not show up.

     

    I am also signing the email but Outlook does not show the signature. I am adding the signature generated by a cert as follows:

     

    //Sign the email

    SignedCms Cms = new SignedCms(new ContentInfo(EncryptedBytes));

    CmsSigner Signer = new CmsSigner

    (SubjectIdentifierType.IssuerAndSerialNumber, SignCert);

    Cms.ComputeSignature(Signer);

    byte[] SignedBytes = Cms.Encode();

     

    MemoryStream ms1 = new MemoryStream(SignedBytes);

    AlternateView av1 = new AlternateView(ms,

    "application/pkcs7-mime; smime-type=signed-data;name=smime.p7m; content-transfer-encoding=Base64; content-disposition=attachment; fileName=smime.p7m;");

    Msg.AlternateViews.Add(av1);

     

     

    If anyone can help me to get the signing and adding attachments working, it would be hugely appreciated!

     

    Thanks

    Stephen

    Friday, October 19, 2007 12:23 PM
  • Hi Stephen.

     

    Did you get any solution on sending attachments within an encrypted email (your last post) ?

    I am having the same issue.

     

    Thanks,

     

    Felipe

     

     

     

    Thursday, October 25, 2007 12:36 PM
  • Hi Felipe,

     

    Yes I found a solution.

     

    Its a bit tricky, you have to build the actual email content with attachments from scratch then enjcrypt that.

     

    So my code looks something like this:

     

    StringBuilder Message = new StringBuilder();

    Message.AppendLine("content-type: multipart/mixed;");

    Message.AppendLine(" boundary=\"----=_NextPart_414_0c36_f93e4f59.6ee76195_.MIX\"");

    Message.AppendLine();

    Message.AppendLine("This is a multi-part message in MIME format.");

    Message.AppendLine();

    Message.AppendLine("------=_NextPart_414_0c36_f93e4f59.6ee76195_.MIX");

    Message.AppendLine("content-type: text/plain; charset=\"us-ascii\"");

    Message.AppendLine("content-transfer-encoding: 7bit");

    Message.AppendLine();

    Message.AppendLine(Body);

    Message.AppendLine("------=_NextPart_414_0c36_f93e4f59.6ee76195_.MIX");

    Message.AppendLine("content-disposition: attachment; filename=\"" + attachment.FileName + "\"");

    Message.AppendLine("content-transfer-encoding: base64");

    Message.AppendLine("content-type: application/octet;");

    Message.AppendLine(" name=\"" + attachment.FileName + "\"");

    Message.AppendLine();

    Message.AppendLine(Convert.ToBase64String(attachment.RawData));

    Message.AppendLine("------=_NextPart_414_0c36_f93e4f59.6ee76195_.MIX--");

     

     

    Then I convert the string result to a byte[] and encrypt it.

     

    Cheers

    Stephen

    Friday, October 26, 2007 6:31 AM
  •  

    I am still not able to get this working...

     

    I do the above as you have said.. as shown below...

     

    Message.AppendLine("content-type: multipart/mixed;");

    Message.AppendLine(" boundary=\"----=_NextPart_414_0c36_f93e4f59.6ee76195_.MIX\"");

    Message.AppendLine();

    Message.AppendLine("This is a multi-part message in MIME format.");

    Message.AppendLine();

    Message.AppendLine("------=_NextPart_414_0c36_f93e4f59.6ee76195_.MIX");

    Message.AppendLine("content-type: text/plain; charset=\"us-ascii\"");

    Message.AppendLine("content-transfer-encoding: 7bit");

    Message.AppendLine();

    Message.AppendLine("Messge test5");

    Message.AppendLine("------=_NextPart_414_0c36_f93e4f59.6ee76195_.MIX");

    Message.AppendLine("content-disposition: attachment; filename=\"" + "x.txt" + "\"");

    Message.AppendLine("content-transfer-encoding: base64");

    Message.AppendLine("content-type: application/edine;\n name=\"" + "E_x.txt" + "\"");

    Message.AppendLine();

    string data = File.ReadAllText("C:\\Temp\\x.txt");

    Message.AppendLine(Convert.ToBase64String(Encoding.ASCII.GetBytes(data)));

    Message.AppendLine();

    Message.AppendLine("------=_NextPart_414_0c36_f93e4f59.6ee76195_.MIX--");

     

    byte[] BodyBytes = Encoding.ASCII.GetBytes(Message.ToString());

    EnvelopedCms ECms = new EnvelopedCms(new ContentInfo(BodyBytes));

    CmsRecipient Recipient = new CmsRecipient(

    SubjectIdentifierType.SubjectKeyIdentifier, EncryptCert);

    ECms.Encrypt(Recipient);

    byte[] EncryptedBytes = ECms.Encode();

     

    SignedCms Cms = new SignedCms(new ContentInfo(EncryptedBytes));

    CmsSigner Signer = new CmsSigner

    (SubjectIdentifierType.SubjectKeyIdentifier, SignCert);

    Pkcs9SigningTime time = new Pkcs9SigningTime();

    Signer.SignedAttributes.Add(time);

    Cms.ComputeSignature(Signer);

    byte[] SignedBytes = Cms.Encode();

     

    MemoryStream ms1 = new MemoryStream(SignedBytes);

    AlternateView av1 = new AlternateView(ms1,

    "application/pkcs7-mime; smime-type=signed-data;name=smime.p7m; content-transfer-encoding=Base64; content-disposition=attachment; fileName=smime.p7m;");

     

     

    Should I have set anything different, or aam I missing something?

     

    thanks

    CR

     

    Monday, November 19, 2007 4:08 PM
  •  

    Hi Stephen.

     

    Did you get solution with signed and encrypted email in Outlook (did Outlook show email like signed and encypted email)?

     

    Thanks, Peter

    Saturday, February 16, 2008 1:56 PM
  • Hi All,

     

    can we add these encrypted files using code like

     

    message.attachments.add(filename);

     

    when I try to delete the dynamically created attachment..I dont find attachment in mail.attachment[] collectoin.

    has anyboy idea of how can we find how many files are attached in this scenario.

     

    Looking forward to all you champs.

     

    Thanks,

    Jayant

     

    Monday, March 03, 2008 4:04 PM
  • Hi,
    Has anyone got this thing working - I mean sending signed and encrypted message so that Outlook can recognize them propetly. I still can't find the answer. My last post can be found here :http://social.msdn.microsoft.com/Forums/en-US/ncl/thread/ca93b37d-b8ad-4793-96a5-46d663c6cfad

    Please help!
    Friday, February 13, 2009 9:38 AM
  • Hi everyone,

    I have the same problem previously described here http://social.microsoft.com/Forums/en-US/netfxnetcom/thread/0d52d1fa-983a-40b8-a81a-b5a13cd58fdd, but the thread seems to be inactive. Does anyone know how to include "SMIME Capabilites" and "Encryption Key Preference" attributes to your SMime signature?

    Thanks
      Binus
    Thursday, April 02, 2009 12:53 PM
  • I have to attach a PDF File here which gets attached with the following code

                X509Certificate2 SignCert = new X509Certificate2(SigningCertPath, strPassword );
    X509Certificate2 EncryptCert = new X509Certificate2(EncryptingCertPath, "");

    Configuration config = ConfigurationManager.OpenExeConfiguration(Application.ExecutablePath);
    string strSMTP = config.AppSettings.Settings["SMTPHOST"].Value.ToString();
    string strPort = config.AppSettings.Settings["SMTPPORT"].Value.ToString();

    StringBuilder Message = new StringBuilder();
    Message.AppendLine("content-type: multipart/mixed;");
    Message.AppendLine(" boundary=\"-this-is-the-boundary-\"");
    Message.AppendLine();
    Message.AppendLine("This is a multi-part message in MIME format.");
    Message.AppendLine();
    Message.AppendLine("-this-is-the-boundary-");
    Message.AppendLine("content-type: text/plain; charset=\"us-ascii\"");
    Message.AppendLine("content-transfer-encoding: 7bit");
    Message.AppendLine();
    Message.AppendLine(theEmail.Body);


    foreach ( Attachment aFile in theEmail.Attachments)
    {
    String fullPathToFile = NSDLog.FilePath + aFile.Name;
    Message.AppendLine("-this-is-the-boundary-");
    Message.AppendLine("content-disposition: attachment; filename=\"" + aFile.Name + "\"" + Environment.NewLine );
    Message.AppendLine("content-transfer-encoding: base64");
    Message.AppendLine("content-type: application/octet;");
    Message.AppendLine(" name=\"" + aFile.Name + "\"");
    Message.AppendLine();
    string AttData = File.ReadAllText(fullPathToFile);
    Message.AppendLine(Convert.ToBase64String(Encoding.ASCII.GetBytes(AttData)));
    }

    Message.AppendLine("-this-is-the-boundary-");
    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.Subject = theEmail.Subject;
    Msg.To.Clear();

    Msg.From = theEmail.From;

    foreach (MailAddress mAddress in theEmail.To)
    {
    Msg.To.Add(mAddress);
    NSDLog.Debugger("New Encryption for " + mAddress + " using " + EncryptingCertPath);
    }

    foreach (MailAddress mAddress in theEmail.CC)
    {
    Msg.CC.Add(mAddress);
    }

    foreach (MailAddress mAddress in theEmail.Bcc)
    {
    Msg.Bcc.Add(mAddress);
    }




    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(strSMTP , int.Parse (strPort ));
    smtp.UseDefaultCredentials = true;
    smtp.Send(Msg);
    And the email is sent encrypted however the attachments cannot be opened and the error message says decoding was not right.

    Please Help!!
    Tuesday, June 02, 2009 11:54 AM
  • Check it out. Very nice code.
    http://www.codeproject.com/KB/security/CPI_NET_SecureMail.aspx

    Jason Camp, MCSE, MCSD, MCDBA, MCPD: Web, MCAD, MCSA, CISSP, SCSA
    Tuesday, August 25, 2009 1:20 PM
  • are there any updates to this with .NET 4? I have my certificates stored in Active Directory - does that matter? what are other people using for this?
    Thursday, February 24, 2011 7:56 PM
  • I tried the code project and from my end it does not support attachments. I can encrypt a message and that works fine. I can send an attachment using their library and that works fine too. But as soon as I encrypt when sending an attachment I only receive the body of the email (no attachment).
    Friday, April 08, 2011 2:53 PM
  • Hello everyone,

    I am looking for a C# function to sign an XML file, this function must replace the following command :
     

    openssl smime -sign -in toto.xml  -text -out toto.etape2 -signer user-cert.pem -inkey user-key.pem 


    Thank you for your answers,

    Tuesday, August 09, 2011 6:10 PM
  • Taka a look at Mail.dll:

    http://www.limilabs.com/blog/send-encrypted-email-using-smime

    Creating encrypted and signed email is dead easy:

    // C# version
    
    IMail email = Mail
        .Html("<html><body>Encrypted and signed</body></html>")
        .Subject("Encrypted and signed")
        .From(new MailBox("email@in-the-certificate.com", "Alice"))
        .To(new MailBox("bob@mail.com", "Bob"))
        .AddAttachment(@"c:report_2010.pdf")
        .SignWith(new X509Certificate2("SignCertificate.pfx", ""))
        .EncryptWith(new X509Certificate2("EncryptCertificate.pfx", ""))
        .EncryptWith(new X509Certificate2("BobsCertificate.pfx", ""))
        .Create();
    
    using (Smtp client = new Smtp())
    {
        client.Connect("smtp.example.com"); // or ConnectSSL
        client.UseBestLogin("user", "password");
        client.SendMessage(email);
        client.Close();
    }
    

    You can download it here:

    http://www.limilabs.com/mail/download

    Friday, April 13, 2012 9:04 PM