Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WPF

A .NET Encryption Library

4.86/5 (13 votes)
26 Jun 2010CPOL10 min read 58.6K   2K  
This article introduces a .NET encryption library and demonstrates how to use the library with a WPF application.

Introduction

This article introduces a .NET encryption library and demonstrates how to use the library with a WPF application.

Background

The advancement of Information Technology makes data security a very important subject. Encrypting data is one of the most common and effective methods to secure it. Most software products come with built-in data encryption mechanisms, such as the SSL/TLS for secure communication over the internet and the SQL Server encryption for the secure storage of data.

It is not uncommon that programmers may want to write code to implement their own data encryption in their applications. This article introduces a .NET encryption library that makes data encryption/decryption easy. This library is built upon the "System.Security.Cryptography" namespace in the .NET Framework. This article is inspired by another CodeProject article: "Public Key RSA Encryption in C# .NET" by Mathew John Schlabaugh. This article borrows Mathew's idea into a class library, and provides a WPF application to demonstrate how to use the library. You can find other great articles on this subject, such as "RSA Encryption in .NET -- Demystified!" by Peter A. Bromberg. Of course, the "Wikipedia" is always with us.

The class library and the example WPF application are developed in a Visual Studio 2008 solution, which has three .NET projects:

SolutionExplorerOverall.jpg

  • "RSAEncryptionUtilities" is the encryption class library.
  • "WPFUILibrary" is a class library to simplify the building of the user interfaces in WPF applications. In this class library, I implemented an easy to use "Modal Window Controller" to display "modal windows" and a "WPF XML document viewer" user control to display XML documents.
  • "EncryptionLibraryExample" is the WPF application to demonstrate how to use the encryption library.

In this article, I will first introduce the encryption class library, and then introduce the WPF UI utilities implemented in the "WPFUILibrary" project. Finally, we will talk about the "EncryptionLibraryExample" WPF application and how the encryption class library is used. This article comes with the complete source code of the Visual Studio solution. If you are interested in this article, I will recommend you to download the source code to compile and run it. By running the application first, your reading of this article will be much easier. The example application is written in WPF, which has some useful WPF techniques. In this article, these WPF techniques may be over-killing for a simple example application. If you are not interested in the WPF specific topics, you can directly go to "How to use the encryption library" section after taking a look at the encryption class library.

Let us now start with the encryption class library.

The encryption class library

SolutionExplorerEncryptionLib.jpg

As shown in the above picture, the encryption class library is implemented in the "Utilities.cs" file:

C#
using System;
using System.Security.Cryptography;
 
namespace RSAEncryptionUtilities
{
    // A struct representing the pair of 
    // public and private keys
    public struct RSAKeys
    {
        public string RSAPrivateKey;
        public string RSAPublicKey;
    }
 
    // A static class to generate the RSAKeys pair
    public static class RSAKeyGenerator
    {
        public static RSAKeys GetNewKeys()
        {
            // Default key length is 1024 bits or 128 bytes
            RSACryptoServiceProvider RSA =
                new RSACryptoServiceProvider();
            RSAKeys keys = new RSAKeys();
            keys.RSAPrivateKey = RSA.ToXmlString(true);
            keys.RSAPublicKey = RSA.ToXmlString(false);
 
            return keys;
        }
 
        public static RSAKeys GetNewKeys(int KeySize)
        {
            // Keys shorter than 48 or longer than 128 bytes
            // are not guarantted supported.
            if (KeySize < 48) KeySize = 48;
            if (KeySize > 128) KeySize = 128;
 
            RSACryptoServiceProvider RSA =
                new RSACryptoServiceProvider(KeySize * 8);
            RSAKeys keys = new RSAKeys();
            keys.RSAPrivateKey = RSA.ToXmlString(true);
            keys.RSAPublicKey = RSA.ToXmlString(false);
 
            return keys;
        }
    }
 
    // A class to encrypt data
    public class RSAEncryptor
    {
        private RSACryptoServiceProvider RSA;
        public RSAEncryptor(string RSAKey)
        {
            RSA = new RSACryptoServiceProvider();
            RSA.FromXmlString(RSAKey);
        }
 
        public int GetMaxEncryptionChunkSize()
        {
            return RSA.KeySize / 8 - 11;
        }
 
        public byte[] Encrypt(byte[] OriginalBytes)
        {
            int maxLength = GetMaxEncryptionChunkSize();
            int dataLength = OriginalBytes.Length;
            int iterations = dataLength / maxLength;
 
            System.Collections.ArrayList arrayList =
                new System.Collections.ArrayList();
            for (int i = 0; i <= iterations; i++)
            {
                int chunkSize =
                        (dataLength - maxLength * i > maxLength) ?
                        maxLength : dataLength - maxLength * i;
                if (chunkSize == 0) { break; }
 
                byte[] tempBytes =
                    new byte[chunkSize];
                System.Buffer.BlockCopy(OriginalBytes, maxLength * i,
                    tempBytes, 0, tempBytes.Length);
                byte[] encriptedBytes = RSA.Encrypt(tempBytes, false);
 
                System.Array.Reverse(encriptedBytes);
                arrayList.AddRange(encriptedBytes);
            }
 
            return (byte[])arrayList.ToArray(Type.GetType("System.Byte"));
        }
    }
 
