ASP.NET 加密 Part.3(非对称算法)
非对称加密算法和对称加密算法有些类似,只有少许不同。主要在于对密钥的管理上。对称加密算法只有一个密钥,而非对称加密算法有两个密钥:一个用来加密数据(公钥),一个用来解密数据(私钥)。公钥可以传递给任何想加密数据的人,而私钥只应由解密信息的人所拥有。
.NET Framework 只为实际的数据加密提供了 1 个非对称加密算法(RSA,记住,DSA 只用于数字签名),因此,创建这个工具并不需要包含一个用来选择加密算法的方法:
public static class AsymmetricEncryptionUtility
{
public static string GenerateKey(string targetFile){}
private static void ReadKey(RSACryptoServiceProvider algorithm , string keyFile){}
public static byte[] EncryptData(string data,string publicKey){}
public static string DecryptData(byte[] data,string keyFile){}
}
GenerateKey() 创建 RSA 加密算法的实例,用来生成密钥。它只在通过 DPAPI 保护的文件中存储私钥,并使用该算法的 ToXmlString() 以及 XML 字符串的形式返回公钥。这是比较实际的,私钥通常被应用程序保存为秘密信息,公钥可以被共享以便加密信息,然后该加密信息被应用程序使用其秘密的私钥解密。
这个函数的调用者需要将公钥存储在某个地方,这对加密信息来讲是必要的:
public static string GenerateKey(string targetFile)
{
RSACryptoServiceProvider algorithm = new RSACryptoServiceProvider();
// 创建并返回包含当前 RSA 对象的密钥的 XML 字符串
// true 表示同时包含 RSA 公钥和私钥;false 表示仅包含公钥
string completeKey = algorithm.ToXmlString(true);
byte[] keyBytes = Encoding.UTF8.GetBytes(completeKey);
keyBytes = ProtectedData.Protect(keyBytes, null, DataProtectionScope.LocalMachine);
using (FileStream fs = new FileStream(targetFile, FileMode.Create))
{
fs.Write(keyBytes, 0, keyBytes.Length);
}
return algorithm.ToXmlString(false);
}
ReadKey() 只需要从文件中读取密钥,然后通过 FromXml() 初始化传递的加密算法实例即可:
private static void ReadKey(RSACryptoServiceProvider algorithm, string keyFile)
{
byte[] keyBytes;
using (FileStream fs = new FileStream(keyFile, FileMode.Open))
{
keyBytes = new byte[fs.Length];
fs.Read(keyBytes, 0, (int)fs.Length);
}
keyBytes = ProtectedData.Unprotect(keyBytes, null, DataProtectionScope.LocalMachine);
algorithm.FromXmlString(Encoding.UTF8.GetString(keyBytes));
}
EncryptData() 需要调用者传入 GenerateKey() 返回的公钥的 XML 字符串表示,又因为私钥不需要用来加密,使用 RSA 进行加密和解密的代码如下:
public static byte[] EncryptData(string data, string publicKey)
{
RSACryptoServiceProvider algorithm = new RSACryptoServiceProvider();
// 通过 XML 字符串中的密钥信息初始化 RSA 对象
algorithm.FromXmlString(publicKey);
return algorithm.Encrypt(Encoding.UTF8.GetBytes(data), true);
}
public static string DecryptData(byte[] data, string keyFile)
{
RSACryptoServiceProvider algorithm = new RSACryptoServiceProvider();
ReadKey(algorithm, keyFile);
byte[] clearData = algorithm.Decrypt(data, true);
return Encoding.UTF8.GetString(clearData);
}
测试页面的代码:
using System;
using System.IO;
using System.Text;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Security.Cryptography;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using EncryptionUtility;
public partial class _Asymmetric : System.Web.UI.Page
{
private string KeyFileName;
protected void Page_Load(object sender, EventArgs e)
{
KeyFileName = Server.MapPath("~/") + "asymmetric_key.config";
}
protected void GenerateKeyCommand_Click(object sender, EventArgs e)
{
try
{
PublicKeyText.Text = AsymmetricEncryptionUtility.GenerateKey(KeyFileName);
Response.Write("Key generated successfully!<br/>");
}
catch
{
Response.Write("Exception occured when encrypting key!");
}
}
protected void EncryptCommand_Click(object sender, EventArgs e)
{
// Check for encryption key
if (!File.Exists(KeyFileName))
{
Response.Write("Missing encryption key. Please generate key!");
}
try
{
byte[] data = AsymmetricEncryptionUtility.EncryptData(
ClearDataText.Text, PublicKeyText.Text);
EncryptedDataText.Text = Convert.ToBase64String(data);
}
catch
{
Response.Write("Unable to encrypt data!");
}
}
protected void DecryptCommand_Click(object sender, EventArgs e)
{
// Check for encryption key
if (!File.Exists(KeyFileName))
{
Response.Write("Missing encryption key. Please generate key!");
}
try
{
byte[] data = Convert.FromBase64String(EncryptedDataText.Text);
ClearDataText.Text = AsymmetricEncryptionUtility.DecryptData(data, KeyFileName);
}
catch
{
Response.Write("Unable to decrypt data!");
}
}
protected void ClearCommand_Click(object sender, EventArgs e)
{
ClearDataText.Text = "";
EncryptedDataText.Text = "";
}
}
测试页面设计代码:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Asymmetric.aspx.cs" Inherits="_Asymmetric" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div style="text-align: center">
<asp:Panel ID="MainPanel" runat="server" BorderStyle="Solid" BorderWidth="1px" Width="100%">
<table border="0" width="100%">
<tr>
<td style="text-align: left">
Step 1:<br />
Generate Encryption Key
</td>
<td style="text-align: left">
<asp:LinkButton ID="GenerateKeyCommand" runat="server" OnClick="GenerateKeyCommand_Click">Generate Key</asp:LinkButton><br />
<asp:TextBox ID="PublicKeyText" runat="server" Rows="5" TextMode="MultiLine" Columns="40"
Width="600px"></asp:TextBox>
</td>
</tr>
<tr>
<td style="text-align: left">
Step 2:<br />
Clear-text data
</td>
<td style="text-align: left">
<asp:TextBox ID="ClearDataText" runat="server" Rows="5" TextMode="MultiLine" Width="600px"
Columns="40"></asp:TextBox>
</td>
</tr>
<tr>
<td style="text-align: left">
Step 3:<br />
Encrypted data
</td>
<td style="text-align: left">
<asp:TextBox ID="EncryptedDataText" runat="server" Rows="5" TextMode="MultiLine"
Width="600px" Columns="40"></asp:TextBox>
</td>
</tr>
<tr>
<td>
</td>
<td>
<asp:LinkButton ID="EncryptCommand" runat="server" OnClick="EncryptCommand_Click">Encrypt</asp:LinkButton> <asp:LinkButton
ID="DecryptCommand" runat="server" OnClick="DecryptCommand_Click">Decrypt</asp:LinkButton> <asp:LinkButton
ID="ClearCommand" runat="server" OnClick="ClearCommand_Click">Clear</asp:LinkButton>
</td>
</tr>
</table>
</asp:Panel>
</div>
</form>
</body>
</html>
测试效果:
最后,这里是非对称算法工具的完整代码,并附上中文注释:
using System;
using System.Security.Cryptography;
using System.IO;
using System.Text;
namespace EncryptionUtility
{
public static class AsymmetricEncryptionUtility
{
/// <summary>
/// 生成加密密钥
/// </summary>
/// <param name="targetFile">保存私钥的文件路径</param>
/// <returns>返回公钥</returns>
public static string GenerateKey(string targetFile)
{
RSACryptoServiceProvider algorithm = new RSACryptoServiceProvider();
// RSACryptoServiceProvider.ToXmlString():
// 创建并返回包含当前 RSA 对象的密钥的 XML 字符串
// true 表示同时包含 RSA 公钥和私钥;false 表示仅包含公钥
string completeKey = algorithm.ToXmlString(true);
byte[] keyBytes = Encoding.UTF8.GetBytes(completeKey);
// 使用 DPAPI 保护私钥
keyBytes = ProtectedData.Protect(keyBytes, null, DataProtectionScope.LocalMachine);
using (FileStream fs = new FileStream(targetFile, FileMode.Create))
{
fs.Write(keyBytes, 0, keyBytes.Length);
}
return algorithm.ToXmlString(false);
}
/// <summary>
/// 读取私钥,重新配置 RSA 算法实例的密钥
/// </summary>
/// <param name="algorithm"></param>
/// <param name="keyFile"></param>
private static void ReadKey(RSACryptoServiceProvider algorithm, string keyFile)
{
byte[] keyBytes;
using (FileStream fs = new FileStream(keyFile, FileMode.Open))
{
keyBytes = new byte[fs.Length];
fs.Read(keyBytes, 0, (int)fs.Length);
}
// 解除 DPAPI 对私钥的保护
keyBytes = ProtectedData.Unprotect(keyBytes, null, DataProtectionScope.LocalMachine);
algorithm.FromXmlString(Encoding.UTF8.GetString(keyBytes));
}
/// <summary>
/// 加密数据
/// </summary>
/// <param name="data">欲加密的对象文本字符串</param>
/// <param name="publicKey">加密公钥</param>
/// <returns>加密后的数据</returns>
public static byte[] EncryptData(string data, string publicKey)
{
RSACryptoServiceProvider algorithm = new RSACryptoServiceProvider();
// 通过 XML 字符串中的密钥信息初始化 RSA 对象
algorithm.FromXmlString(publicKey);
return algorithm.Encrypt(Encoding.UTF8.GetBytes(data), true);
}
/// <summary>
/// 解密数据
/// </summary>
/// <param name="data">被加密的数据</param>
/// <param name="keyFile">私钥所在文件路径</param>
/// <returns>解密后的数据</returns>
public static string DecryptData(byte[] data, string keyFile)
{
RSACryptoServiceProvider algorithm = new RSACryptoServiceProvider();
ReadKey(algorithm, keyFile);
byte[] clearData = algorithm.Decrypt(data, true);
return Encoding.UTF8.GetString(clearData);
}
}
}