|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionIn this article, I will show how to sign any data with the private key you use to sign your assemblies. BackgroundThe general idea of digital signature using asymmetric cryptography is very simple:
In PKI (Public Key Infrastructure), we have a chain of certificates. There are some "root" CAs (Certificate Authorities) which certify the public keys of other parties and thus we can believe that code signed by "Microsoft" is really from Microsoft (we believe that root CAs wouldn't certify a key if they were not sure that it belongs to Microsoft). So, when we want to verify a signature of data in the PKI world, we can just get the public key of the signer from the CA and use it in the asymmetric algorithm. One of the applications of asymmetric cryptography is digital code signing. In the .NET world, we have all we need to sign our code: we can generate as many key pairs as we want (using sn.exe from the .NET Framework SDK), we can build our assemblies signing them and we can even secure our system to avoid running any single assembly without the signature of the party we trust. I made a medical imaging database for dermatologists. They often decide not to connect their machines to any network (to protect the sensitive data from being stolen), so any kind of on-line activation was not an option. My idea is to provide an individual personalized license for every client and make tampering this license not easy. I like the idea described in the article "Piracy and Unconventional Wisdom" by Chad Z. Hower aka Kudzu, so I wanted to make my solution as easy as possible for the client. The license file is just a separate plain text file which is easy to transport, so for example, upgrading the program to the full product requires just copying one file to the program folder. The license file is plain text, but (using Base64 encoding of any binary data) it can contain any complex license data (e.g. serialized graph of objects). And Now the Idea: Use Code Signing Keys for Other PurposesSome day I asked myself: can I use the same key for signing my assemblies and for signing any other data? After some research, I found a way to import keys from a *.snk file (generated by sn.exe tool) to an RSA cryptographic object from So now we can use our code-signing key to sign any data and we can use the public key embedded in our application assembly for verification. Simple, isn't it? If you are interested in key BLOBs, you can read the MSDN article "Private Key BLOBs". Using the CodeIn the article code, you can find two executables:
I wanted to keep the code simple, so please note that I omitted some rsa.ImportCspBlob(System.IO.File.ReadAllBytes("key.snk"));
Then we can sign any binary data: byte[] licenseData = Encoding.UTF8.GetBytes(licenseText);
byte[] signature = rsa.SignData(licenseData,
new System.Security.Cryptography.SHA1CryptoServiceProvider());
And, finally, we write the data and the signature into the license file (as text): System.IO.File.WriteAllText("license.txt",
Convert.ToBase64String(licenseData)+Environment.NewLine+
Convert.ToBase64String(signature));
When we start our application, we need to get the public key from the assembly. We need to strip some header from it before we pass the key to // Here is a trick: the public key in assembly file has a 12-byte header
// at the beginning.
// We strip it - and the remaining bytes can be now imported by
// RSACryptoServiceProvider class
byte[] tmpKey = new byte[pubKey.Length - 12];
Array.Copy(pubKey, 12, tmpKey, 0, tmpKey.Length);
rsa.ImportCspBlob(tmpKey);
Then we can get data (and a signature) from the license file and verify the signature: string[] licenseLines = System.IO.File.ReadAllLines("license.txt");
byte[] licenseData = Convert.FromBase64String(licenseLines[0]);
if (rsa.VerifyData(licenseData,
new System.Security.Cryptography.SHA1CryptoServiceProvider(),
Convert.FromBase64String(licenseLines[1]))) {
Console.WriteLine("License is:\n" + Encoding.UTF8.GetString(licenseData));
}
If you want to quickly play with the demo, just unpack NeatLicenseDemo.zip and run rundemo.bat. You will be asked to type some text and then you'll see that verification passed. If you want to play with the code, unpack NeatLicense.zip and run:
Points of InterestOne can ask: What if the hacker will generate a new key pair, re-sign our assembly and prepare a new license file and then distribute the program? Hmm, that's right, we can't avoid this - exactly as we can't prevent a hacker from changing our code to bypass any other software protection (on any platform, not only .NET). Can we try? I think we should focus on client needs, not on fighting with the cruel world. But the good thing: A pirate can't hide his activity. It is impossible (to be precise: probability is extremely low) to generate a new key pair with the same public key, so we can always detect that the assembly was tampered - it's public key (and public key token) will change. To make the hacker's life harder, we can check random assembly key bytes in several places in our program. As an example, you can see the
|
||||||||||||||||||||||