|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionIf you are thinking of using RSA encryption on a Web server maintained by a shared hosting company (as many small websites are), you might find yourself out of luck. The RSA encryption class ( What is Public Key Encryption (in 15 Words or Less)?Public key encryption provides a secure, snoop-proof means of transporting small amounts of data from one party to another without the need for any previously agreed secret key information. What one needs to achieve this is a public-private key pair. The public key is made generally available while the private key is kept secret (by the person who generated it in the first place, usually). Data encrypted with the public key can only be decrypted by the person holding a copy of the private key, while data encrypted with the private key is known to come from the holder of that key. There is some further reading here. Our company, AlpineSoft, uses RSA public key encryption for generating software license codes that cannot be forged. When the user purchases our shareware application, he is emailed a license code which is signed with our private key. At the receiving end, the software verifies this code using our public key and only if the signature is valid is the license code accepted. This procedure is automated, but we ran into problems with the signing process on our public Web server, which runs on a shared hosting service. What that problem was, and how we overcame it, is the subject of this article. What are Digital Signatures?A digital signature is a way of proving that a piece of information - a license code in our case - comes from a particular source. What is actually signed is not the data itself, but a hash (also known as a message digest) of the data. A hash is a fixed length string of bytes (16 bytes for MD5 and 20 bytes for SHA1) that is calculated by a 'one-way' hashing algorithm. The idea is that every message almost certainly generates a unique hash value, and contriving a different message which generates the same hash value is effectively impossible. If you want to know more about hashing, read this. To sign a piece of data, we do this:
In other words, a digital signature is just an encrypted hash of the data to be signed. At the receiving end, the integrity of the signed data is verified as follows:
If the values differ, the signature is not valid. The point about this procedure is that only the sender can generate a valid encrypted hash, since only the sender knows the private key. Using Digital Signatures in .NETRSA encryption services in .NET are provided by the Key GenerationThe first thing you need to do is to generate your own private-public key pair. You also export this in XML form so that you can refer to it later. You only need to do this once and it can be done on any computer, not necessarily the one you are going to use for signing things. The mechanics of doing this are simple: RSACryptoServiceProvider rsacp = new RSACryptoServiceProvider (512);
string my_key_pair_formatted_as_an_xml_string = rsacp.ToXmlString (true);
Here <RSAKeyValue>
<Modulus>zjFmn/hT8J3wZqW5IhU4aQggHtqZmL+OpWO1HCgo4x38HAbRXrrzXH2d3FA0AOSipfluDh1vSq/
FMC/Kvm//xw==</Modulus>
<Exponent>AQAB</Exponent>
<P>+yOEiADmhrquMF4vms1fo4jItF/PlziDNleyxEZLqFk=</P>
<Q>0i8pKzRseS6ODbgFTw0pZEf9Z+SXtsyDWqk09CVH5R8=</Q>
<DP>0hE0k5rFOV8/wv+VrFQrspwA3jfiaeiAgN08kEcIlAk=</DP>
<DQvLrE5ZRa1TkpwVk3eKAyVeGihu9XFel9+gsJ6OA6cJSM=</DQ>
<InverseQ>Uj3vYWWtNW346N6Tn25RmDWfNrlysdKl1qkANGx4JEI=</InverseQ>
<D>zXQgBAoW6c0WO9Gp1TI70TxNdTDwl2lYI6hkUDgb9aChZfOswClaRV/
ApM+QZQQmFYZbWphmMtlUrEAuMe6C4Q==</D>
</RSAKeyValue>
Don't worry if this looks intimidating - it's just a bunch of very large numbers, encoded as base64 strings so that they can be stored as text ('base64' is a convenient way of representing binary data as printable text). What you see here is a private key, i.e. it contains all the information needed to sign or decrypt messages. To verify or encrypt messages, you need only the public key which consists of just the first two fields: <RSAKeyValue>
<Modulus>zjFmn/hT8J3wZqW5IhU4aQggHtqZmL+OpWO1HCgo4x38HAbRXrrzXH2d3FA0AOSipfluDh1vSq/
FMC/Kvm//xw==</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
Signing DataTo sign your license code string (or whatever) in your signing script, you must first load up an RSACryptoServiceProvider rsacp = new RSACryptoServiceProvider (512);
string my_private_key = "<our PRIVATE key in XML form, as generated above>";
rsacp.FromXmlString (my_private_key);
You are now ready to start signing things. To sign our license codes, we do something like this: string license_code = "Licensed to J.T. Ripper, London";
ASCIIEncoding ByteConverter = new ASCIIEncoding ();
byte [] sign_this = ByteConverter.GetBytes (license_code);
byte [] signature = rsacp.SignData (sign_this, new SHA1CryptoServiceProvider ());
string base64_string = Convert.ToBase64String (signature);
This actually does a number of things:
Steps 2, 3 and 4 are all carried out in the call to Verifying Signed DataVerifying the license code at the receiving end is, essentially, a mirror image of the above: RSACryptoServiceProvider rsacp = new RSACryptoServiceProvider (512);
string my_public_key = "<our PUBLIC key in XML form, as generated above>";
rsacp.FromXmlString (my_public_key);
string license_code = "<license code received by email>";
ASCIIEncoding ByteConverter = new ASCIIEncoding ();
byte [] verify_this = ByteConverter.GetBytes (license_code);
string base64_encoded_signature = "<base64-encoded signature generated above>";
byte [] signature = Convert.FromBase64String (base64_encoded_signature);
bool ok = rsacp.VerifyData (verify_this, new SHA1CryptoServiceProvider (), signature);
if (!ok)
barf ();
This actually does the following:
Steps 3, 5 and 6 are all carried out in the call to And that's all there is to it; say goodbye to software piracy. Or at least, it should be, but... But There's a ProblemHaving figured out how to make our license codes secure, we thought we could sit back and relax, but when we tried to run our signing script on our public Web server (which runs in a shared hosting environment), we got the following security exception from System.Security.SecurityException: Request for the permission of type
'System.Security.Permissions.KeyContainerPermission, mscorlib, Version=2.0.0.0,
Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.
Help! What do we do?? A bit of Googling around, and a quick email to our (excellent) Web hosting providers Liquid Six, revealed that the reason for this lies deep inside the Windows crypt API, on which Some more Googling turned up two essential resources: Chew Keong TAN's most excellent BigInteger class and some LGPL 'C' code to do the requisite calculations and PKCS#1 encapsulation from XySSL (originally written by Christopher Devine). These resources were particularly useful to me because (a) the ability to manipulate numbers with hundreds of digits is a specialist area, and (b) I hate ASN.1 (on which the PKCS#1 format is built). The calculations themselves are deceptively simple. A day or two of stitching and patching later and EZRSA was born. EZRSA does pretty much everything that Using the CodeUsing EZRSA is a lot simpler than understanding what it does. It is pretty much plug-compatible with I have included a Visual Studio 2005 project to build EZRSA as a DLL (AlpineSoft.EZRSA.dll) which you can then simply drop into the bin directory on your Web server. I have also included a copy of Chew Keong TAN's BigInteger class as the one on his page has a bug in it which is fixed in the version I provide. If you are looking for a pre-built DLL (which is built for .NET 2.0), you will find it in with the demo program (see link at the top of the article). This program demonstrates the fundamentals and also shows you how to use RSA to encrypt and decrypt data, if that is your bag. A word of warning though; don't use RSA (in any form) to encrypt large messages. It is not designed to do this and is very slow. Instead, use your public RSA key to encrypt a DES, Blowfish or whatever symmetric key and send that to the receiver first. You can then safely use this symmetric key to encrypt the body of your message using the encryption method of your choice, as only the receiver can decipher the symmetric key. I am unaware of any 'trust level' issues doing this in a shared hosting environment. Points of InterestThe thing that struck me most strongly doing this project is what a nice language C# is to program in. The code I converted from C ended up much shorter and easier to read by the time I was done. This left some time to add some bells and whistles (in the form of And finally, a word about keeping your private key(s) safe. The major problem with the foregoing is that your private key appears in your server-side scripts (although it's never sent to the browser of course). It is this limitation which prompted Microsoft to lock up private keys in Key Containers in the crypt API. UNFORTUNATELY, they made this the only way to use them, which was a mistake IMO. Anyway, from a .NET standpoint, the obvious way to make things a little more secure is to compile your private key into a DLL, perhaps wrapped up in a simple API to (say) sign a string. This way, if someone hacks into your Web server, at least they won't get a plaintext copy of your private key. Oh yes, one more thing. It is now apparently possible to crack a 640 bit RSA key in under a year if you put your mind to it. Shows how much faster computers have become. When RSA was designed back in 1977, the estimate was over 1 billion years. History
|
||||||||||||||||||||||