本实例实现对计算机中的一些机密的文本文件进行加密与解密操作。运行程序,单击“选择文件”按钮,选择要进行加密或解密的文本文件(.txt格式的文件),单击“加密”或“解密”按钮,即可完成对文本文件的加密或解密操作。
技术要点
实现本实例功能主要用到了System.Security.Cryptography命名空间下的RijndaelManaged类的CreateDecryptor方法、CreateEncryptor方法、CryptoStream类的Write方法、FlushFinalBlock方法、Close方法、System.IO命名空间下的FileStream类、StreamReader类的ReadToEnd方法、StreamWriter类的Write方法、Flush方法和BinaryReader类的ReadBytes方法。System.Security.Cryptography命名空间在本实例517中已经做过详细介绍,这里不再赘述,下面对本实例中用到的其他知识进行详细讲解。
(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 rgbKey:用于对称算法的机密密钥。
l rgbIV:用于对称算法的IV。
l 返回值:对称的Rijndael加密器对象。
(4)CryptoStream类。此类定义将数据流链接到加密转换的流。其语法格式如下:
public CryptoStream (Streamstream,ICryptoTransform transform,CryptoStreamMode mode)
参数说明如下。
l stream:对其执行加密转换的流。
l transform:要对流执行的加密转换。
l mode:CryptoStreamMode值之一。CryptoStreamMode值及明说如表17.1所示。
表17.1 CryptoStreamMode值及说明
值 | 说 明 |
Read | 对加密流的读访问 |
Write | 对加密流的写访问 |
(5)CryptoStream类的Write方法。此方法将一个字节序列写入当前CryptoStream类中,并从当前位置写入指定的字节数。其语法格式如下:
public override void Write (byte[] buffer,intoffset,int count)
参数说明如下。
l buffer:字节数组。此方法将count个字节从buffer复制到当前流。
l offset:buffer中的字节偏移量,从此偏移量开始将字节复制到当前流。
l count:要写入当前流的字节数。
(6)FlushFinalBlock方法。此方法用缓冲区的当前状态更新基础数据源或储存库,随后清除缓冲区。其语法格式如下:
public voidFlushFinalBlock ()
(7)Close方法。关闭当前流并释放与之关联的所有资源(如套接字和文件句柄)。其语法格式如下:
public virtual voidClose ()
(8)System.IO命名空间。System.IO命名空间包含允许读写文件和数据流的类型以及提供基本文件和目录支持的类型。
(9)FileStream类。此类公开以文件为主的Stream,既支持同步读写操作,也支持异步读写操作。其语法格式如下:
public FileStream(string path,FileMode mode,FileAccess access)
参数说明如下。
l path:当前FileStream类对象封装文件的相对路径或绝对路径。
l mode:FileMode常数,确定如何打开或创建文件。FileMode常数的值及说明如表17.2所示。
l access:FileAccess常数,确定FileStream对象访问文件的方式。这将获取FileStream对象的CanRead和CanWrite属性。如果path指定磁盘文件,则CanSeek为True。FileAccess常数的值及说明如表17.3所示。
表17.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打开的文件中进行读取将导致异常 |
表17.3 FileAccess常数的值及说明
常 数 值 | 说 明 |
Read | 对文件的读访问。可从文件中读取数据。同Write组合即构成读写访问权 |
ReadWrite | 对文件的读访问和写访问。可从文件读取数据和将数据写入文件 |
Write | 文件的写访问。可将数据写入文件。同Read组合即构成读/写访问权 |
(10)StreamReader类。此类实现一个TextReader,使其以一种特定的编码从字节流中读取字符。其语法格式如下:
public StreamReader(Stream stream)
参数说明如下。
l stream:要读取的流。
(11)ReadToEnd方法。此方法从流的当前位置到末尾读取流。其语法格式如下:
public override stringReadToEnd ()
l 返回值:字符串形式的流的其余部分(从当前位置到末尾)。如果当前位置位于流的末尾,则返回空字符串(“”)。
(12)StreamWriter类。此类实现一个TextWriter,使其以一种特定的编码向流中写入字符。其语法格式如下:
public StreamWriter(Stream stream)
参数说明如下。
l stream:要写入的流。
(13)StreamWriter类的Write方法。此方法将字符写入流。其语法格式如下:
public override voidWrite (char value)
参数说明如下。
l value:要写入文本流中的字符。
(14)Flush方法。此方法清理当前编写器的所有缓冲区,并使所有缓冲数据写入基础流。其语法格式如下:
public override voidFlush ()
(15)BinaryReader类。此类用特定的编码将基元数据类型读作二进制值。其语法格式如下:
public BinaryReader(Stream input)
参数说明如下。
l input:流。
(16)ReadBytes方法。此方法从当前流中将count个字节读入字节数组,并使当前位置提升count个字节。其语法格式如下:
public virtual byte[]ReadBytes (int count)
参数说明如下。
l count:要读取的字节数。
l 返回值:包含从基础流中读取的数据的字节数组。如果到达了流的末尾,则该字节数组可能小于所请求的字节数。
实现过程
(1)新建一个Windows应用程序,将其命名为“文本文件加密与解密”,默认窗体为Form1。
(2)在Form1窗体中,主要添加1个TextBox控件,用来显示文件路径;添加1个OpenFileDialog控件,用来选择要加密或解密的文件;添加3个Button控件,用来执行加密、解密和选择文件操作。
(3)主要程序代码。加密文本文件的实现代码如下:
private void button5_Click(objectsender, 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 = newCryptoStream(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(); //关闭CryptoStream对象
fsIn.Close(); //关闭FileStream对象
fsOut.Close(); //关闭FileStream对象
if (MessageBox.Show(strOutName, "提示:加密成功!加密后的文件名及路径为:\n"+"是否删除源文件", Message BoxButtons. YesNo) ==DialogResult.Yes)
{
File.Delete(strPath); //删除指定文件
textBox1.Text = ""; //清空文本框
}else
{ textBox1.Text = ""; }
}
catch (Exception ee) //如果出现异常
{
MessageBox.Show(ee.Message); //输出异常信息
}
}
}
解密文本文件的实现代码如下:
private void button4_Click(objectsender, 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(); //创建RijndaelManaged实例
FileStream fsOut = File.Open(strPath, FileMode.Open, FileAccess.Read);//实例化FileStream对象
CryptoStream csDecrypt = newCryptoStream(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 = "";
}
}
}