Asymmetric Key Encryption on the Compact Framework

Asymmetric key encryption is useful for use in exchanging data over insecure communication channels. This is due to the use of separate keys for encryption and decryption. This blog entry follows on from the previous one on symmetric key encryption and discusses how to utilise asymmetric key encryption within a .NET Compact Framework application.

Asymmetric Encryption classes within the .NET Compact Framework
The .NET base class library provides Asymmetric Encryption classes within the System.Security.Cryptography namespace. They all derive from the AsymmetricAlgorithm base class.

The .NET Compact Framework base class library provides the following asymmetric encryption classes

  • RSACryptoServiceProvider – This is the .NET implementation of the RSA algorithm named after its three creators – Ronald Rivest, Adi Shamir and Leonard Adleman. It was developed in 1977. This is a wrapper around an unmanaged algorithm implementation provided by the Windows CE Crypto API.
  • DSACryptoServiceProvider – This class is used for digitally signing messages to ensure that they are not tampered with during transit. This is also a managed wrapper around unmanaged code. Since digital signatures and hashing are not part of the current topic I will not discuss this class any further.

Common Asymmetric Algorithm properties
When dealing with the encryption aspect of these crypto service providers there are only a couple of properties of interest defined on the AsymmetricAlgorithm base class:

  • KeySize – the size of the secret key used by the asymmetric algorithm in bits. These are typically much larger than symmetric keys. For example RSA as implemented in the .NET Compact Framework supports keys between 384 and 16384 bits. Setting this property affects the strength of the encryption process.
  • LegalKeySizes – an array of KeySizes objects which outline the range of key sizes supported by the current encryption algorithm.

Likewise there are only a couple of methods defined on the AsymmetricAlgorithm base class.

  • ToXmlString – exports a key pair in the form of an string containing an XML document.
  • FromXmlString – imports a key pair from a string containing an XML document.

You will notice there are no methods for encryption or decryption on the AsymmetricAlgorithm base class. Most of the useful methods (including those used for actual encryption) are actually defined on the classes which derive from AsymmetricAlgorithm.

The most useful methods defined on the RSACryptoServiceProvider class are as follows:

  • Decrypt – a method to decrypt data with the RSA algorithm.
  • Encrypt – a method to encrypt data with the RSA algorithm.
  • ExportParameters – returns a RSAParameters struct, which defines the RSA encryption key pairs (i.e. returns the same data as the ToXmlString method, just in a different format).
  • ImportParameters – imports a public key or key pair from the specified RSAParameters struct.

How to export and import key pairs
Once you have created an instance of the RSACryptoServiceProvider class it will generate it’s own public/private key pair. You can import and export the keys for storage purposes, or to transfer them to another device. In most cases you will probably want to only export the public key (since the private key should be kept a closely guarded secret).

When exporting the keys via the ToXmlString (as XML) and ExportParameters (as a RSAParameters struct) methods, you have the choice of exporting only the public key (false), or both the public key and private keys (true) via a boolean parameter.

The following code sample, demonstrates using these methods:

// Export the public key (pass in true to also export the private keys)
// from device A
RSACryptoServiceProvider rsa1 = new RSACryptoServiceProvider();
RSAParameters publicKey = rsa1.ExportParameters(false);
string publicKeyXML = rsa1.ToXmlString(false);
 
// ... transmit public key to another device ...
 
// Import the public key
// into device B
RSACryptoServiceProvider rsa2 = new RSACryptoServiceProvider();
rsa2.ImportParameters(publicKey);
rsa2.FromXmlString(publicKeyXML);

Please note that in the code sample I have demonstrated using both the XML and object based methods. In a typically application only one set of methods would be utilised.

Secure private key storage
Your application most likely needs to reuse the same public/private key pair a number of times. This means that once generated you will typically need to store your key pair in a secure manor.

The RSACryptoServiceProvider class includes the ability to save the keys in a secure manor by using the Crypto API CSP key container facility. You should seriously consider using this functionality to store private keys, as it is highly likely that it will be more secure than most solutions you will be able to create manually.

To store your private keys in a CSP key container, perform the following actions within your code:

The .NET Compact Framework then handles creating and retrieving keys automatically. The first time the code executes a unique public/private key pair will be generated and saved into the specific key container. In the future when the RSACryptoServiceProvider instance is created with the same key container name, the keys will automatically be retrieved.

// Create the CspParameters object and set the
// key container name used to store the RSA key pair.
CspParameters cp = new CspParameters();
cp.KeyContainerName = "MySampleKeyContainer";
 
// Create a new instance of RSACryptoServiceProvider 
// that accesses the key container "MySampleKeyContainer"
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(cp);
 
// Display the key information to the debug window.
Debug.WriteLine("Key added to container: \n  {0}", rsa.ToXmlString(true));

How to encrypt and decrypt data using Asymmetric encryption
The Encrypt() and Decrypt() methods both accept two parameters as follows:

  • rgb – a byte array containing the data to be encrypted or decrypted
  • fOAEP – a boolean flag. If true OAEP data padding is used, otherwise PKCS #1 v1.5 padding is used. This parameter doesn’t really matter as much, as long as encryption and decryption utilise the same value. Controlling this parameter becomes important in scenarios where one end of the encryption process is dictated by a third party system.

The following example demonstrates basic RSA encryption and decryption by providing a couple of small helper functions which will be useful within your own applications.

The methods encrypt (or decrypt) data passed to them in the form of a byte array and return another byte array containing the processed output. These methods utiilise the CSP Key Container storage mechanism to securely store the public/private key pair used to perform the encryption process.

