在企业的计算机中,往往存有大量的机密文件,这些机密文件与企业的发展有紧密联系,如果这些机密文件保管不善,将会使企业遭受巨大的损失。本节通过几个典型实例详细介绍一下C#中的加密与解密技术。
实例463 数据加密技术
实例说明
本实例实现对文件的机密数据进行加密的功能。运行程序,在文本框中输入要加密的数据,单击【加密】按钮,对数据进行加密,并将加密后的数据显示在“加密后的字符”文本框中。实例运行结果如图16.1所示。
技术要点
实现本实例功能主要用到了System.Security.Cryptography命名空间下的MD5Crypto- ServiceProvider类的ComputeHash( )方法、System.Text命名空间下的ASCIIEncoding类的ASCII属性、GetBytes( )方法和GetString( )方法。下面分别进行介绍。
(1)System.Security.Cryptography命名空间
System.Security.Cryptography 命名空间提供加密服务(包括安全的数据编码和解码)以及许多其他操作,例如散列法、随机数字生成和消息身份验证。
(2)MD5CryptoServiceProvider类
此类使用加密服务提供程序(CSP)提供的实现,计算输入数据的MD5哈希值。
语法格式为:
public sealed class MD5CryptoServiceProvider : MD5
注意:MD5CryptoServiceProvider类的哈希大小为128位。
(3)ComputeHash( )方法
此方法计算指定字节数组的哈希值。
语法格式为:
public byte[] ComputeHash (byte[] buffer)
参数说明如下。
l buffer:要计算其哈希代码的字节数组。
l 返回值:计算所得的哈希代码。
(4)System.Text命名空间
表示 ASCII、Unicode、UTF-7和UTF-8字符编码的类;是用于将字符块转换为字节块和将字节块转换为字符块的抽象基类。
(5)ASCIIEncoding类
此类表示Unicode字符的ASCII字符编码。
语法格式为:
public class ASCIIEncoding : Encoding
(6)ASCII属性
此属性获取 ASCII(7位)字符集的编码。
语法格式为:
public static Encoding ASCII { get; }
l 属性值:ASCII(7 位)字符集的Encoding。
(7)GetBytes( )方法
此方法将指定的String中的所有字符编码为一个字节序列。
语法格式为:
public virtual byte[] GetBytes (string s)
参数说明如下。
l s:包含要编码的字符的String。
(8)GetString( )方法
此方法将指定字节数组中的所有字节解码为一个字符串。
语法格式为:
public virtual string GetString (byte[] bytes)
参数说明如下。
l bytes:包含要解码的字节序列的字节数组。
l 返回值:包含指定字节序列解码结果的String。
注意:使用MD5CryptoServiceProvider类必须引用System.Security.Cryptography命名空间。
实现过程
(1)新建一个Windows应用程序,将其命名为Ex16_01,默认窗体为Form1。
(2)在Form1窗体中,主要添加两个TextBox控件,用来输入和显示字符串;添加一个Button控件,用来执行加密操作。
(3)主要程序代码。
private void button1_Click(object sender, EventArgs e)
{
if (textBox1.Text == "")
{ MessageBox.Show("请输入加密数据"); return; }
MD5CryptoServiceProvider M5 = new MD5CryptoServiceProvider();
textBox2.Text = ASCIIEncoding.ASCII.GetString(M5.ComputeHash(ASCIIEncoding.ASCII.GetBytes(textBox1.Text)));
}
举一反三
根据本实例,读者可以实现以下功能。
对机密的文件夹进行加密与解密。
在数据传输中使用,以便保证传输信息不被泄露。
实例464 文本文件加密与解密
实例说明
本实例实现对计算机中的一些非常机密的文本文件进行加密与解密操作。运行程序,单击【选择文件】按钮,选择要进行加密或解密的文本文件(.txt格式的文件),单击【加密】或【解密】按钮,即可完成对文本文件的加密或解密操作。实例运行结果如图16.2所示。
技术要点
实现本实例功能主要用到了System.Security.Cryptography命名空间下的RijndaelManaged类的CreateDecryptor( )方法、CreateEncryptor( )方法、CryptoStream类的Write( )方法、FlushFinalBlock( )方法、Close( )方法、System.IO命名空间下的FileStream类、StreamReader类的ReadToEnd( )方法、StreamWriter类的Write( )方法、Flush( )方法和BinaryReader类的ReadBytes( )方法。System.Security.Cryptography命名空间在第16章实例463中已经做过详细介绍,这里不再详细描述,下面对本实例中用到的其他知识进行详细介绍。
(1)RijndaelManaged类
此类表示Rijndael对称加密算法的所有实现必须从其继承的基类中获得。
语法格式为:
public abstract class Rijndael SymmetricAlgorithm
注意:此算法支持128、192或256位的密钥长度。
(2)CreateDecryptor( )方法
此方法使用指定的Key和初始化向量(IV)创建对称的Rijndael解密器对象。
语法格式为:
public override IcryptoTransform CreateDecryptor (byte[] rgbKey,byte[] rgbIV)
参数说明如下。
l rgbKey:用于对称算法的机密密钥。
l rgbIV:用于对称算法的IV。
l 返回值:对称的Rijndael解密器对象。
(3)CreateEncryptor( )方法
此方法使用指定的Key和初始化向量(IV)创建对称的Rijndael加密器对象。
语法格式为:
public override ICryptoTransform CreateEncryptor (byte[] rgbKey,byte[] rgbIV)
参数说明如下。
l rgbKe:用于对称算法的机密密钥。
l rgbIV:用于对称算法的IV。
l 返回值:对称的Rijndael加密器对象。
(4)CryptoStream类
此类定义将数据流链接到加密转换的流。
语法格式为:
public CryptoStream (Stream stream,ICryptoTransform transform,CryptoStreamMode mode)
参数说明如下。
l stream:对其执行加密转换的流。
l Transform:要对流执行的加密转换。
l Mode:CryptoStreamMode值之一。CryptoStreamMode值及明说如表16.1所示。
表16.1 CryptoStreamMode值及说明
值
|
说 明
|
Read
|
对加密流的读访问
|
Write
|
对加密流的写访问
|
(5)CryptoStream类的Write( )方法
此方法将一个字节序列写入当前CryptoStream类中,并从当前位置写入指定的字节数。
语法格式为:
public override void Write (byte[] buffer,int offset,int count)
参数说明如下。
l buffer:字节数组。此方法将count个字节从buffer复制到当前流。
l offset:buffer中的字节偏移量,从此偏移量开始将字节复制到当前流。
l count:要写入当前流的字节数。
(6)FlushFinalBlock( )方法
此方法用缓冲区的当前状态更新基础数据源或储存库,随后清除缓冲区。
语法格式为:
public void FlushFinalBlock ()
(7)Close( )方法
关闭当前流并释放与之关联的所有资源(如套接字和文件句柄)。
语法格式为:
public virtual void Close ()
(8)System.IO命名空间
System.IO命名空间包含允许读写文件和数据流的类型以及提供基本文件和目录支持的类型。
(9)FileStream类
此类公开以文件为主的Stream,既支持同步读写操作,也支持异步读写操作。
语法格式为:
public FileStream (string path,FileModemode,FileAccessaccess)
参数说明如下。
l path:当前FileStream类对象封装文件的相对路径或绝对路径。
l Mode:FileMode常数,确定如何打开或创建文件。FileMode常数的值及说明如表16.2所示。
l Access:FileAccess常数,确定FileStream对象访问文件的方式。这将获取FileStream对象的CanRead和CanWrite属性。如果path指定磁盘文件,则CanSeek为True。FileAccess常数的值及说明如表16.3所示。
表16.2 FileMode常数的值及说明
常 数 值
|
说 明
|
Append
|
打开现有文件并查找到文件尾,或创建新文件。FileMode.Append只能同FileAccess.Write一起使用。任何读尝试都将失败并引发ArgumentException
|
Create
|
指定操作系统应创建新文件。如果文件已存在,它将被改写。这要求FileIOPermissionAccess.Write和System.IO.FileMode.Create等效于这样的请求:如果文件不存在,则使用CreateNew;否则使用Truncate
|
CreateNew
|
指定操作系统应创建新文件。此操作需要FileIOPermissionAccess.Write。如果文件已存在,则将引发IOException
|
Open
|
指定操作系统应打开现有文件。打开文件的能力取决于FileAccess所指定的值。如果该文件不存在,则引发System.IO.FileNotFoundException
|
OpenOrCreate
|
指定操作系统应打开文件(如果文件存在);否则,应创建新文件。如果用FileAccess.Read打开文件,则需要FileIOPermissionAccess.Read。如果文件访问为FileAccess.Write或FileAccess.ReadWrite,则需要FileIOPermissionAccess.Write。如果文件访问为FileAccess.Append,则需要FileIOPermissionAccess.Append
|
Truncate
|
指定操作系统应打开现有文件。文件一旦打开,就将被截断为零字节大小。此操作需要FileIOPermissionAccessWrite。试图从使用Truncate打开的文件中进行读取将导致异常
|
表16.3 FileAccess常数的值及说明
常 数 值
|
说 明
|
Read
|
对文件的读访问。可从文件中读取数据。同Write组合即构成读写访问权
|
ReadWrite
|
对文件的读访问和写访问。可从文件读取数据和将数据写入文件
|
Write
|
文件的写访问。可将数据写入文件。同Read组合即构成读/写访问权
|
(10)StreamReader类
此类实现一个TextReader,使其以一种特定的编码从字节流中读取字符。
语法格式为:
public StreamReader (Stream stream)
参数说明如下。
l stream:要读取的流。
(11)ReadToEnd( )方法
此方法从流的当前位置到末尾读取流。
public override string ReadToEnd ()
l 返回值:字符串形式的流的其余部分(从当前位置到末尾)。如果当前位置位于流的末尾,则返回空字符串(“”)。
(12)StreamWriter类
此类实现一个TextWriter,使其以一种特定的编码向流中写入字符。
语法格式为:
public StreamWriter (Stream stream)
参数说明如下。
l stream:要写入的流。
(13)StreamWriter类的Write( )方法
此方法将字符写入流。
语法格式为:
public override void Write (char value)
参数说明如下。
l value:要写入文本流中的字符。
(14)Flush( )方法
此方法清理当前编写器的所有缓冲区,并使所有缓冲数据写入基础流。
语法格式为:
public override void Flush ()
(15)BinaryReader类
此类用特定的编码将基元数据类型读作二进制值。
语法格式为:
public BinaryReader (Stream input)
参数说明如下。
l Input:流。
(16)ReadBytes( )方法
此方法从当前流中将count个字节读入字节数组,并使当前位置提升count个字节。
语法格式为:
public virtual byte[] ReadBytes (int count)
参数说明如下。
l count:要读取的字节数。
l 返回值:包含从基础流中读取的数据的字节数组。如果到达了流的末尾,则该字节数组可能小于所请求的字节数。
实现过程
(1)新建一个Windows应用程序,将其命名为Ex16_02,默认窗体为Form1。
(2)在Form1窗体中,主要添加一个TextBox控件,用来显示文件路径;添加一个OpenFileDialog控件,用来选择要加密或解密的文件;添加3个Button控件,用来执行加密、解密和选择文件操作。
(3)主要程序代码。
加密文本文件的实现代码如下:
private void button5_Click(object sender, EventArgs e)
{
if (textBox1.Text == "")
{ MessageBox.Show("请选择要加密的文件"); }
else
{
try{
string strPath = textBox1.Text;//加密文件的路径
int intLent=strPath.LastIndexOf("//")+1;
int intLong = strPath.Length;
//要加密的文件名称
string strName = strPath.Substring(intLent,intLong-intLent);
int intTxt = strName.LastIndexOf(".");
int intTextLeng = strName.Length;
//取出文件的扩展名
string strTxt = strName.Substring(intTxt,intTextLeng-intTxt);
strName = strName.Substring(0,intTxt);
//加密后的文件名及路径
string strOutName = strPath.Substring(0, strPath.LastIndexOf("//") + 1) + strName + "Out" + strTxt;
//加密文件密钥
byte[] key = { 24, 55, 102, 24, 98, 26, 67, 29, 84, 19, 37, 118, 104, 85, 121, 27, 93, 86, 24, 55, 102, 24, 98, 26, 67, 29, 9, 2, 49, 69, 73, 92 };
byte[] IV ={ 22, 56, 82, 77, 84, 31, 74, 24, 55, 102, 24, 98, 26, 67, 29, 99 };
RijndaelManaged myRijndael = new RijndaelManaged();
FileStream fsOut = File.Open(strOutName, FileMode.Create, FileAccess.Write);
FileStream fsIn = File.Open(strPath, FileMode.Open, FileAccess.Read);
//写入加密文本文件
CryptoStream csDecrypt = new CryptoStream(fsOut, myRijndael.CreateEncryptor(key, IV), CryptoStreamMode.Write);
//读加密文本
BinaryReader br = new BinaryReader(fsIn);
csDecrypt.Write(br.ReadBytes((int)fsIn.Length), 0, (int)fsIn.Length);
csDecrypt.FlushFinalBlock();
csDecrypt.Close();
fsIn.Close();
fsOut.Close();
if (MessageBox.Show(strOutName, "提示:加密成功!加密后的文件名及路径为:/n"+"是否删除源文件", MessageBoxButtons.YesNo) == DialogResult.Yes)
{
File.Delete(strPath);
textBox1.Text = "";
}else
{ textBox1.Text = ""; }
}
catch (Exception ee)
{
MessageBox.Show(ee.Message);
}
}
}
解密文本文件的实现代码如下:
private void button4_Click(object sender, EventArgs e)
{
if (textBox1.Text == "")
{
MessageBox.Show("请选择要解密的文件路径");
}
else
{
string strPath = textBox1.Text;//加密文件的路径
int intLent = strPath.LastIndexOf("//") + 1;
int intLong = strPath.Length;
//要加密的文件名称
string strName = strPath.Substring(intLent, intLong - intLent);
int intTxt = strName.LastIndexOf(".");
int intTextLeng = strName.Length;
strName = strName.Substring(0, intTxt);
if (strName.LastIndexOf("Out") != -1)
{
strName = strName.Substring(0, strName.LastIndexOf("Out"));
}
else
{
strName = strName + "In";
}
//加密后的文件名及路径
string strInName = strPath.Substring(0, strPath.LastIndexOf("//") + 1) + strName + ".txt"; //解密文件密钥
byte[] key = { 24, 55, 102, 24, 98, 26, 67, 29, 84, 19, 37, 118, 104, 85, 121, 27, 93, 86, 24, 55, 102, 24, 98, 26, 67, 29, 9, 2, 49, 69, 73, 92 };
byte[] IV ={ 22, 56, 82, 77, 84, 31, 74, 24, 55, 102, 24, 98, 26, 67, 29, 99 };
RijndaelManaged myRijndael = new RijndaelManaged();
FileStream fsOut = File.Open(strPath, FileMode.Open, FileAccess.Read);
CryptoStream csDecrypt = new CryptoStream(fsOut, myRijndael.CreateDecryptor(key, IV), CryptoStreamMode.Read);
StreamReader sr = new StreamReader(csDecrypt);//把文件读出来
StreamWriter sw = new StreamWriter(strInName);//解密后文件写入一个新的文件
sw.Write(sr.ReadToEnd());
sw.Flush();
sw.Close();
sr.Close();
fsOut.Close();
if (MessageBox.Show(strInName, "提示:解密成功!解密后的文件名及路径为:" + "是否删除源文件", MessageBoxButtons.YesNo) == DialogResult.Yes)
{
File.Delete(strPath);
textBox1.Text = "";
}
else
{
textBox1.Text = "";
}
}
}
举一反三
根据本实例,读者可以实现以下功能。
对重要公文进行加密。
对网络中传输的文件进行加密与解密。
实例465 利用图片加密文件
实例说明
本实例中,利用图片生成密钥,然后对文本文件进行加密和解密操作。运行程序,单击【打开图片】按钮,选择密钥图片,然后单击【打开文本文件】按钮,选择要加密或解密的文件,单击【加密】或【解密】按钮完成文本文件的加密或解密操作。解密时的密钥图片要与加密时的密钥图片相同,否则解密不能成功。实例运行结果如图16.3所示。
技术要点
实现本实例功能主要用到了System.Security.Cryptography命名空间下的RC2CryptoServiceProvider类的CreateDecryptor( )方法、CreateEncryptor( )方法、CryptoStream类的Write( )方法、FlushFinalBlock( )方法、Close( )方法、System.IO命名空间下的FileStream类、BinaryReader类的ReadBytes( )方法、BinaryWriter类的Write( )方法、File类的Delete( )方法和Copy( )方法。以上大部分知识在第16章实例464中已经做过详细介绍,这里不再详细讲解。下面主要对RC2CryptoServiceProvider类、BinaryWriter类的Write( )方法、File类的Delete( )方法和Copy( )方法进行介绍。
(1)RC2CryptoServiceProvider类
此类定义访问RC2算法的加密服务提供程序(CSP)实现的包装对象。
(2)BinaryWriter类
此类以二进制形式将基元类型写入流,并支持用特定的编码写入字符串。
语法格式为:
public BinaryWriter (Streamoutput)
参数说明如下。
l output:输出流。
(3)BinaryWriter类的Write( )方法
此方法将一个无符号字节写入当前流,并将流的位置提升1个字节。
语法格式为:
public virtual void Write (byte value)
参数说明如下。
l value:要写入的无符号字节。
(4)File类
此类提供用于创建、复制、删除、移动和打开文件的静态方法,并协助创建FileStream对象。
(5)Delete( )方法
此方法删除指定的文件。如果指定的文件不存在,则引发异常。
语法格式为:
public static void Delete (string path)
参数说明如下。
l path:要删除的文件的名称。
(6)Copy( )方法
此方法将现有文件复制到新文件,不允许改写同名的文件。
语法格式为:
public static void Copy (string sourceFileName,string destFileName)
参数说明如下。
l sourceFileName:要复制的文件。
l destFileName:目标文件的名称,不能是一个目录或现有文件。
实现过程
(1)新建一个Windows应用程序,将其命名为Ex16_03,默认窗体为Form1。
(2)在Form1窗体中,主要添加一个TextBox控件,用来显示加密或解密文件的路径;添加一个OpenFileDialog控件,用来选择要加密或解密的文件和打开密钥的图片;添加4个Button控件,用来执行加密、解密、打开文件和打开图片操作;添加一个PictureBox控件,用于显示密钥图片。
(3)主要程序代码。
利用图片加密文本文件的实现代码如下:
private void button3_Click(object sender, EventArgs e)
{
try
{
if (pictureBox1.ImageLocation==null)
{ MessageBox.Show("请选择一幅图片用于加密"); return; }
if (textBox1.Text == "")
{ MessageBox.Show("请选择加密文件路径"); return; }
//图片流
FileStream fsPic = new FileStream(pictureBox1.ImageLocation, FileMode.Open, FileAccess.Read);
//加密文件流
FileStream fsText = new FileStream(textBox1.Text, FileMode.Open, FileAccess.Read);
//初始化Key IV
byte[] bykey = new byte[16];
byte[] byIv = new byte[8];
fsPic.Read(bykey, 0, 16);
fsPic.Read(byIv, 0, 8);
//临时加密文件
string strPath = textBox1.Text;//加密文件的路径
int intLent = strPath.LastIndexOf("//") + 1;
int intLong = strPath.Length;
string strName = strPath.Substring(intLent, intLong - intLent);//要加密的文件名称
string strLinPath = "C://" + strName;//临时加密文件路径
FileStream fsOut = File.Open(strLinPath, FileMode.Create, FileAccess.Write);
//开始加密
RC2CryptoServiceProvider desc = new RC2CryptoServiceProvider();//desc进行加密
BinaryReader br = new BinaryReader(fsText);//从要加密的文件中读出文件内容
CryptoStream cs = new CryptoStream(fsOut, desc.CreateEncryptor(bykey, byIv), CryptoStreamMode.Write);//写入临时加密文件
cs.Write(br.ReadBytes((int)fsText.Length), 0, (int)fsText.Length);//写入加密流
cs.FlushFinalBlock();
cs.Flush();
cs.Close();
fsPic.Close();
fsText.Close();
fsOut.Close();
File.Delete(textBox1.Text.TrimEnd());//删除原文件
File.Copy(strLinPath, textBox1.Text);//复制加密文件
File.Delete(strLinPath);//删除临时文件
MessageBox.Show("加密成功");
pictureBox1.ImageLocation = null;
textBox1.Text = "";
}
catch (Exception ee)
{
MessageBox.Show(ee.Message);
}
}
利用图片解密文本文件的实现代码如下:
private void button4_Click(object sender, EventArgs e)
{
try
{
//图片流
FileStream fsPic = new FileStream(pictureBox1.ImageLocation, FileMode.Open, FileAccess.Read);
//解密文件流
FileStream fsOut = File.Open(textBox1.Text, FileMode.Open, FileAccess.Read);
//初始化Key IV
byte[] bykey = new byte[16];
byte[] byIv = new byte[8];
fsPic.Read(bykey, 0, 16);
fsPic.Read(byIv, 0, 8);
//临时解密文件
string strPath = textBox1.Text;//加密文件的路径
int intLent = strPath.LastIndexOf("//") + 1;
int intLong = strPath.Length;
string strName = strPath.Substring(intLent, intLong - intLent);//要加密的文件名称
string strLinPath = "C://" + strName;//临时解密文件路径
FileStream fs = new FileStream(strLinPath, FileMode.Create, FileAccess.Write);
//开始解密
RC2CryptoServiceProvider desc = new RC2CryptoServiceProvider();//desc进行解密
CryptoStream csDecrypt = new CryptoStream(fsOut, desc.CreateDecryptor(bykey, byIv), CryptoStreamMode.Read);//读出加密文件
BinaryReader sr = new BinaryReader(csDecrypt);//从要加密流中读出文件内容
BinaryWriter sw = new BinaryWriter(fs);//写入解密流
sw.Write(sr.ReadBytes(Convert.ToInt32(fsOut.Length)));//
sw.Flush();
sw.Close();
sr.Close();
fs.Close();
fsOut.Close();
fsPic.Close();
csDecrypt.Flush();
File.Delete(textBox1.Text.TrimEnd());//删除原文件
File.Copy(strLinPath, textBox1.Text);//复制加密文件
File.Delete(strLinPath);//删除临时文件
MessageBox.Show("解密成功");
pictureBox1.ImageLocation = null;
textBox1.Text = "";
}
catch (Exception ee)
{
MessageBox.Show(ee.Message);
}
}
举一反三
根据本实例,读者可以实现以下功能。
利用各种图片加密文件。
使用图片批量解密文件。