This is a mirror of official site: http://jasper-net.blogspot.com/

Using RSA Public Key Encryption in a Shared Web Hosting Environment

| Sunday, March 20, 2011
Introduction

If 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 (RSACryptoServiceProvider) supplied by .NET is not usable in such an environment because it would compromise the security of the server's own private keys. This article provides an alternative solution that does not require the use of RSACryptoServiceProvider and hence can be used anywhere.

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:
  • Compute the hash of the data to be signed
  • Encrypt the hash using our private key
  • Send the data and encrypted hash to the receiver
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:

Decrypt the digital signature using our public key
Compute the hash of the data received
Compare the hash computed from the data with that contained in the decrypted signature
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 .NET

RSA encryption services in .NET are provided by the RSACryptoServiceProvider class. There are also classes to perform hashing, namely MD5CryptoServiceProvider and SHA1CryptoServiceProvider. None of these classes are hard to use, once you understand the principles behind what they do.

Key Generation

The 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 512 is your desired key length, in bits. Microsoft doesn't actually make it clear, but the RSACryptoServiceProvider constructor generates a random key pair for you of the length you specify. The XML string generated encapsulates the magic numbers comprising your key. It looks something like this (when nicely formatted):

<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 Data

To sign your license code string (or whatever) in your signing script, you must first load up an RSACryptoServiceProvider instance with the private key you made earlier, which you do like this:

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);

Read more: Codeproject

Posted via email from Jasper-net

0 comments: