一个 .NET 加密库
本文介绍了一个 .NET 加密库,并演示了如何使用该库创建一个 WPF 应用程序。
引言
本文介绍了一个 .NET 加密库,并演示了如何使用该库创建一个 WPF 应用程序。
背景
信息技术的发展使得数据安全成为一个非常重要的问题。加密数据是保护数据最常见和最有效的方法之一。大多数软件产品都附带内置的数据加密机制,例如用于互联网安全通信的 SSL/TLS 和用于安全存储数据的 SQL Server 加密。
程序员可能希望编写代码来实现自己应用程序中的数据加密,这种情况并不少见。本文介绍了一个 .NET 加密库,它使得数据加密/解密变得容易。该库基于 .NET Framework 中的“System.Security.Cryptography
”命名空间构建。本文的灵感来源于另一篇 CodeProject 文章:“Public Key RSA Encryption in C# .NET”,作者是 Mathew John Schlabaugh。本文将 Mathew 的想法融入一个类库中,并提供一个 WPF 应用程序来演示如何使用该库。您还可以找到其他关于此主题的精彩文章,例如 Peter A. Bromberg 的“RSA Encryption in .NET -- Demystified!”。当然,“维基百科”也总是在我们身边。
该类库和示例 WPF 应用程序是在一个 Visual Studio 2008 解决方案中开发的,其中包含三个 .NET 项目。
- "RSAEncryptionUtilities" 是加密类库。
- "WPFUILibrary" 是一个类库,用于简化 WPF 应用程序用户界面的构建。在此类库中,我实现了一个易于使用的“模态窗口控制器”来显示“模态窗口”以及一个“WPF XML 文档查看器”用户控件来显示 XML 文档。
- "EncryptionLibraryExample" 是演示如何使用加密库的 WPF 应用程序。
在本文中,我将首先介绍加密类库,然后介绍在“WPFUILibrary”项目中实现的 WPF UI 工具。最后,我们将讨论“EncryptionLibraryExample”WPF 应用程序以及加密类库是如何使用的。本文附带 Visual Studio 解决方案的完整源代码。如果您对此文章感兴趣,我建议您下载源代码进行编译和运行。首先运行应用程序,将使您更容易阅读本文。示例应用程序是用 WPF 编写的,其中包含一些有用的 WPF 技术。在本文中,这些 WPF 技术对于一个简单的示例应用程序来说可能有点“杀鸡用牛刀”。如果您对 WPF 特定主题不感兴趣,在浏览完加密类库后,可以直接跳转到“如何使用加密库”部分。
加密类库
如上图所示,加密类库是在“Utilities.cs”文件中实现的。
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"));
}
}
}
这个加密类库实现了以下功能:
- 一个用于存储加密密钥的“
RSAKeys
”结构体
。公钥和私钥都以 .NET 字符串形式保存。 - 一个名为“
RSAKeyGenerator
”的静态类,用于生成新的加密密钥。 - 一个名为“
RSAEncryptor
”的类,用于加密数据。 - 一个名为“
RSADecryptor
”的类,用于解密加密的数据。
加密和解密功能实现为两个不同的类,是因为我们可以使用公钥或私钥来加密数据,但必须使用私钥来解密加密的数据。您可以参考此网页链接以获取更多关于为什么只能使用私钥进行解密的信息。您可能还会注意到,“RSAEncryptor
”类中的“GetMaxEncryptionChunkSize()
”方法使用“RSA.KeySize
/ 8 - 11”来计算要加密的数据的最大长度。您可以参考微软网站来了解为什么这里使用了神奇的数字“11”。
在本文的其余部分,我将介绍 WPF 示例应用程序,以演示此类库的用法。如果您对 WPF 特定主题不感兴趣,可以跳转到文章的“如何使用加密库”部分。
WPF UI 库
为了简化“EncryptionLibraryExample”WPF 示例应用程序的开发,我创建了一个名为“WPFUILibrary”的类库来辅助 WPF UI 的开发。
"XMLViewer.xaml" 及其代码隐藏文件实现了一个 WPF 用户控件,用于显示 XML 文档。我将在本文中省略对此用户控件的介绍。如果您对此感兴趣,可以查看我之前的文章“A Simple WPF XML Document Viewer Control”。"ModalController.cs" 实现了一个简单的类,它帮助 WPF 应用程序以 Silverlight 风格显示模态窗口。
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); }
}
}
要在 WPF 应用程序中显示 Silverlight 风格的模态窗口,您可以简单地创建一个“ModalController
”类的对象,并将父窗口的顶层“Panel 元素”以及模态窗口本身的引用传递给构造函数。模态窗口通常实现为 WPF 的“用户控件”。创建对象后,可以调用“Show()
”方法显示模态窗口,并调用“Close()
”方法关闭它。
WPF 示例应用程序
为了演示加密库的使用方法,我们构建了一个 WPF 应用程序“EncryptionLibraryExample”。此应用程序的入口点是“App.xaml”的代码隐藏文件。
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();
}
}
}
它首先显示一个“启动画面”,然后启动应用程序的主窗口。“App.xaml”文件本身保留了用于控制整个应用程序整体样式的资源,其实现如下:
<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>
"启动画面" 在“Splash.xaml”中实现。
<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>
"启动画面" 仅显示一张图片“EncryptionExample.gif”。您可以在解决方案资源管理器中的“Images”文件夹中找到此图片文件。“应用程序的主窗口是在“ApplicationMain.xaml”文件中实现的。
<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" 包含以下 UI 组件:
- 一个“
菜单
”,用于启动一个模态帮助框。 - 一个“
选项卡控件
”,包含三个选项卡: - 第一个选项卡将用于演示如何使用加密类库生成加密密钥。
- 第二个选项卡将用于演示如何加密文本内容,将加密内容转换为“Base64”格式,并将“Base64”内容解密回原始文本。
- 第三个选项卡将用于演示如何加密和解密二进制数据。
- "ApplicationMain.xaml" 文件还显示了一些关于应用程序的通用信息,例如应用程序名称和此应用程序的版权信息。
"ApplicationMain.xaml" 的代码隐藏文件如下:
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();
}
}
}
此文件将“ApplicationMain.xaml”文件的“DataContext
”设置为应用程序的设置,因此在应用程序主窗口上显示的应用程序名称和版权信息可以直接通过应用程序设置进行配置,该配置在此应用程序中如下所示:
除了设置“DataContext
”之外,此文件还执行以下操作:
- 将三个用户控件(用于演示加密类库的用法)连接到“ApplicationMain.xaml”文件中的“
选项卡控件
”。 - 处理菜单事件以启动帮助窗口。
这三个用户控件将在“如何使用加密库?”部分进行介绍。帮助窗口在“Help.xaml”文件中实现为一个用户控件。
<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>
"Help.xaml" 文件的代码隐藏文件实现如下:
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();
}
}
}
在此文件中,"WPFUILibrary" 项目中实现的“ModalController
”类用于帮助调用函数以 Silverlight 风格的模态窗口形式启动帮助窗口。此文件还将“Help.xaml”文件的“DataContext
”设置为前面提到的应用程序的设置。
如何使用加密库
在示例 WPF 应用程序中,我将向您展示以下内容:
- 如何生成加密密钥。
- 如何加密/解密文本数据以及如何将加密内容转换为“Base64”格式以便显示。
- 如何加密/解密二进制数据。
我将实现三个“用户控件”。每个控件都插入到前面介绍的主应用程序窗口的“TabControl
”中。
"GenerateEncryptionKeysExample.xaml" 文件实现了用户控件,用于演示如何使用加密类库生成加密密钥。
<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>
其代码隐藏文件实现如下:
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;
}
}
}
"Base64EncryptionExample.xaml" 文件实现了一个控件,用于演示如何加密/解密文本数据并将加密内容转换为“Base64”格式。
<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>
其代码隐藏文件实现如下:
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");
}
}
}
"BinaryEncryptionExample.xaml" 文件实现了一个控件,用于演示如何加密/解密二进制内容。
<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>
其代码隐藏文件实现如下:
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);
}
}
}
上述示例使用的加密密钥存储在解决方案资源管理器“EncryptionKeys”文件夹的“RSAKeysStore.cs”文件中。
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>";
}
}
运行应用程序
现在我们已经完成了加密类库和示例 WPF 应用程序的实现。编译并运行该应用程序,您将首先看到启动画面,然后是应用程序的主窗口。选择第一个选项卡“生成加密密钥”后,您可以从下拉框中选择一个密钥大小,然后点击“生成新密钥”按钮,一对新生成的加密密钥将以 XML 格式显示。
点击“切换 XML/文本显示”按钮,密钥将以“文本”形式显示。如果您想使用这对加密密钥,可以将其复制并保存在某处。
现在,让我们切换到第二个选项卡“加密文本并将结果转换为 Base64 字符串”。在“原始文本”中键入一些文本,然后点击“加密”按钮;您将在“加密文本”的“TextBox
”中看到加密内容的“Base64”字符串。点击“解密”按钮,加密内容将被解密到“解密文本”的“TextBox
”。
要查看二进制数据是如何被加密/解密的,您可以选择第三个选项卡“加密二进制数据”。
点击“浏览文件进行加密”按钮,将显示一个“OpenFileDialog
”框,让您选择一个要加密的文件。选择文件后,应用程序将加密文件,并将加密内容保存到另一个文件中,使用相同的文件名,并添加“.encripted”作为文件扩展名。
同样,您可以点击“浏览加密文件进行解密”按钮来解密加密文件。解密后,您可以打开文件查看文件内容是否与原始文件相同。在我测试此应用程序时,我加密了以下图像文件:
点击菜单项“帮助”,然后点击“关于”,将以 Silverlight 风格显示帮助窗口。
关注点
- 本文介绍了一个基于 Microsoft 的“
System.Security.Cryptography
”命名空间的加密类库。使用 Microsoft 的函数使得类库的实现非常简单,但同时,类库的功能和性能也受到 Microsoft 所提供功能和性能的限制。 - 本文中的示例应用程序演示了如何加密/解密文本和二进制数据。类库本身实际上并不区分数据是文本还是二进制。要使用类库,所有数据都需要转换为“字节数组”。
- 在大多数应用程序场景下,类库的性能应该足够了。但我确实注意到,该类库在处理大型二进制文件时存在困难,尤其是在解密数据时。如果按照原样使用该类库,文件中的所有数据将被读入内存才能处理。这将导致巨大的内存消耗。要解决这个问题,您可以将文件分成多个块,然后逐块处理数据。我将把改进类库以提高性能的任务留给感兴趣的读者。如果您在遇到性能问题时,Mathew John Schlabaugh 的文章“Public Key RSA Encryption in C# .NET”应该是一个很好的参考。
- 要能够解密某些加密数据,您需要拥有加密密钥。如果您丢失了加密密钥,没有人能够解密它。您需要找到一种方法来保护您的加密密钥。在本文中,加密密钥被编译到示例应用程序中,这可能不是保护密钥的最佳方式。
- 本文最初的目的是一篇短文,但 WPF 示例应用程序使其不必要地冗长。如果您对开发 WPF 应用程序的技术感兴趣,这可能会有所帮助。但如果您对此类技术不感兴趣,我希望这篇冗长的 WPF 示例应用程序不会打扰到您。
历史
这是本文的第一个修订版。