Need Quality Code? Get Silver Backed

Asymmetric Encryption with RSA

18thJul

0

by Gary H

When cryptography is outlawed, bayl bhgynjf jvyy unir cevinpl.

Kevin McCurley

Asymmetric Encryption is a cryptographic system that uses two keys - a public key known to everyone and a private or secret key known only to the recipient of the message. When Alice wants to send a secure message to Bob, she uses Bob's public key to encrypt the message. Bob then uses his private key to decrypt it.

An important element to the public key system is that the public and private keys are related in such a way that only the public key can be used to encrypt messages and only the corresponding private key can be used to decrypt them. Moreover, it is virtually impossible to deduce the private key even if you know the public key.

For an overview on what encryption is I heartily recommend you read a primer. It's a broad topic and to cover it in depth would be way out of scope of this series.

Getting Started

We will be using the RSA Crypto Service Provider to demonstrate encryption with a public key and decryption using the related private key.

Generating Keys

The first step to using RSA encryption is to actually create a keypair. We can do this using the Strong Name Tool which ships with the .Net SDK. We will generate a key pair file and then extract the public key.

sn -k keypair.snk
sn -p keypair.snk public.snk

Common Methods

Encrypting or Decrypting with RSA have a few commonalities. Whichever we choose we start by creating an RSACryptoServiceProvider and configure it with the parameters of our RSA keys. To make this easier we can create some common functions that will load an RSACryptoServiceProvider from the SNK files that we created earlier.

This is a lot of boilerplate code. Click to toggle view.

Encrypting

Encrypting involves creating an RSA Crypto Service provider from a public key source, then streaaming data through a cipher into a memory stream. We ensure that we work in chunks that match our block size otherwise our data would get junked. In our example we pull the public key from a signed assembly but you can easily use GetRSAFromSnkFile instead.

public static byte[] AsymmetricEncrypt(Assembly assembly, 
											byte[] dataToEncrypt)
{
    if (assembly == null || dataToEncrypt == null)
        return null;

    //Load the keys from the assembly
    RSACryptoServiceProvider cipher = 
    		EncryptionUtils.GetPublicKeyFromAssembly(assembly);

    //Prepare variables used in encryption
    int blockSize = EncryptionUtils.BLOCK_SIZE, 
    						index = 0, 
    						bytesLeft = dataToEncrypt.Length, 
    						totalBytes = 0;

    //Ensure that the total bytes is a multiple of 128, the final 
    // encrypted content block will  be padded to this length in bytes
    if ((bytesLeft * 2) % 128 != 0)
        totalBytes = (bytesLeft * 2) + (128 - ((bytesLeft * 2) % 128));
    else
        totalBytes = bytesLeft * 2;

    //Create the buffer for encrypted data
    using (MemoryStream memEncryptedTextbuffer = 
										new MemoryStream(totalBytes)) 
    {
        byte[] block = null;
        byte[] encryptedBlock = null;

        //Split the serialized data into smaller blocks for encryption
        while (bytesLeft > 0) {
            //If the blocksize is too large, set to the required amount
            if (bytesLeft < blockSize)
                blockSize = bytesLeft;

            //Get a block and encrypt it
            block = EncryptionUtils.BlockCopy(dataToEncrypt, 
            									index, blockSize);
            encryptedBlock = cipher.Encrypt(block, false);
            memEncryptedTextbuffer.Write(encryptedBlock, 0, 
            								encryptedBlock.Length);

            //Update position and size tracking
            index += blockSize;
            bytesLeft -= blockSize;
            block = null;
            encryptedBlock = null;
        }
        cipher.Clear();

        //Dump the encrypted data to the caller
        memEncryptedTextbuffer.Position = 0;
        return memEncryptedTextbuffer.ToArray();
    }
}

Decryption

Decryption is essentially the encryption routine in reverse. This time we load the private keyfile and reverse the process we did for encryption.

public static byte[] AsymmetricDecrypt(byte[] encryptedData, 
											byte[] snkFileContent)
{
    RSACryptoServiceProvider cipher = 
    		EncryptionUtils.GetRSAFromSnkBytes(snkFileContent);
    return DoAsymmetricDecrypt(cipher, encryptedData);
}
private static byte[] DoAsymmetricDecrypt(
									RSACryptoServiceProvider cipher, 
									byte[] encryptedData)
{
    	//Prepare block detail
    int blockSize = EncryptionUtils.BLOCK_SIZE * 2, 
    					index = 0, 
    					bytesLeft = encryptedData.Length;

    using (MemoryStream memDecryptedTextbuffer = 
    							new MemoryStream(bytesLeft / 2)) 
    {
        byte[] block = null;
        byte[] decryptedBlock = null;

        //Split the serialized data into smaller blocks for processing
        while (bytesLeft > 0) {
            //If the blocksize is too large, set to the required amount
            if (bytesLeft < blockSize)
                blockSize = bytesLeft;

            	//Get a block from the encrypted data
            block = EncryptionUtils.BlockCopy(encryptedData, 
            										index, blockSize);
            decryptedBlock = cipher.Decrypt(block, false);
            memDecryptedTextbuffer.Write(decryptedBlock, 0, 
            								decryptedBlock.Length);

            	//Update position and size tracking
            index += blockSize;
            bytesLeft -= blockSize;
            block = null;
            decryptedBlock = null;
        }
        cipher.Clear();

        //Dump the encrypted data to the caller
        memDecryptedTextbuffer.Position = 0;
        return memDecryptedTextbuffer.ToArray();
    }
}

Putting it all together

You can aquire a full library of the encryption functions that we have discussed from our Open Source Archive.

Other Posts

Symmetric Encryption with AES

C# , Encryption

Comments are Locked for this Post