    // A class to decrypt data
    public class RSADecryptor
    {
        private RSACryptoServiceProvider RSA;
        public RSADecryptor(string RSAPrivateKey)
        {
            RSA = new RSACryptoServiceProvider();
            RSA.FromXmlString(RSAPrivateKey);
        }
 
        public int GetEncryptedChunkSize()
        {
            return RSA.KeySize / 8;
        }
 
        public byte[] Descrypt(byte[] EncriptedBytes)
        {
            int EncriptedChunckSize = GetEncryptedChunkSize();
            int dataLength = EncriptedBytes.Length;
            int iterations = dataLength / EncriptedChunckSize;
 
            System.Collections.ArrayList arrayList =
                new System.Collections.ArrayList();
            for (int i = 0; i < iterations; i++)
            {
                byte[] tempBytes = new byte[EncriptedChunckSize];
                System.Buffer.BlockCopy(EncriptedBytes,
                    EncriptedChunckSize * i,
                    tempBytes, 0, tempBytes.Length);
                System.Array.Reverse(tempBytes);
 
                arrayList.AddRange(RSA.Decrypt(tempBytes, false));
            }
 
            return (byte[])arrayList.ToArray(Type.GetType("System.Byte"));
        }
    }
}

This encryption class library implements the following:

  • A "struct" "RSAKeys" used to store the encryption keys. Both the the public key and the private key are saved as .NET strings.
  • A static class "RSAKeyGenerator" used to generate new encryption keys.
  • A class "RSAEncryptor" used to encrypt the data.
  • A class "RSADecryptor" used to decrypt the encrypted data.

The reason why the encryption and decryption functions are implemented in two different classes is because we can use either a public key or a private key to encrypt the data, but we will have to use a private key to decrypt the encrypted data. You can refer to this web link for more information about why only private keys can be used for decryption. You may also notice that the "GetMaxEncryptionChunkSize()" method in the class "RSAEncryptor" uses "RSA.KeySize / 8 - 11" to calculate the maximum length of the data to be encrypted. You can refer to the Microsoft website to find out why the magic number "11" is used here.

In the remaining parts of this article, I will be introducing the WPF example application to demonstrate the usage this class library. If you are not interested in the WPF specific topics, you can directly go to the "How to use the encryption library" section of the article.

The WPF UI library

To simplify the development of the WPF example application "EncryptionLibraryExample", I created a class library "WPFUILibrary" to help the WPF UI development.

SolutionExplorerWPFUILib.jpg

"XMLViewer.xaml" and its code-behind file implement a WPF user control that will be used to display XML documents. I will skip the introduction of this user control in this article. If you are interested in it, you can take a look at my earlier article "A Simple WPF XML Document Viewer Control". "ModalController.cs" implements a simple class that helps the WPF applications to display a modal window in Silverlight style:

C#
using System.Windows;
using System.Windows.Controls;
 
namespace WPFUILibrary
{
    public class ModalController
    {
        private Panel ModalParent;
        private UIElement ModalElement;
 
        public ModalController(Panel ModalParent, UIElement ModalElement)
        {
            this.ModalParent = ModalParent;
            this.ModalElement = ModalElement;
        }
 
        public void Show() { ModalParent.Children.Add(ModalElement); }
        public void Close() { ModalParent.Children.Remove(ModalElement); }
    }
}

To display a Silverlight style model window in WPF applications, you can simply create an object of the class "ModalController" by passing the parent window's top level "Panel Element" and a reference of the modal window itself to the constructor. The modal windows are normally implemented as WPF "UserControls". After the object is created, you can call the "Show()" method to display the modal window, and call the "Close()" method to close it.

The WPF example application

SolutionExplorerWPFApplication.jpg

To demonstrate how the encryption library is used, a WPF application "EncryptionLibraryExample" is built. The entry point of this application is the code-behind file of "App.xaml":

C#
using System;
using System.Windows;
using System.Windows.Controls;
using System.Threading;
 
namespace EncryptionLibraryExample
{
    public partial class App : Application
    {
        public Grid ApplicationVisualRoot;
        public ApplicationMain applicationMainWnd;
        private void Application_Startup(object sender, StartupEventArgs e)
        {
            Splash SWnd = new Splash();
            applicationMainWnd = new ApplicationMain();
            ApplicationVisualRoot = applicationMainWnd.ApplicationVisualRoot;
 
            SWnd.Show(); Thread.Sleep(1500);
            applicationMainWnd.Show(); SWnd.Close();
        }
    }
}

It first displays a "Splash Screen", and then launches the application's main window. The "App.xaml" file itself keeps the resources used to control the overall styling of the whole application, which is implemented as follows:

