andrewlocatelliwoodcock

Thoughts on Software

Implementing RSA asymmetric public-private key encryption in C#: encrypting under the public key

with 11 comments


Following on from my last post on how to generate a public / private key pair in C#, this is the next post in my series on using RSA asymmetric encryption in .Net.

Now we have a public / private key pair, we can encrypt an arbitrary string using RSA encryption. To make this code more general, we are going to allow users to specify the bit length of the public key, allowing us to easily encrypt under 1024, 2048 and 4096 bit keys. To this end, I am reusing the RsaKeyLengths enumeration from the last post containing three common bit lengths: 1024, 2048 and 4096.

Performing RSA Encryption is not particularly difficult, but neither is it straightforward.

The basic steps are as follows:

  1. Instantiate a new RSACryptoServiceProvider object using the bit length used to generate the public key. These bit lengths must match: you cannot provide a 1024 bit public key and then attempt to encrypt the data using 2048 bits for example.
  2. Initialize the RSACryptoServiceProvider object from the public key using the FromXmlString method. Remember that the public key is serialized as XML (see my post on generating public / private key pairs if uncertain)
  3. Convert the data to encrypt to a byte array
  4. Work out the maximum length of data that can be encrypted per block. This requires a bit of knowledge of how the RSACryptoServiceProvider works, specifically that it uses the SHA1 hash function internally.
  5. Encrypt the data block by block.
    As we allowing encryption of arbitrary strings, we may not be able to encrypt our data in a single block. We encrypt the data block by block until it is completely encrypted.
  6. Reverse the array of the encrypted bytes in the block.
    <sarcasm>Microsoft have never before been known to implement an incompatible version of a standard process but there’s always a first time</sarcasm>:  the RSACryptoServiceProvider reverses the order of the encrypted bytes after encryption and again before decryption which makes our completed encrypted string look like gibberish to other implementations, so we need to undo this before we continue.
  7. Concatenate the encrypted blocks and return the encrypted string
As I said, not particularly difficult but certainly not straightforward.
Here’s our completed encryption method. Next time we’ll cover decryption.
 
/// <summary>
/// Encrypt an arbitrary string of data under the supplied public key
/// </summary>
/// <param name="publicKey">The public key to encrypt under</param>
/// <param name="data">The data to encrypt</param>
/// <param name="length">The bit length or strength of the public key: 1024, 2048 or 4096 bits. This must match the 
/// value actually used to create the publicKey</param>
/// <returns></returns>
public static string Encrypt(string publicKey, string data, RsaKeyLengths length)
{
    // full array of bytes to encrypt
    byte[] bytesToEncrypt;

    // worker byte array
    byte[] block;

    // encrypted bytes
    byte[] encryptedBytes;

    // length of bytesToEncrypt
    var dataLength = 0;

    // number of bytes in key                
    var keySize = 0;

    // maximum block length to encrypt          
    var maxLength = 0;

    // how many blocks must we encrypt to encrypt entire message?
    var iterations = 0;

    // the encrypted data
    var encryptedData = new StringBuilder();

    // instantiate the crypto provider with the correct key length
    var rsaCryptoServiceProvider = new RSACryptoServiceProvider((int) length);

    // initialize the RSA object from the given public key
    rsaCryptoServiceProvider.FromXmlString(publicKey);

    // convert data to byte array
    bytesToEncrypt = Encoding.Unicode.GetBytes(data);

    // get length of byte array
    dataLength = bytesToEncrypt.Length;

    // convert length of key from bits to bytes
    keySize = (int)length / 8;

    // .NET RSACryptoServiceProvider uses SHA1 Hash function
    // use this to work out the maximum length to encrypt per block
    maxLength  = ((keySize - 2) - (2 * SHA1.Create().ComputeHash(bytesToEncrypt).Length));

    // how many blocks do we need to encrypt?
    iterations = dataLength / maxLength;

    // encrypt block by block
    for (int index = 0; index <= iterations; index++)
    {
        // is there more than one full block of data left to encrypt?
        if ((dataLength - maxLength * index) > maxLength)
        {
            block = new byte[maxLength];
        }
        else
        {
            block = new byte[dataLength - maxLength * index];
        }

        // copy the required number of bytes from the array of bytes to encrypt to our worker array
        Buffer.BlockCopy(bytesToEncrypt, maxLength * index, block, 0, block.Length);

        // encrypt the current worker array block of bytes
        encryptedBytes = rsaCryptoServiceProvider.Encrypt(block, true);

        // RSACryptoServiceProvider reverses the order of encrypted bytesToEncrypt after encryption and before decryption.
        // Undo this reversal for compatibility with other implementations
        Array.Reverse(encryptedBytes);

        // convert to base 64 string
        encryptedData.Append(Convert.ToBase64String(encryptedBytes));

    }
    return encryptedData.ToString();

}
 

The RsaKeyLengths enumeration used to request 1024, 2048 or 4096 bit encryption:


public enum RsaKeyLengths
{
    Bit1024 = 1024,
    Bit2048 = 2048,
    Bit4096 = 4096
}

Written by andrewlocatelliwoodcock

August 1, 2011 at 21:58

Posted in Base64 Encoding, C#, Encryption

Tagged with , , ,

11 Responses

Subscribe to comments with RSS.

  1. I learned a lot from this article, great help for me, thank you!

    Jeff Machine

    August 20, 2011 at 11:10

  2. hi, how will we handle the encryption if the external party has given us their public key (not in xml format)
    do we have to convert the public key in an xml format?

    anant

    September 6, 2012 at 17:56

    • There are several methods of conveying public key data and in order to use FromXmlString yes the public key will have to be in the expected XML format. I can’t remember off the top of my head whether RSACryptoServiceProvider implements any methods to load keys in non-XML format.

      andrewlocatelliwoodcock

      September 6, 2012 at 18:43

      • i have the public key, can i directly put that in the XML format…putting public key between the Modulus tag….something like this
        Public key will come hereAQAB

        anant

        September 6, 2012 at 20:40

  3. I noticed that your code appears to be copied from a 2007 Code Project article with minor changes. See http://www.codeproject.com/Articles/10877/Public-Key-RSA-Encryption-in-C-NET

    Peter

    October 4, 2012 at 15:42

    • Hi Peter,

      thanks for the accusation of plagiarism. The code is taken my 2006 / 2007 Masters thesis for the University of Liverpool on supplying high-quality random numbers via web services for which I was awarded a first class MSc degree. Draw whatever conclusions you want.

      Andrew

      andrewlocatelliwoodcock

      October 4, 2012 at 15:59

  4. Nice post Andrew and it was really useful. Sharing this to my community through my daily digest.

    Kannan Subbiah

    January 14, 2013 at 02:15

  5. Hi,

    This is an excellent work. This implementation works well on larger text. Then how do I decrypt it? any implementation available?

    Selva

    Selva

    February 8, 2013 at 07:59

  6. Hi,

    Excellent work, then It is encrypting a larger text. then how do I decrypt the text?

    Regards,
    Selva

    Selva

    February 8, 2013 at 09:14

    • Hi Selva – thanks for the comment. Apologies but the follow-up has been on my to-do list for a while now … I will try to get round to it shortly …

      andrewlocatelliwoodcock

      February 12, 2013 at 10:01

  7. Hi,

    Excellent work, then It is encrypting a larger text. then how do I decrypt the text?

    Regards,
    Alex van der Lans

    alex1678

    August 5, 2014 at 20:38


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: