Beside from the security we squeeze from appliance such as firewall and antivirus, it is a must for our applications to have a second layer of defense. Most often than not you already have several company-wide applications that are interconnected to each other. If you have remote offices, your data will be transmitted over broadband network or internet but how sure are you that each of the application is only accepting data from valid sender? For example, a billing software at remote office that transmits payment confirmation to the financial system located at your head office. Fortunately for .NET developers we can make use of digital certificates. Using digital signatures, we can implement the following security check on our example scenario:
Where:
App 1 - Billing Software located at the remote office
App 2 - Financial System located at the head office
1. App 1 sign the unique message using digital certificate private key. For example: "POST TRANSACTION"
2. App 1 send the digital signature to App 2 (via web service or ftp file)
3. App 2 receives the digital signature and verify command by using digital certificate public key
Let's put it in C# Code:
1. Sign the text or message using digital certificate's private key. You can have set of commands that are expected by the client application.
private byte[] SignCertificate(string text)
{
// Open certificate store of current user
X509Store my = new X509Store(StoreName.My, StoreLocation.CurrentUser);
my.Open(OpenFlags.ReadOnly);
// Look for the certificate with specific subject
RSACryptoServiceProvider csp = null;
foreach (X509Certificate2 cert in my.Certificates)
{
if (cert.Subject.Contains("CN=WINGROUP\\micwein"))
{
// retrieve private key
csp = (RSACryptoServiceProvider)cert.PrivateKey;
}
}
if (csp == null)
{
throw new Exception("Valid certificate was not found");
}
// Hash the data
SHA1Managed sha1 = new SHA1Managed();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(text);
byte[] hash = sha1.ComputeHash(data);
// Sign the hash
return csp.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
}
2. On your server/listener application, validate the digital signature if correct
private bool VerifyCommand(string text, byte[] signature, string certPath)
{
// Load the certificate file to use to verify the signature from a file
// If using web service or ASP.NET, use: X509Certificate2 cert = new X509Certificate2(Request.ClientCertificate.Certificate);
X509Certificate2 cert = new X509Certificate2(certPath);
// Get public key
RSACryptoServiceProvider csp = (RSACryptoServiceProvider)cert.PublicKey.Key;
// Hash the text, the text is the expected command by the client application.
// Remember hased data cannot be unhash. It is irreversable
SHA1Managed sha1 = new SHA1Managed();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(text);
byte[] hash = sha1.ComputeHash(data);
// Verify the signature with the hash
return csp.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA1"), signature);
}
Here a sample code on how to call VerifyCommand:
//Use the mycert.cer certificate to verify signature and validate it against the allowed commands
if (VerifyCommand("POST TRANSACTION", signature, @"C:\mycert.cer")) //POST TRANS
{
MessageBox.Show("POST command received from remote client....");
}
else if (VerifyCommand("CANCEL TRANSACTION", signature, @"C:\mycert.cer")) //CANCEL TRANS
{
MessageBox.Show("Cancel command received from remote client....");
}
else if (VerifyCommand("RETRIEVE TRANSACTION", signature, @"C:\mycert.cer")) //RETRIEVE TRANS
{
MessageBox.Show("RETRIEVE Transaction received from remote client....");
}
else
{
MessageBox.Show("Signature is not valid");
}
You can pass the signature to client using web service or FTP. You can download the complete sample source code of this article from here
Wednesday, April 01, 2009