XML
<Application x:Class="EncryptionLibraryExample.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             Startup="Application_Startup">
    
    <Application.Resources>
         <Style x:Key="WindowDefault" TargetType="Window">
            <Setter Property="FontFamily" Value="Verdana" />
            <Setter Property="FontSize" Value="10" />
        </Style>
        
        <Style x:Key="ModalBackground" TargetType="Rectangle">
            <Setter Property="Fill" Value="Black" />
            <Setter Property="Opacity" Value="0.5" />
        </Style>
        
        <Style x:Key="SmallBold" TargetType="TextBlock">
            <Setter Property="FontWeight" Value="Bold" />
            <Setter Property="FontSize" Value="10" />
        </Style>
        
        <DataTemplate x:Key="TabItemHeaderTemplate">
            <Grid Margin="5,10,5,10">
                <TextBlock Text="{TemplateBinding Content}"
                           Style="{StaticResource SmallBold}" />
            </Grid>
        </DataTemplate>
    </Application.Resources>
</Application>

The "Splash Screen" is implemented in "Splash.xaml":

XML
<Window x:Class="EncryptionLibraryExample.Splash"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      WindowStyle="None" Background="{x:Null}" 
      AllowsTransparency="True">
    <Image Source="Images/EncryptionExample.gif" />
</Window>

The "Splash Screen" simply displays a picture "EncryptionExample.gif". You can find this picture file in the "Images" folder in the Solution Explorer. The application's main window is implemented in the "ApplicationMain.xaml" file:

XML
<Window x:Class="EncryptionLibraryExample.ApplicationMain"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="{Binding Path=ApplicationName, Mode=OneTime}"
    Style="{StaticResource WindowDefault}" >
    
    <Window.Resources>
    </Window.Resources>
    
    <Grid x:Name="ApplicationRoot">
    <Grid VerticalAlignment="Stretch"
          HorizontalAlignment="Stretch" Margin="0,0,0,0">
        <Grid.RowDefinitions>
            <RowDefinition Height="20" />
            <RowDefinition Height="*" />
            <RowDefinition Height="20" />
        </Grid.RowDefinitions>
 
            <Menu Grid.Row="0" HorizontalAlignment="Stretch"
                  Background="AliceBlue">
                <MenuItem Header="File"></MenuItem>
                <MenuItem Header="Help">
                    <MenuItem Header="About" Click="Help_Click" />
                </MenuItem>
            </Menu>
 
 
            <Grid Grid.Row="1" 
              x:Name="MainContent" HorizontalAlignment="Stretch" 
              VerticalAlignment="Stretch" Margin="5, 2, 5, 10">
            <TabControl FontSize="12" x:Name="ApplicationMainTab">
                <TabItem
                    HeaderTemplate="{StaticResource TabItemHeaderTemplate}">
                    <TabItem.Header>
                        Generate encryption keys
                    </TabItem.Header>
                </TabItem>
                
                <TabItem
                    HeaderTemplate="{StaticResource TabItemHeaderTemplate}">
                    <TabItem.Header>
                        Encrypt text and convert the result to base64 string
                    </TabItem.Header>
                </TabItem>
                
                <TabItem
                    HeaderTemplate="{StaticResource TabItemHeaderTemplate}">
                    <TabItem.Header>
                        Encrypt binary data
                    </TabItem.Header>
                </TabItem>
 
            </TabControl>
        </Grid>
 
        <TextBlock Grid.Row="2" 
                   Text="{Binding Path=Copyright, Mode=OneTime}"
                   HorizontalAlignment="Center" 
                   Style="{StaticResource SmallBold}"
                   Foreground="Silver" />
    </Grid>
    </Grid>
</Window>

"ApplicationMain.xaml" has the following UI components:

  • A "Menu" that will be used to launch a modal help box.
  • A "TabControl" which has three tabs:
    • The first tab will be used to demonstrate how to use the encryption class library to generate encryption keys.
    • The second tab will be used to demonstrate how to encrypt text content, translate the encrypted content into "Base64" format, and decrypt the "Base64" content back to the original text.
    • The third tab will be used to demonstrate how to encrypt and decrypt binary data.
  • The "ApplicationMain.xaml" file also displays some general information about the application, such as the application name and the copyright information about this application.

The code-behind file of "ApplicationMain.xaml" is the following:

C#
using System;
using EncryptionLibraryExample.Properties;
using System.Windows;
using System.Windows.Controls;
using WPFUILibrary;
 
namespace EncryptionLibraryExample
{
    public partial class ApplicationMain : Window
    {
        public ApplicationMain()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(ApplicationMain_Loaded);
        }
 
        public Grid ApplicationVisualRoot
        {
            get { return ApplicationRoot; }
        }
 
        void ApplicationMain_Loaded(object sender, RoutedEventArgs e)
        {
            this.DataContext = Settings.Default;
            ((TabItem)ApplicationMainTab.Items[0]).Content
                = new GenerateEncryptionKeysExample();
            ((TabItem)ApplicationMainTab.Items[1]).Content
                = new Base64EncryptionExample();
            ((TabItem)ApplicationMainTab.Items[2]).Content
                = new BinaryEncryptionExample();
        }
 
        private void Help_Click(object sender, RoutedEventArgs e)
        {
            Help HelpWnd = new Help();
            HelpWnd.ShowModal();
        }
    }
}

