I am playing with .Net2.0 Cryptography, and want to Implement different scenarios. Let me start with this one and please comment if you know of a better solution.
Scenario 1: I want to setup a Public Key Interface, where Sender signs a Message using Senders Certificate and then Encrypts / Envelops it using Recipients Certificate and then Send the Encrypted Message.
Solution: I am writing a Sender WebApplication where on click of a Button, Recipients Certificate is downloaded from a WebService Store and then signed and encrypted and sent to the Webservice. The Webservice then saves the encrypted Message in the c:\temp folder.
I ll be using the new X509Certificate2 that ships with .Net2.0.
Step 1 : I created 2 Certificate one for Recipient and one for Sender using the makecert tool.
C:\Program Files\Microsoft Visual Studio 8\VC>makecert -n "CN=SmartCryptoGraphy
Recipient1" -ss My
Succeeded
C:\Program Files\Microsoft Visual Studio 8\VC>makecert -n "CN=SmartCryptoGraphy
Signer1" -ss My
Succeeded
for more details on the Certificate Creation Tool please refer to this link.
http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/sag_cmprocsimport.mspx?mfr=true
Step 2: Create a WebService with the implementation of 2 Methods:
[WebMethod]
public byte[] GetRecipientCertificateFromStore(string recipientName)
{
}
[WebMethod]
public bool SendMessage(string message)
{
}
GetRecipientCertificateFromStore will return Recipients Certificate from My store. Here is the Implementation.
[WebMethod]
public byte[] GetRecipientCertificateFromStore(string recipientName)
{
//Do the searching of Certificate and send it back
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certColl =
store.Certificates.Find(X509FindType.FindBySubjectName,
recipientName, false);
if (certColl.Count == 0)
{
store.Close();
return null;
}
store.Close();
return certColl[0].Export(X509ContentType.Cert);
}
The code is self explanatory it instantiates a X509Store object pointing to StoreName.My where I have previously saved the Certificates. Then it searches the store by the recipientName using the store.Certificates.Find method, and when it finds the desired certificate, returns byte[] using the X509Certificate2.Export Method.
SendMessage will recive encrypted message and simply store it to c:\temp folder. Here is the implementation:
[WebMethod]
public bool SendMessage(string message)
{
Guid id = Guid.NewGuid();
try
{
using (System.IO.StreamWriter sw = System.IO.File.CreateText(string.Format(@"C:\temp\message_{0}.txt",id.ToString())))
{
sw.Write(message);
}
}
catch
{
return false;
}
return true;
}
The above code is self explanatory.
Step 3: Creating the Sender WebApplication
I chosen a WebApplication for Sender as in future Scenarios I want to implement SSL, RequestClientCertificate etc.
Now the WebService can be communicated via any client but in future scenarios I want to restrict that communication too and want to implement autorized clients access to the webservice using SSL and Certificates...
Anyways now its simple and the future scenarios will be more complicated....Now I created I WebPage with 2 TextBox and a Send Button.
<form id="form1" runat="server">
<div>
Recipient Name<br />
<asp:TextBox ID="tbRecipientName" runat="server"></asp:TextBox><br />
Enter Message<br />
<asp:TextBox ID="tbMessage" runat="server" Height="88px" TextMode="MultiLine"></asp:TextBox><br />
<asp:Button ID="btnSend" runat="server" OnClick="btnSend_Click" Text="Send" /><br />
<asp:Label ID="lblReport" runat="server"></asp:Label></div>
</form>
On Button Click the web application loads the Signers Certificate then contacts the previously created webservice and requests the Recipient Certificate......by the RecipientName entered in the textbox.
After getting both Certificates it signs the message entered in the textbox, and then encrypts and envelops it using the Recipients Certificate and then Sends to the WebService.....The source code looks like this.
protected void btnSend_Click(object sender, EventArgs e)
{
//Get Signer Certificate
X509Certificate2 signerCertificate = GetSignerCert();
if (signerCertificate == null)
return;
//Get Recipient Certificate
GetRecipientCert();
if (recipientCertificate == null)
{
lblReport.Text ="Recipient Certificate to use for this application could not be found.";
return;
}
//Sign the Message
Encoding unicode = Encoding.Unicode;
byte[] msgBytes = unicode.GetBytes(tbMessage.Text);
byte[] encodedSignedCms = SignMesage(msgBytes, signerCertificate);
//Encrypt the Message
// Encrypt the encoded SignedCms message.
byte[] encodedEnvelopedCms = EncryptMsg(encodedSignedCms,
recipientCertificate);
//Send
string encryptedMessage = Convert.ToBase64String(encodedEnvelopedCms);
Send(encryptedMessage);
}
public X509Certificate2 GetSignerCert()
{
// Open the My certificate store.
X509Store storeMy = new X509Store(StoreName.My,
StoreLocation.CurrentUser);
storeMy.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certColl =
storeMy.Certificates.Find(X509FindType.FindBySubjectName,
signerName, false);
if (certColl.Count == 0)
{
lblReport.Text ="Signer Certificate to use for this application could not be found in the certificate store. Please check the SignerName in Web.config for signing the message.";
storeMy.Close();
return null;
}
storeMy.Close();
return certColl[0];
}
X509Certificate2 recipientCertificate = null;
public void GetRecipientCert()
{
SmartCryptoGraphyService service = new SmartCryptoGraphyService();
byte[] recipientCertificateByte = service.GetRecipientCertificateFromStore(tbRecipientName.Text);
X509Certificate2 cert = new X509Certificate2(recipientCertificateByte);
recipientCertificate = cert;
}
public byte[] SignMesage( Byte[] message, X509Certificate2 signerCert)
{
// Place message in a ContentInfo object.
// This is required to build a SignedCms object.
ContentInfo contentInfo = new ContentInfo(message);
// Instantiate SignedCms object with the ContentInfo above.
// Has default SubjectIdentifierType IssuerAndSerialNumber.
// Has default Detached property value false, so message is
// included in the encoded SignedCms.
SignedCms signedCms = new SignedCms(contentInfo);
// Formulate a CmsSigner object, which has all the needed
// characteristics of the signer.
CmsSigner cmsSigner = new CmsSigner(signerCert);
// Sign the PKCS #7 message.
signedCms.ComputeSignature(cmsSigner);
// Encode the PKCS #7 message.
return signedCms.Encode();
}
public byte[] EncryptMsg(Byte[] message, X509Certificate2 recipientCert)
{
// Place message in a ContentInfo object.
// This is required to build an EnvelopedCms object.
ContentInfo contentInfo = new ContentInfo(message);
// Instantiate EnvelopedCms object with the ContentInfo
// above.
// Has default SubjectIdentifierType IssuerAndSerialNumber.
// Has default ContentEncryptionAlgorithm property value
// RSA_DES_EDE3_CBC.
EnvelopedCms envelopedCms = new EnvelopedCms(contentInfo);
// Formulate a CmsRecipient object that
// represents information about the recipient
// to encrypt the message for.
CmsRecipient recip1 = new CmsRecipient(
SubjectIdentifierType.IssuerAndSerialNumber,
recipientCert);
// Encrypt the message for the recipient.
envelopedCms.Encrypt(recip1);
// The encoded EnvelopedCms message contains the encrypted
// message and the information about each recipient that
// the message was enveloped for.
return envelopedCms.Encode();
}
private void Send(string message)
{
SmartCryptoGraphyService service = new SmartCryptoGraphyService();
service.SendMessageCompleted +=new SendMessageCompletedEventHandler(service_SendMessageCompleted);
service.SendMessageAsync(message);
}
void service_SendMessageCompleted(object sender, SendMessageCompletedEventArgs e)
{
if (e.Result)
{
lblReport.Text = "Message Sent Successfully";
}
else
{
lblReport.Text = "Message sending failed";
}
}
The above code is self explanatory but you can study a bit more about these
classes in MSDN.... ContentInfo, SignedCms, CmsSigner, EnvelopedCms, CmsRecipient.
Note the signedCms.ComputeSignature(cmsSigner);
The CheckSignature(Boolean) method verifies the digital signatures on the signed CMS/PKCS #7 message and, optionally, validates the signers' certificates.
Also Note the signedCms.Encode(); The Encode method encodes the information in the object into a CMS/PKCS #7 message.
Also Note the envelopedCms.Encrypt(recip1);
The message is encrypted by using a message encryption key with a symmetric encryption algorithm such as triple DES.
The message encryption key is then encrypted with the public key of each recipient.
The application worked ok for me as a demonstration of example
and generated encrypted messages...here is an example message
MIID7gYJKoZIhvcNAQcDoIID3zCCA9sCAQAxgcQwgcECAQAwKj
AWMRQwEgYDVQQDEwtSb290IEFnZW5jeQIQtoMOH1HeyJNCKmxxp
3EJRTANBgkqhkiG9w0BAQEFAASBgCi5O+Oe/Wp2Ju/6RkpVRKl
HedkAS7Q0t0j5ht/SEBf5uXkqz2SH23or283msqM07nt52oh
FW/2bU42gpcU8JkU3YenMyC/GCatHdwk5QgsDMZ6jEmcKaQs
RXtux7UVWK6TXbYuDoUyQgH0XafTutWC3HGRw2hz/bva7CkFZZ+
EnMIIDDQYJKoZIhvcNAQcBMBQGCCqGSIb3DQMHBAhBUF7do+aaAI
CCAuiCqzjjI0APbhwStHLw1NXw8sUyNCmPsDhT2KHsXtVNhxXVVhQnA
7A2c6c4H2s6n9X71TGx0UIK2b+Dy6kR4PzzkTROLUWCImp8tt9gh++mz
UUDjicCxUjBUpwUdyPmMw0eD2WV9ZIoJpg47KmoKqdezoj0WUHKzjXp
6n8Tnp6FWvKnP+AgiVPqagagOamPVRJ+Du/x5Kf8PbVTLixIdUOBXVb
G1cd1qROp+o7d9Gfb6G1NDQHLOzG74R0Hbn30ut9NOpXir7QI/ZL4who
2xAm7WAFLJKvS/jvRQCSJo+4iPEdrHCIl+f0Ls93URiZeJKvygleNMEw1
Aze0GAwF5ydHLcohMA2BlcUReCkKi8s7qXM9OMq2mqU8QSnc7c5/keoPiun
DXWj/yBJYWDbw7PXhIuD56Ai8rdDbAjd7EQp1HXVD14X+RLUvKAgLohzm7N2
G1jDXOgkB+IV6d50l+mVMJWHUwUS+g37I3iUfnUWpIzhHmzZK5NOEeFfnEtRo
/did1ufmSMOrblV5WVi18hQ/n5VdmSq4mXYratb4v97BBqmPoSqT45CY8e15FCm
BUF+SJ6I07ZGaHBIk0/Il/DEmezLDUlU7df9vCY/BfpQ27zcVL2twTaLD1Zf7KrB8
J+mBODXiV3x1s06AJ6ZPtWXk9jXWBhWKCcCNMr3S1WBJ4lFjwHvYR8dHCmbKkqmluEwe
2tRHRA8mbTI9G200JAJY/oWqgbpFVr2U1eCU73OQUaXYe4bI10nfByDxiDi4c4O+
I ll keep posting my learnings and implementation of different Scenarios.
Probably next would be Veryfying the Encrypted Message Dycrypting and then
looking at the original message from another application....