using System.Security.Cryptography;
 
public byte[] Encrypt(byte[] data, string keyContainerName)
{
  // Configure the parameters to indicate the name of the
  // key container which contains our public/private key pair
  CspParameters csp = new CspParameters();
  csp.KeyContainerName = keyContainerName;
 
  // Create an instance of the RSA encryption algorithm
  // and perform the actual encryption
  RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(csp);
  return rsa.Encrypt(data, true);
}
 
public byte[] Decrypt(byte[] data, string keyContainerName)
{
  // Configure the parameters to indicate the name of the
  // key container which contains our public/private key pair
  CspParameters csp = new CspParameters();
  csp.KeyContainerName = keyContainerName;
 
  // Create an instance of the RSA encryption algorithm
  // and perform the actual decryption
  RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(csp);
  return rsa.Decrypt(data, true);
}

Please note that if the encryption and decryption code was running on two different devices, you would have to use the technique discussed in the “How to export and import key pairs” section to ensure that the decryptor had access to the public key.

A common request on newsgroup forums is for help in encrypting small amounts of text. Common problems encountered in this scenario include improperly converting the binary data produced by the encryption process back into a text string.

For this reason I have also provided the following helper functions which make it really easy to securely encrypt and decrypt text strings.

using System;
using System.Text;
 
public string EncryptString(string data, string keyContainerName)
{
  // Encrypt a string using the private key stored within the
  // specified key container
  byte dataToEncrypt[] = Encoding.UTF8.GetBytes(data);
  byte encryptedData[] = Encrypt(dataToEncrypt, keyContainerName);
  return Convert.ToBase64String(data);
}
 
public string DecryptString(string data, string keyContainerName)
{
  // Decrypt a string using the public key stored within the
  // specified key container
  byte dataToDecrypt[] = Convert.FromBase64String(data);
  byte decryptedData[] = Decrypt(dataToDecrypt, keyContainerName);
  return Encoding.UTF8.GetString(decryptedData);
}
 
// Example use
 
string myEncryptedString = EncryptString("This is my top secret message",
                                        "SampleKeyContainer");
...
string myDecryptedString = DecryptString(myEncryptedString,
                                         "SampleKeyContainer");

Now that I have provided some rather long discussions on symmetric and asymmetric key encryption within the .NET Compact Framework, I am finally ready to dicuss some practical uses of encryption. In the next few days I aim to cover off some of the encryption related aspects of exam 70-540.

7 Responses to “Asymmetric Key Encryption on the Compact Framework”

  1. ?brahim ULUDA? says:

    Very thanks. It was very helpful.

  2. Dnickes says:

    I think the function “EncryptString” should return

    return Convert.ToBase64String(encryptedData);

    instead of:

    return Convert.ToBase64String(data);

    Could be?

  3. Matheus says:

    Hello,

    How can I transfer PrivateKey from certificate into RSACryptoServiceProvider in Compact Framework (2.0 or 3.5)
    There are not supported ways I know from “big framework”:

    1, this.rsaProvider = (RSACryptoServiceProvider)certificate.PrivateKey; (there are not available PrivateKey property)
    2, ToXmlString, FromXmlString (not available on compact framework)

    I need to verify retrieved data on mobile device using RSA. Certificate with private key is installed in mobile device.

  4. Anudeep says:

    @ Matheus

    I was also stucked at the same point as of u. Hav u found any solution
    for that .

    @Alll
    Can u help in resolving the issue specified by Matheus ??

  5. ricardo says:

    I have the same problem, help

  6. ricardo says:

    i have a solution , but i am from mexico , i dont speak english very well, write me and I send my source code.

    thorr80@hotmail.com

  7. Skarlman says:

    Use the XmlSerializer to serialize the RSAParameters yourself, store the xml in a file which you transfer to the mobile and then deserialize it on the mobile into a RSAParameters object. My only problem is that I cannot load just the public key on the mobile for some reason (it won’t store it).. which is funny because it really removes the whole point of the asymmetric key cryptography.. But it is probably just a matter of time before I manage to solve that part too..

    I don’t have a clean code example, but it is something like this:

    //Serialize
    CspParameters csp = new CspParameters();
    csp.KeyContainerName = txtKeyName.Text;

    RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(csp);

    RSAParameters rsaPsBothKeys = rsa.ExportParameters(true);
    RSAParameters rsaPsOnlyPublic = rsa.ExportParameters(false);

    XmlSerializer xmlSer = new XmlSerializer(rsaPsBothKeys.GetType());

    StringWriter strWriter = new StringWriter();
    xmlSer.Serialize(strWriter, rsaPsBothKeys);

    textBox1.Text = strWriter.ToString();

    //deserialize:
    string path = System.IO.Path.GetDirectoryName(
    System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase) +
    “\\PublicKey.txt”;
    if (File.Exists(path))
    {
    StreamReader sr = new StreamReader(path);
    XmlTextReader xr = new XmlTextReader(sr);

    XmlSerializer xs = new XmlSerializer(typeof(RSAParameters));
    RSAParameters rsaParams;
    rsaParams = (RSAParameters)xs.Deserialize(xr);

    xr.Close();
    sr.Close();

    CspParameters csp = new CspParameters();
    csp.KeyContainerName = txtKeyName.Text;

    RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(csp);

    rsa.ImportParameters(rsaParams);
    rsa.PersistKeyInCsp = true;

    textBox1.Text = “Loaded keys from file”;
    }

Leave a Reply