This file sets the "DataContext" of the "ApplicationMain.xaml" file to the application's settings, so the application's name and the copyright information displayed on the application's main window are directly configurable through the application's settings, which is configured as the following in this application:

ApplicationSettings.jpg

In addition to setting the "DataContext", this file also does the following:

  • Hook up three user controls to demonstrate the usage of the encryption class library to the "TabControl" in the "ApplicationMain.xaml" file.
  • Process the menu event to launch the help window.

The three user controls will be introduced in the "How to use the encryption library?" section. The help window is implemented in the "Help.xaml" file as a user control:

XML
<UserControl x:Class="EncryptionLibraryExample.Help"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
    <Grid>
        <Rectangle Style="{StaticResource ModalBackground}" />
        <Grid Background="White" VerticalAlignment="Center"
                  HorizontalAlignment="Center" >
            <Border BorderBrush="Black" BorderThickness="2">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="20" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>
                    
                    <Border Grid.Row="0" BorderBrush="silver" BorderThickness="1">
                        <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*" />
                                <ColumnDefinition Width="20" />
                            </Grid.ColumnDefinitions>
                            
                            <Rectangle Fill="LightBlue" />
                            <StackPanel Grid.Column="0" Orientation="Horizontal"
                                        VerticalAlignment="Center">
                                <TextBlock Foreground="White"
                                           Style="{StaticResource SmallBold}"
                                           Margin="5,0,5,0">About</TextBlock>
                                <TextBlock Foreground="White"
                                           Style="{StaticResource SmallBold}"
                                           Text="{Binding Path=ApplicationName,
                                Mode=OneTime}"/>
                            </StackPanel>
                            <Button Grid.Column="1" 
                                  Background="LightBlue" Click="CloseWnd">
                                <TextBlock Style="{StaticResource SmallBold}">
                                    X</TextBlock>
                            </Button>
                        </Grid>
                    </Border>
                    
                    <Grid Grid.Row="1" Margin="10,10,10,10">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="*" />
                        </Grid.RowDefinitions>
                    
                        <Grid Grid.Row="0" Margin="0,0,0,3">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="120" />
                                <ColumnDefinition Width="*" />
                            </Grid.ColumnDefinitions>
                            
                            <Grid.RowDefinitions>
                                <RowDefinition Height="20" />
                                <RowDefinition Height="20" />
                                <RowDefinition Height="20" />
                                <RowDefinition Height="20" />
                                <RowDefinition Height="20" />
                            </Grid.RowDefinitions>
                            
                            <TextBlock Grid.Row="0" Grid.Column="0">
                                Application Name</TextBlock>
                            <TextBlock Grid.Row="1" Grid.Column="0">
                                Author</TextBlock>
                            <TextBlock Grid.Row="2" Grid.Column="0">
                                Development Date</TextBlock>
                            <TextBlock Grid.Row="3" Grid.Column="0">
                                Copyright</TextBlock>
                            <TextBlock Grid.Row="4" Grid.Column="0">
                                Version</TextBlock>
                            
                            <TextBlock Grid.Row="0" Grid.Column="1" 
                                       Text="{Binding Path=ApplicationName,
                                Mode=OneTime}" />
                            <TextBlock Grid.Row="1" Grid.Column="1" 
                                       Text="{Binding Path=Author,
                                Mode=OneTime}" />
                            <TextBlock Grid.Row="2" Grid.Column="1" 
                                       Text="{Binding Path=DevelopmentDate,
                                Mode=OneTime}" />
                            <TextBlock Grid.Row="3" Grid.Column="1" 
                                       Text="{Binding Path=Copyright,
                                Mode=OneTime}" />
                            <TextBlock Grid.Row="4" Grid.Column="1" 
                                       Text="{Binding Path=Version,
                                Mode=OneTime}" />
                        </Grid>
                    </Grid>
                    </Grid>
            </Border>
        </Grid>
    </Grid>
</UserControl>

The code-behind file of the "Help.xaml" file is implemented as follows:

C#
using System;
using System.Windows;
using System.Windows.Controls;
using EncryptionLibraryExample.Properties;
using WPFUILibrary;
 
namespace EncryptionLibraryExample
{
    /// <summary>
    /// Interaction logic for Help.xaml
    /// </summary>
    public partial class Help : UserControl
    {
        private ModalController modalController;
        public Help()
        {
            InitializeComponent();
            modalController = new ModalController((
              (App)Application.Current).ApplicationVisualRoot, this);
            this.Loaded += new RoutedEventHandler(Help_Loaded);
        }
 
        void Help_Loaded(object sender, RoutedEventArgs e)
        {
            DataContext = Settings.Default;
        }
 
        public void ShowModal() { modalController.Show();}
 
        private void CloseWnd(object sender, RoutedEventArgs e)
        {
            modalController.Close();
        }
    }
}

In this file, the "ModalController" class implemented in the "WPFUILibrary" project is used to help the calling function to launch the help window as a Silverlight style modal window. This file also sets the "DataContext" of the "Help.xaml" file to the application's settings mentioned earlier.

How to use the encryption library

