18. januar 2010 by Thomas Stern
Sending encrypted email - C#
Okay so now we are done and have build the content of the mail.
Now we need to sign it. If you dont want to sign the mail skip this
part and jum straight to encrypting "which will be the next post".
I've found quite a few post on tje net on howto sign at encrypt
mails but none of them worked for me. I've had to look at package
and mail content to analyze how to to build the right content and
signing part as well. For the signing part we off course need a
certificate with access to the private key.
Link to part
1,
part 2,
part 3
NOTE AGAIN IF THE PROCESS RUNNING THE CODE DOESN'T HAVE
ACCESS TO THE PRIVATE KEY YOU GET A KEYSET DOESN'T MATCH OR PRIVATE
KEY COULD NOT BE FOUND ERROR WHEN TRYING TO SIGN THE
MAIL.
See earlier post on howo to setup up security for
certificates.
Now finaly to some code. First we gonna make a little helper
method for signing the content. Also note that I use a simple
helper function "GetCert()" all i does is returning the same
certificate since i use the same certificate for signing and
ecrypting. You should offcourse use the methode shown in part 2 of
this series. My get cert is a like but just with an hardcode serial
number. this should be replaced with the serial number for your
certificate.
The little helper function:
public static X509Certificate2 GetCert()
{
//Sets up a new store to look for at certificat in.
X509Store localStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
localStore.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
try
{
//NOTE FALSE IS ONLY USED FOR TESTS SHOULD BE CHANGED TO true
X509Certificate2Collection matches =
localStore.Certificates.Find(X509FindType.FindBySerialNumber, "a0 51 bf 0a bc 4a 11 8b 41 9d 56 47 92 b2 34 6c", false);
if (matches.Count > 0)
{
return matches[0];
}
else
{
return null;
}
}
finally
{
localStore.Close();
}
}
And here is how crypting part works. The signature calculation.
Note that the function takes two certifactes one for signing the
mail and the certificates used for encrypting the mail. Theese two
could be the same. But you might have an certificat only for
signing and the you need the extra one for encrypting the mail.
public byte[] GetSignature(string message, X509Certificate2 signingCertificate, X509Certificate2 encryptionCertificate)
{
byte[] messageBytes = Encoding.ASCII.GetBytes(message);
SignedCms signedCms = new SignedCms(new ContentInfo(messageBytes), true);
CmsSigner cmsSigner = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, signingCertificate);
cmsSigner.IncludeOption = X509IncludeOption.WholeChain;
if (encryptionCertificate != null)
{
cmsSigner.Certificates.Add(encryptionCertificate);
}
Pkcs9SigningTime signingTime = new Pkcs9SigningTime();
cmsSigner.SignedAttributes.Add(signingTime);
signedCms.ComputeSignature(cmsSigner, false);
return signedCms.Encode();
}
With this function we can start adding new boundary to the
content so it will be correct signed. This part was nesscary for
me otherwise i could either encryot the mail or sign it not
both. So before we sign the mail we add som new boundaries and
finaly we gonna add the calculated signature. Again this function
accept the content from we made in the laste post so as input you
could give the method the result from "buildcontent()". Againg note
this is build in simple function so we can got a simple reference
when we wnat to encrypt the content.
public string signed(string Content)
{
string signatureBoundry = "--PTBoundry=2";
string signatureBoundry2 = "--PTBoundry=3";
StringBuilder fullUnsignedMessageBuilder = new StringBuilder();
fullUnsignedMessageBuilder.Append("Content-Type: ");
fullUnsignedMessageBuilder.Append("multipart/mixed;");
fullUnsignedMessageBuilder.Append(" boundary=\"");
fullUnsignedMessageBuilder.Append(signatureBoundry);
fullUnsignedMessageBuilder.Append("\"\r\n");
fullUnsignedMessageBuilder.Append("Content-Transfer-Encoding: ");
fullUnsignedMessageBuilder.Append(TransferEncoding.SevenBit);
fullUnsignedMessageBuilder.Append("\r\n");
fullUnsignedMessageBuilder.Append(Content);
string fullUnsignedMessage = fullUnsignedMessageBuilder.ToString();
byte[] signature = GetSignature(fullUnsignedMessage, GetCert(), GetCert());
StringBuilder signedMessageBuilder = new StringBuilder();
signedMessageBuilder.Append("--");
signedMessageBuilder.Append(signatureBoundry2);
signedMessageBuilder.Append("\r\n");
signedMessageBuilder.Append(fullUnsignedMessage);
signedMessageBuilder.Append("\r\n");
signedMessageBuilder.Append("--");
signedMessageBuilder.Append(signatureBoundry2);
signedMessageBuilder.Append("\r\n");
signedMessageBuilder.Append("Content-Type: application/x-pkcs7-signature; name=\"smime.p7s\"\r\n");
signedMessageBuilder.Append("Content-Transfer-Encoding: base64\r\n");
signedMessageBuilder.Append("Content-Disposition: attachment; filename=\"smime.p7s\"\r\n\r\n");
signedMessageBuilder.Append(Convert.ToBase64String(signature));
signedMessageBuilder.Append("\r\n\r\n");
signedMessageBuilder.Append("--");
signedMessageBuilder.Append(signatureBoundry2);
signedMessageBuilder.Append("--\r\n");
return signedMessageBuilder.ToString();
}
It is now posssible to return and signed message string ready to
be encrypted by calling :
signed(buildcontent())
Do note the reference the tells we the content boundary start is
different form the boundary for the signing part, and boundary for
the signature is offcourse the same as for the signing boundary. So
it is important to have acces to the different boundaries for
between each steps. And when we want to encryp the content we also
need the referencs for the signing boundary.
Okay with the content now signed we should be ready to encrypt
the mail and send it see next post Where we also will look at how
the final content should look just before it is send, This will
also help one to build the mailcontent for your own purposes.
Now whe signed the mail.