6 comments:
Thanks! 5 stars for you!
Do you have any sample that uses XML instead of plain text?
Hi, My english is not so good but i hope you will understand me.
When i builded my application in VS2008 everything works great i was able to sign, get publicKey, and later verify signature, but when i published on IIS Server i got error:Valid certificate was not found, wich should mean that i don't have sertificate.
So is there any preset that i should have done on ISS.
thanks, for your help
Hi! good day. To properly install certificate on your web server, read the following articles from Microsoft.
- HOW TO: How to Install/Uninstall a Public Key Certificate Authority for Windows 2000
- HOW TO: Install Imported Certificates on a Web Server in Windows Server 2003
-Installing Server Certificates (IIS 6.0)
hi and thanks for your quick response.I will try to explain my problem more specific. I am building web application for company, where everyone has his own sertificate.There is a some text whic i want they to "sign".I have button where i am locking of their own signature.On click i need to find and take their sertificate(from User local machine in StoreLocation.CurrentUser) encrypt text using this certifacate Private Key and also get his Public Key (csp =(Security.Cryptography.RSACryptoServiceProvider)cert.PrivateKey;
key = csp.ToXmlString(false); in key i get public Key) and store in Database. I want to keep there public key for case if i need to verify(for what i hope that i will not have to do) there authentication. And here is my code:
public static string GetPublicKey()
{
string key=null;
X509Store my = new X509Store(StoreName.My, StoreLocation.CurrentUser);
my.Open(OpenFlags.ReadOnly);
// Look for the certificate with specific subject
System.Security.Cryptography.RSACryptoServiceProvider csp = null;
foreach (X509Certificate2 cert in my.Certificates)
{ if (cert.SubjectName.Name.Contains("@companyname.com"))
{
// retrieve private key
csp = (System.Security.Cryptography.RSACryptoServiceProvider)cert.PrivateKey;
key = csp.ToXmlString(false);
}
}
return key;
}
/*********************************/
public static byte[] SignCertificate(string text)
{
// Open certificate store of current user
X509Store my = new X509Store(StoreName.My, StoreLocation.CurrentUser);
my.Open(OpenFlags.ReadOnly);
// Look for the certificate with specific subject
System.Security.Cryptography.RSACryptoServiceProvider csp = null;
foreach (X509Certificate2 cert in my.Certificates)
{
if (cert.SubjectName.Name.Contains("@companyname.com"))
{
// retrieve private key
csp = (System.Security.Cryptography.RSACryptoServiceProvider)cert.PrivateKey;
}
}
if (csp == null)
{
throw new Exception("Valid certificate was not found");
}
// Hash the data
SHA1Managed sha1 = new SHA1Managed();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(text);
byte[] hash = sha1.ComputeHash(data);
// Sign the hash
return csp.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
}
/*********************************/
public static bool VerifyCommand(string text, byte[] signature, string certPath,string PublicKey)
{
System.Security.Cryptography.RSACryptoServiceProvider csp = new System.Security.Cryptography.RSACryptoServiceProvider();
csp.FromXmlString(PublicKey);
// Hash the text, the text is the expected command by the client application.
// Remember hased data cannot be unhash. It is irreversable
SHA1Managed sha1 = new SHA1Managed();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(text);
byte[] hash = sha1.ComputeHash(data);
// Verify the signature with the hash
return csp.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA1"), signature);
}
/*********************************/
And like i explained in my first post, when i am run on http://localhost:3554/Name/, application works greate. I am gething sertificate(encrypting text), and also get public key and i am able to verify signature later. But when i published application on (IIS) and company's server i can't get certifate.Error:Valid certificate was not found
I hope i have now make more clearly.
Thanks. I realy appreciate your time spended for helping me out.
The reason why your code throws an exception of "Valid certificate was not found" is that, the web application cannot find the certificate which contains "@companyname.com" on the company's web server. You must install this certificate on the company's web server server first. To check if the certificate exists, Look for it using Certificate Manager (MMC).
Thank you very much for your post, I had a problem that was solved with learning on what to cast from an X509 to an RSACryptoprovider.
just for aditional info after reading your commenters posts. if you have a page that uses your code then it should have the certificate stored in the Machine certificate store and change the following lines of code from:
public static byte[] SignCertificate(string text)
{
// Open certificate store of current user
X509Store my = new X509Store(StoreName.My, StoreLocation.CurrentUser);
to public static byte[] SignCertificate(string text)
{
// Open certificate store of current user
X509Store my = new X509Store(StoreName.My, StoreLocation.LocalMachine);
the local machine is important since it is the only store availabl to system proceses that are not running under a windows accout just like ASP.NET and other Win32 Services.
i hope that solves your commenter problems.
Post a Comment