In the example WPF application, I will show you the following:

  1. How to generate the encryption keys.
  2. How to encrypt/decrypt text data and how to transform the encrypted content to "Base64" format for displaying purpose.
  3. How to encrypt/decrypt binary data.

I will implement three "user controls". Each of the controls is inserted into the main application window's "TabControl" introduced earlier.

The "GenerateEncryptionKeysExample.xaml" file implements the user control to demonstrate how to use the encryption class library to generate encryption keys:

XML
<UserControl x:Class="EncryptionLibraryExample.GenerateEncryptionKeysExample"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Utility="clr-namespace:WPFUILibrary;assembly=WPFUILibrary">
    
    <Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
          Margin="10,0,10,0">
        <Grid.RowDefinitions>
            <RowDefinition Height="20" />
            <RowDefinition Height="90" />
            <RowDefinition Height="20" />
            <RowDefinition Height="*" />
            <RowDefinition Height="5" />
            <RowDefinition Height="25" />
        </Grid.RowDefinitions>
        
        <TextBlock Grid.Row="2" Style="{StaticResource SmallBold}">
            Private Key:</TextBlock>
        <Utility:XMLViewer x:Name="xmlViewerPrivateKey" Grid.Row="3" />
        <TextBox x:Name="txtPrivateKey" Grid.Row="3" TextWrapping="Wrap"
                 IsReadOnly="True"
                 VerticalScrollBarVisibility="Auto" />
        
        <TextBlock Grid.Row="0" Style="{StaticResource SmallBold}">
            Public Key:</TextBlock>
        <Utility:XMLViewer x:Name="xmlViewerPublicKey" Grid.Row="1" />
        <TextBox x:Name="txtPublicKey" Grid.Row="1" TextWrapping="Wrap"
                 IsReadOnly="True"
                 VerticalScrollBarVisibility="Auto" />
 
        <StackPanel Grid.Row="5" HorizontalAlignment="Right"
                    Orientation="Horizontal" Margin="0,0,5,0"
                    VerticalAlignment="Center">
            <TextBlock Margin="0,0,10,0" Style="{StaticResource SmallBold}">
                Select a key size and click "Generate new keys" button for new keys
            </TextBlock>
            <ComboBox x:Name="listKeySizes" Margin="0,0,5,0" Width="50"
                      SelectionChanged="listKeySizes_SelectionChanged"/>
            <Button Width="180" Click="SwitchDisplayFormat"
                    Margin="0,0,5,0">
                Switch XML/Text Display</Button>
            <Button Width="150" Click="GenerateKeys">
                Generate new keys</Button>
        </StackPanel>
        
    </Grid>
</UserControl>

and its code-behind file is implemented as follows:

C#
using System;
using System.Windows;
using System.Windows.Controls;
using RSAEncryptionUtilities;
using System.Xml;
 
namespace EncryptionLibraryExample
{
    public partial class GenerateEncryptionKeysExample : UserControl
    {
        private bool ViewRSAKeysAsXML;
 
        public GenerateEncryptionKeysExample()
        {
            InitializeComponent();
            InitializeKeySizeList();
            ViewRSAKeysAsXML = true;
            ChangeDisplayFormat();
        }
 
        private void InitializeKeySizeList()
        {
            listKeySizes.Items.Clear();
            listKeySizes.Items.Add("***");
            listKeySizes.SelectedIndex = 0;
            for (int Idex = 48; Idex <= 128; Idex ++)
            {
                listKeySizes.Items.Add(Idex);
            }
        }
 
        private void ChangeDisplayFormat()
        {
            if (ViewRSAKeysAsXML)
            {                
                xmlViewerPrivateKey.Visibility = Visibility.Visible;
                xmlViewerPublicKey.Visibility = Visibility.Visible;
                txtPrivateKey.Visibility = Visibility.Collapsed;
                txtPublicKey.Visibility = Visibility.Collapsed;
            }
            else
            {
                xmlViewerPrivateKey.Visibility = Visibility.Collapsed;
                xmlViewerPublicKey.Visibility = Visibility.Collapsed;
                txtPrivateKey.Visibility = Visibility.Visible;
                txtPublicKey.Visibility = Visibility.Visible;
            }
        }
 
        private void GenerateKeys(object sender, RoutedEventArgs e)
        {
            if (listKeySizes.SelectedItem.ToString() == "***")
            {
                MessageBox.Show(((App)Application.Current).applicationMainWnd,
                    "Please select a key size");
                return;
            }
 
            RSAKeys EncriptionKeys =
                RSAKeyGenerator.GetNewKeys((int)listKeySizes.SelectedItem);
            string PrivateKey = EncriptionKeys.RSAPrivateKey;
            string PublicKey = EncriptionKeys.RSAPublicKey;
 
            XmlDocument XMLPrivateKey = new XmlDocument();
            XMLPrivateKey.LoadXml(PrivateKey);
 
            XmlDocument XMLPublicKey = new XmlDocument();
            XMLPublicKey.LoadXml(PublicKey);
 
            txtPrivateKey.Text = PrivateKey;
            txtPublicKey.Text = PublicKey;
            xmlViewerPrivateKey.xmlDocument = XMLPrivateKey;
            xmlViewerPublicKey.xmlDocument = XMLPublicKey;
        }
 
        private void SwitchDisplayFormat(object sender,
            RoutedEventArgs e)
        {
            ViewRSAKeysAsXML = !ViewRSAKeysAsXML;
            ChangeDisplayFormat();
        }
 
        private void listKeySizes_SelectionChanged(object sender,
            SelectionChangedEventArgs e)
        {
            txtPrivateKey.Text = "";
            txtPublicKey.Text = "";
            xmlViewerPrivateKey.xmlDocument = null;
            xmlViewerPublicKey.xmlDocument = null;
        }
    }
}

The "Base64EncryptionExample.xaml" file implements a control to demonstrate how to encrypt/decrypt text data and translate the encrypted content into "Base64" format:

XML
<UserControl x:Class="EncryptionLibraryExample.Base64EncryptionExample"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
    <Grid VerticalAlignment="Stretch"
          HorizontalAlignment="Stretch" Margin="5,5,5,10">
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="40" />
        </Grid.RowDefinitions>
        
        <Grid Grid.Row="0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="120" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="20" />
                <RowDefinition Height="5" />
                <RowDefinition Height="*" />
                <RowDefinition Height="5" />
                <RowDefinition Height="20" />
            </Grid.RowDefinitions>
        
            <TextBlock Grid.Row="0" Grid.Column="0" 
                       Style="{StaticResource SmallBold}">
                Original Text</TextBlock>
            <TextBlock Grid.Row="2" Grid.Column="0"
                       Style="{StaticResource SmallBold}">
                Encrypted Text</TextBlock>
            <TextBlock Grid.Row="4" Grid.Column="0"
                       Style="{StaticResource SmallBold}">
                Decrypted Text</TextBlock>
            
            <TextBox Grid.Row="0" Grid.Column="1"
                     x:Name="txtOriginalText" MaxLength="100"/>
            <TextBox Grid.Row="2" Grid.Column="1"
                     x:Name="txtEncyptedText" TextWrapping="Wrap"
                     VerticalScrollBarVisibility="Auto"
                     IsReadOnly="True" />
            <TextBox Grid.Row="4" Grid.Column="1"
                     x:Name="txtDecyptedText" IsReadOnly="True" />
        </Grid>
        
        <StackPanel Grid.Row="1" Orientation="Horizontal"
                    HorizontalAlignment="Right" Margin="0, 12, 10, 5">
            <Button Margin="0,0,5,0" Width="100"
                    Click="Encrypt_Click">Encrypt</Button>
            <Button Width="100" Click="Decrypt_Click">Decrypt</Button>
        </StackPanel>
        
    </Grid>
</UserControl>

Its code-behind file is implemented as follows:

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using EncryptionLibraryExample.EncryptionKeys;
using RSAEncryptionUtilities;
 
namespace EncryptionLibraryExample
{
    public partial class Base64EncryptionExample : UserControl
    {
        public Base64EncryptionExample()
        {
            InitializeComponent();
        }
 
        private void Encrypt_Click(object sender, RoutedEventArgs e)
        {
            string TextToEncypt = txtOriginalText.Text.Trim();
            if (TextToEncypt == String.Empty)
            {
                MessageBox.Show("Please type in some text to encrypt");
                return;
            }
 
            txtEncyptedText.Clear();
            txtDecyptedText.Clear();
 
            string EncryptedBase64String = String.Empty;
            try
            {
                string EncryptionKey = RSAKeysStore.PublicKey;
                RSAEncryptor encryptor = new RSAEncryptor(EncryptionKey);
                byte[] OriginalBytes = System.Text.Encoding.UTF8.GetBytes(TextToEncypt);
                byte[] EncryptedBytes = encryptor.Encrypt(OriginalBytes);
                EncryptedBase64String = System.Convert.ToBase64String(EncryptedBytes);
            }
            catch (Exception ex)
            {
                MessageBox.Show("Encryption Failed:" + ex.Message);
                return;
            } 
 
            txtEncyptedText.Text = EncryptedBase64String;
            MessageBox.Show("Encyption succeeded");
        }
 
        private void Decrypt_Click(object sender, RoutedEventArgs e)
        {
            string Base64ToDecrypt = txtEncyptedText.Text.Trim();
            if (Base64ToDecrypt == String.Empty)
            {
                MessageBox.Show("Please encrypt some text before the decyption");
                return;
            }
 
            txtDecyptedText.Clear();
 
            string DecryptedText = String.Empty;
 
            try
            {
                string EncryptionKey = RSAKeysStore.PrivateKey;
                RSADecryptor decryptor = new RSADecryptor(EncryptionKey);
                byte[] EncryptedBytes = System.Convert.FromBase64String(Base64ToDecrypt);
                byte[] DecryptedBytes = decryptor.Descrypt(EncryptedBytes);
                DecryptedText = System.Text.Encoding.UTF8.GetString(DecryptedBytes);
            }
            catch (Exception ex)
            {
                MessageBox.Show("Decryption Failed:" + ex.Message);
                return;
            }
 
            txtDecyptedText.Text = DecryptedText;
            MessageBox.Show("Decyption succeeded");
        }
    }
}

The "BinaryEncryptionExample.xaml" file implements a control to demonstrate how to encrypt/decrypt binary contents:

XML
<UserControl x:Class="EncryptionLibraryExample.BinaryEncryptionExample"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
    <Grid VerticalAlignment="Center"
          HorizontalAlignment="Center" Margin="5,5,5,10">
 
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="250" />
            <ColumnDefinition Width="10" />
            <ColumnDefinition Width="250" />
        </Grid.ColumnDefinitions>
        
        <Button Grid.Column="0" Height="30"
                Click="EncryptBinary_Click">
            Browse a file to encrypt</Button>
        
        <Button Grid.Column="2" 
                Height="30" Click="DecryptBinary_Click">
            Browse an encrypted file to decrypt</Button>
    </Grid>
</UserControl>

Its code-behind file is implemented as follows:

C#
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using EncryptionLibraryExample.EncryptionKeys;
using RSAEncryptionUtilities;
using Microsoft.Win32;
 
namespace EncryptionLibraryExample
{
    public partial class BinaryEncryptionExample : UserControl
    {
        private const string EncriptedFileExtension = ".encripted";
 
        public BinaryEncryptionExample()
        {
            InitializeComponent();
        }
 
        private void EncryptBinary_Click(object sender, RoutedEventArgs e)
        {
            byte[] inputBytes = null;
            FileStream inputFile = null;
            OpenFileDialog dlgOpen = null;
 
            try
            {
                dlgOpen = new OpenFileDialog();
                if (dlgOpen.ShowDialog() != true) { return; }
 
                inputFile =
                    new FileStream(dlgOpen.FileName, FileMode.Open, FileAccess.Read);
 
                inputBytes = new byte[inputFile.Length];
                inputFile.Read(inputBytes, 0, System.Convert.ToInt32(inputFile.Length));
            }
            catch (Exception ex)
            {
                inputFile.Dispose();
                MessageBox.Show("Unable to open the file selected: " + ex.Message);
                return;
            }
 
            string encriptedFileName = dlgOpen.FileName + EncriptedFileExtension;
            FileStream outputFile = null;
            try
            {
                string EncryptionKey = RSAKeysStore.PublicKey;
                RSAEncryptor encryptor = new RSAEncryptor(EncryptionKey);
                byte[] EncryptedBytes = encryptor.Encrypt(inputBytes);
                inputFile.Close();
 
                outputFile =
                    new FileStream(encriptedFileName, FileMode.OpenOrCreate,
                        FileAccess.Write);
                outputFile.Write(EncryptedBytes, 0, EncryptedBytes.Length);
 
 
                outputFile.Flush();
                outputFile.Close();
            }
            catch (Exception ex)
            {
                outputFile.Dispose();
                MessageBox.Show("Encryption failed: " + ex.Message);
                return;
            }
 
           MessageBox.Show("The encryption is completed, result saved to "
               + encriptedFileName);
        }
 
        private void DecryptBinary_Click(object sender, RoutedEventArgs e)
        {
            FileStream inputFile = null;
            byte[] inputBytes = null;
            OpenFileDialog dlgOpen = null;
            try
            {
                dlgOpen = new OpenFileDialog();
                dlgOpen.Filter = "Encripted files (" + 
                    EncriptedFileExtension + ")|*" +
                    EncriptedFileExtension;
                if (dlgOpen.ShowDialog() != true) { return; }
 
 
                inputFile =
                    new FileStream(dlgOpen.FileName, FileMode.Open, FileAccess.Read);
 
                inputBytes = new byte[inputFile.Length];
                inputFile.Read(inputBytes, 0, System.Convert.ToInt32(inputFile.Length));
            }
            catch (Exception ex)
            {
                inputFile.Dispose();
                MessageBox.Show("Unable to open the file selected: " + 
                                ex.Message);
                return;
            }
 
            string encritpedFileFullPath = String.Empty;
            string decriptedFileFullPath = String.Empty;
            FileStream outputFile = null;
            try
            {
                string EncryptionKey = RSAKeysStore.PrivateKey;
                RSADecryptor decryptor = new RSADecryptor(EncryptionKey);
                byte[] DecryptedBytes = decryptor.Descrypt(inputBytes);
                inputFile.Close();
 
                encritpedFileFullPath = dlgOpen.FileName;
                string encritpedFilePath =
                    encritpedFileFullPath.Substring(0, 
                    encritpedFileFullPath.LastIndexOf("\\"));
                string encritpedFileName =
                    encritpedFileFullPath.Substring(
                    encritpedFileFullPath.LastIndexOf("\\") + 1);
                decriptedFileFullPath = encritpedFilePath + "\\" + "Decripted" +
                    encritpedFileName.Substring(0,
                    encritpedFileName.Length - EncriptedFileExtension.Length);
 
 
                outputFile =
                    new FileStream(decriptedFileFullPath, FileMode.OpenOrCreate,
                        FileAccess.Write);
                outputFile.Write(DecryptedBytes, 0, DecryptedBytes.Length);
                outputFile.Flush();
                outputFile.Close();
            }
            catch (Exception ex)
            {
                outputFile.Dispose();
                MessageBox.Show("Decryption failed: " + ex.Message);
                return;
            }
 
            MessageBox.Show("The decryption is completed, result saved to "
                + decriptedFileFullPath);
        }
    }
}

The encryption keys used by the above examples are saved in the "RSAKeysStore.cs" file in the "EncryptionKeys" folder in Solution Explorer:

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EncryptionLibraryExample.EncryptionKeys
{
    public static class RSAKeysStore
    {
        public const string PrivateKey = "<RSAKeyValue><Modulus>" +
            "1CrxSx3ktC+IQLDM/JTm2GTq8ep7M8Te8ND6wqsK2iQ2xpSrPBk" + 
            "1DxwtnBqaYcQt</Modulus><Exponent>AQAB</Exponent><P>/" + 
            "GPiWDR3i8oOAg0PHIp2AMtPdhy/r9LH</P><Q>1zPJIwPw4JZx2wc" + 
            "9+wyqVGfDBZDMuv1r</Q><DP>3+03Cw8x6aLRntw7RhVK8RVxYNfM8" + 
            "pBN</DP><DQ>cwESuSqu/GaJu+I35kTTdb3pw7ypHDi3</DQ>" + 
            "<InverseQ>FvDwRAmKCgMUB7QfkRWrZ8bM/91rmj7n</InverseQ>" + 
            "<D>n9yLWrH3dNyrqTKOAXKgTUQc0pJ+qg8XG8HmvkrdoB7rXxzd0l" + 
            "xBCaNeURxafUxx</D></RSAKeyValue>";
 
        public const string PublicKey = "<RSAKeyValue><Modulus>" + 
            "1CrxSx3ktC+IQLDM/JTm2GTq8ep7M8Te8ND6wqsK2iQ2xpSrPB" + 
            "k1DxwtnBqaYcQt</Modulus><Exponent>AQAB</Exponent>" + 
            "</RSAKeyValue>";
    }
}

Run the application

Now we have completed the implementation of the encryption class library and the example WPF application. Compile and run the application; you will first see the splash screen and then the application's main window. After selecting the first tab "Generate encryption keys", you can select a key size from the drop-down box and click the "Generate new keys" button, and a pair of newly generated encryption keys will be displayed in XML format:

Image 6

Click the "Switch XML/Text Display" button, and the keys will be displayed as "text". If you want to use this pair of encryption keys, you can copy and save them somewhere.

Image 7

Let us now switch to the second tab "Encrypt text and convert the result to Base64 string". Type in some text into "Original Text" and click the "Encrypt" button; you will see the "Base64" string of the encrypted content shown in the "Encrypted Text" "TextBox". Click the "Decrypt" button, and the encrypted content is decrypted to the "Decrypted Text" "TextBox".

Image 8

To see how the binary data is encrypted/decrypted, you can select the third tab "Encrypt binary data":

Image 9

Click the "Browse a file to encrypt" button, an "OpenFileDialog" box is shown to let you select a file to encrypt. Once you select the file, the application will encrypt the file and save the encrypted content to another file and use the same file name by adding ".encripted" as the file extension.

Image 10

Similarly, you can click the "Browse an encrypted file to decrypt" button to decrypt the encrypted file. After the decryption, you can open the file to see if the file content remains the same as the original file. In my test run of this application, I encrypted the following image file:

Tiger.jpg

Click the menu item "Help" and then "About", the help window is shown in Silverlight style:

Image 12

Points of interest

  • This article introduced an encryption class library based upon the "System.Security.Cryptography" namespace from Microsoft. Using the functions from Microsoft makes the implementation of the class library very simple, but at the same time, the functions and the performance of the class library are also limited by the functions and the performance provided by Microsoft.
  • The example application in this article demonstrated how to encrypt/decrypt both text and binary data. The class library itself actually does not differentiate if the data is text or binary. To use the class library, all data need to be transformed into a "byte array".
  • In most application scenarios, the performance of the class library should be sufficient. But I do notice that the class library has difficulty to handle large binary files, particularly when decrypting data. If you use the class library as it is, all the data in the files will be read into the memory before it is processed. This will result in huge memory consumption. To solve this problem, you can divide the file into pieces and process the data piece by piece. I will leave it to the interested readers to modify the class library to improve the performance. Mathew John Schlabaugh's article "Public Key RSA Encryption in C# .NET" should be a good reference if you do encounter performance problems.
  • To be able to decrypt some encrypted data, you will need to have the encryption key. If you lose your encryption key, no one can decrypt it. You will need to find a good way to secure your encryption key. In this article, the encryption key is compiled into the example application, which may not be the best way to secure keys.
  • This article was intended to be a short article at the beginning, but the WPF example application made it unnecessarily lengthy. If you are interested in the techniques to develop WPF applications, it may help you. But if you are not interested in these techniques, I hope you are not bothered by this lengthy WPF example application.

History

This is the first revision of the article.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)