490 likes | 650 Views
C# 网络编程技术教程. 第十一章 密码术与网络编程. 学习目标. 了解密码术的基本概念。 熟悉目前常用的两种加密技术:对称加密、非对称加密。 熟悉数字信封和数字签名技术。 掌握 .NET 中散列算法、对称加密算法和非对称加密算法的编程方法。. 本章内容. 11.1 密码术概述 11.2 .NET 密码术编程基础 11.3 综 合 实 例. 11.1 密码术概述. 11.1 密码术概述
E N D
C#网络编程技术教程 第十一章 密码术与网络编程
学习目标 • 了解密码术的基本概念。 • 熟悉目前常用的两种加密技术:对称加密、非对称加密。 • 熟悉数字信封和数字签名技术。 • 掌握.NET中散列算法、对称加密算法和非对称加密算法的编程方法。
本章内容 11.1 密码术概述 11.2 .NET密码术编程基础 11.3 综 合 实 例
11.1 密码术概述 11.1 密码术概述 计算机网络的广泛应用对社会经济、科学研究、文化的发展产生了重大的影响,同时也不可避免地会带来一些新的社会、道德、政治和法律问题。目前,网络安全问题已经成为信息化社会的一个焦点问题。计算机网络为信息的获取、传输、处理、利用与共享提供了一个高效、快捷、安全的通信环境和传输通道。网络安全技术从根本上来说就是通过解决网络中存在的安全问题来达到确保信息在网络环境中的存储、处理和传输安全的目的。其中网络信息安全是网络安全技术中的主要研究内容之一,它主要包括两个方面的内容:信息存储安全和信息传输安全。 信息存储安全是指如何确保静态存储在联网计算机中的信息不会被未授权的网络用户非法使用,而信息传输安全是指如何保证信息在网络传输的过程中不被泄露与不被攻击。 保证网络中信息安全的主要技术是数据的加密与解密。在密码学中,将源信息称为明文;对明文进行某种变换后生成的隐藏了其真实内容的信息称为密文;将明文变换为密文的过程称为加密;将密文经过逆变换恢复成明文的过程称为解密。图11.1给出了一个加密与解密过程的示意图。 图11.1加密与解密示意图
11.1 密码术概述 其中加密和解密操作通常都是在一组密钥控制下进行的,因此加密过程可表示为式11.1,而解密过程可表示为式(11.2)。 c=Eke(m) (11.1) m=Dkd(c) (11.2) m代表明文,c代表密文,E是加密算法,D是解密算法,参数ke称为加密密钥,参数kd称为解密密钥。密文c是明文m使用加密密钥ke经过加密算法E计算后的结果。 通常,加密算法可以公开,而密钥只能由通信双方来管理。如果在网络传输过程中,传输的是经过加密的信息,那么即使有人窃取了这样的信息,由于其不知道解密密钥,也很难将密文还原成明文,从而可以保证信息在传输与存储中的安全。 对于同一种加密算法,密钥的位数越长,破译的困难就越大,安全性也就越好。但是密钥越长,进行加密和解密过程所需要的计算时间也将越长。因此,密钥的长度往往根据应用的实际需要来确定。
11.1 密码术概述 11.1.2 对称加密算法 根据加密和解密过程中所使用的密钥是否相同可以将加密算法分为对称加密算法和非对称加密算法。对称加密算法是指对信息的加密和解密都使用相同的密钥,因此也称为密钥密码算法,其工作原理如图11.2所示。 由于通信双方加密和解密使用同一个密钥,如果第三方获取该密钥就有可能造成失密。因此,如何确保密钥的安全便成了对称加密算法的主要问题。即使设计了一个很好的加密算法,但是密钥管理问题处理不好,那么这样的系统同样是不安全的。 在对称加密算法中,如果一个用户A要与N个其他的用户进行加密通信,每个用户都将对应一把密钥,即用户A需要维护N把密钥;如果网络中有N个用户之间进行加密通信时,则每个用户都将需要维护N(N−1)个密钥。 对称加密算法的加密强度与密钥长度对应。密钥越长,通过蛮力成功攻击的难度越大。当然,对称密钥越长,加解密时间也越长。对称加密运行速度比非对称加密要快得多,处理大量数据时最好使用对称加密。 目前,典型的对称加密算法主要包括DES算法、Triple-DES算法、RC2、RC4、RC5算法和Rijndael算法等。 图11.2对称加密算法工作原理
11.1 密码术概述 11.1.3 非对称加密算法 非对称加密算法对信息的加密与解密使用不同的密钥,用来加密的密钥是可以公开的,用来解密的密钥需要保密。因此又被称为公钥加密算法。其工作原理如图11.3所示。 在非对称加密算法中,加密的密钥通常是公开的,谁都可以使用,称其为公钥;解密的密钥只有解密人自己知道,称其为私钥。由于采用了两个密钥,大大简化了密钥的管理,网络中N个用户之间进行加密通信时,仅仅需要使用N对密钥就可以。但是与对称加密算法相比,非对称加密算法复杂、加密和解密的速度比较慢。 目前,典型的非对称加密算法主要包括RSA算法和DSA算法等。 图11.3非对称加密的工作原理
11.1 密码术概述 11.1.4 数字信封技术 对称加密算法运行效率高,但是密钥不适合在网络上传递;而非对称加密算法的密钥传递简单,但运行效率较低。数字信封技术则通过将对称加密算法和非对称加密算法结合起来,充分利用对称加密算法的高效性和非对称加密算法的灵活性,以保证信息在传输过程中的安全性。 在数字信封技术中,加密过程主要包括两个步骤,即首先使用对称加密算法对明文进行加密,然后利用非对称加密算法对对称密钥进行加密,并一起将加密结果发往接收方。同样,其解密过程也可以分为两个主要步骤:首先利用非对称解密算法对对称密钥密文进行解密获取对称密钥,然后用所获取的对称密钥对密文进行解密恢复成明文。整个工作原理如图11.4所示。 图11.4数字信封的工作原理
11.1 密码术概述 11.1.5 数字签名技术 数据加密虽然可以防止信息在传输过程中遇到的若干问题,但是没办法确定发送人的身份,也不能解决信息被篡改和假冒。于是,数字签名技术结合散列算法和非对称加密技术来进行篡改检测和解决相关的身份验证问题。这就像在现实生活中用亲笔签名来保证文件或资料的真实性一样。 数字签名将信息发送人的身份与信息传送结合起来,可以保证信息在传输过程中的完整性,并提供信息发送者的身份认证,以防止信息发送者抵赖行为的发生。其实现原理如下: 数字签名使用非对称加密算法,例如RSA,并且使用私钥加密,公钥解密的方法实现数字签名。同时考虑到非对称加密算法对加密信息块的长度有一定限制,在进行数字签名之前利用单向散列函数对要签名的信息进行散列计算以获取信息摘要,然后仅对信息摘要签名,如图11.5所示。 图11.5数字签名的工作示意图
11.2 .NET密码术编程基础 .NET框架的System.Security.Cryptography 命名空间提供了对各种加密服务的编程访问,包括安全的数据加密与解密、确保数据的完整性,以及处理数字签名和证书等。System.Security.Cryptography中的核心加密类分为三层,如图11.6所示。第一层是一组抽象类,用于表示加密算法的类型,主要包括散列算法类HashAlgorithm、对称加密类SymmetricAlgorithm和非对称加密类AsymmetricAlgorithm;第二层表示特定加密算法类,虽然是由相应的加密基类派生而来,但它们也是抽象类,例如System.Security.Cryptography.DES;第三层是一组具体的加密实现方案类,每种实现类都由算法类派生而来,例如System.Security.Cryptography.DESCryptoServiceProvider等。这样,每种特定算法类可以派生多个实现类,甚至允许第三方创建其他更好的实现方案类。 图11.6核心加密类的层次结构
11.2 .NET密码术编程基础 11.2.1 .NET中的散列算法及编程 散列函数是现代密码系统的基础。这些函数将任意长度的二进制字符串映射为固定长度的二进制字符串(称为散列值)。加密散列函数有这样一个属性:在计算时不可能将两个不同的输入通过散列算法获取相同的值。散列函数通常用于数字签名和保持数据完整性等。 在System.Security.Cryptography 命名空间中,所有的散列算法类都继承自HashAlgorithm抽象类。HashAlgorithm类提供了一些可以在所有的散列算法中使用的公共方法和属性,其主要方法和属性分别如表11.1和表11.2所示。 表11.1HashAlgorithm类的主要属性 表11.2HashAlgorithm类的主要方法
11.2 .NET密码术编程基础 11.2.1 .NET中的散列算法及编程 在HashAlgorithm类的基础上,.NET框架中实现了若干众所周知的、可靠的散列算法,具体算法名称和实现类如表11.3所示。 散列算法类的使用非常简单,只要创建一个散列算法类的实例,然后将字节数组数据传入该实例的ComputeHash方法,则该方法将返回计算所得的散列值。下面通过一个通用的散列类Hash来具体说明,其类实现如代码实例11.1所示。该类将根据指定的散列算法简单名称创建相应的散列算法实例,在此基础上将字符串数据传入Encrypt函数,便可以得到其散列值。该方法可以用于对口令的加密存储。由于ComputeHash方法的参数是字节数组,因此程序中引入了ASCIIEncoding. ASCII.GetBytes方法来将字符串转换为字节数组,同时引入Convert.ToBase64String方法将字节数组转换为字符串。
11.2 .NET密码术编程基础 11.2.1 .NET中的散列算法及编程 代码实例11.1 散列算法实例 using System; using System.Collections.Generic; using System.Text; using System.Security.Cryptography; namespace Ex_11_1 { //散列函数类 public class Hash { private HashAlgorithm mCryptoService=null; //构造函数重载 public Hash( ) { mCryptoService = new SHA1Managed( ); } public Hash(string serviceProviderName) { //根据算法名称创建散列对象 mCryptoService = (HashAlgorithm)CryptoConfig.CreateFromName(serviceProviderName.To Upper( )); } public virtual string Encrypt(string plainText) //加密 { byte[] cryptoByte = mCryptoService.ComputeHash(ASCIIEncoding.ASCII.GetBytes(plainText)); return Convert.ToBase64String(cryptoByte, 0, cryptoByte.Length); } }
11.2 .NET密码术编程基础 11.2.1 .NET中的散列算法及编程 代码实例11.1 散列算法实例 class Program //测试类 { static void Main(string[] args) { string m_ServiceProviderName="", m_Txt=""; bool rt = false; Hash m_Hash = null; //从命令行参数提取散列算法名和需要加密的字符串 if (args.Length < 2) { Console.WriteLine("Usage: Hash 散列算法名 加密字符串"); } else { m_ServiceProviderName = args[0].ToString( ); m_Txt = args[1].ToString( ); rt = true; } if (rt) //加密 { m_Hash = new Hash(m_ServiceProviderName); if (m_Hash != null) Console.WriteLine("加密数据:{0}", m_Hash.Encrypt(m_Txt)); else Console.WriteLine("参数错误,加密失败!"); } } } }
11.2 .NET密码术编程基础 11.2.2 .NET中的对称加密算法及编程 System.Security.Cryptography 命名空间支持DES、Triple-DES、RC2和Rijndael等对称加密算法。这些算法类都是从抽象基类SymmetricAlgorithm派生而来。SymmetricAlgorithm类提供了一些对称加密算法的公用方法和属性,其主要公用方法和属性分别如表11.4和表11.5所示。 表11.4 SymmetricAlgorithm类的主要属性 表11.5 SymmetricAlgorithm类的主要方法
11.2 .NET密码术编程基础 11.2.2 .NET中的对称加密算法及编程 在SymmetricAlgorithm类的基础上,System.Security.Cryptography命名空间给出了抽象算法实现类DES、RC2、TripleDES和Rijndael,并在此基础上给出了具体加密方案实现类。具体如表11.6所示。 表11.6对称加密算法实现类列表
11.2 .NET密码术编程基础 11.2.2 .NET中的对称加密算法及编程 上述对称加密算法实现类使用一种称为密码块链接(Cipher Block Chaining,CBC)的链接模式,该模式需要密钥(Key)和初始化向量(IV)才能执行数据的加密运算。若要解密密文数据,必须将Key属性和IV属性设置为用于加密的相同值。 当对称加密算法实现类被实例化时,将会自动生成强壮的密钥和初始化向量,并且设置默认填充模式(PKCS7)和默认密码模式(CBC)。在实际加密过程中,为了减少被破解的可能性,密钥必须有足够的随机性。实例化时生成的密钥都具有足够的随机性,使其难于记忆。因此,在实际加密过程中,往往采用比较易记的口令作为密码,并使用PasswordDriveBytes类生成强密钥,以提高抗破解能力。在解密时只要使用相同的方法生成密钥即可。另外,为了确保生成的是强密钥,还引入了salt随机值,使用它可以确保相同口令生成不同的密钥,这样便可以防止攻击者进行预运算。salt值和初始化向量IV不需要保密。 由于对称加密往往用于加密大量数据信息,因此采用了流式加密方法,以便支持对内存流和文件流等数据的加密和解密。托管对称加密类与称为CryptoStream的特殊流类一起使用。CryptoStream类使用以下参数进行初始化:一个托管流类,一个实现ICryptoTransform接口的类,以及一个CryptoStreamMode 枚举(用于描述允许对CryptoStream进行访问的类型)。可以使用从Stream类派生的任何类(包括FileStream、MemoryStream和NetworkStream)初始化CryptoStream类。使用这些类,可以对各种流对象执行对称加密。 解密用对称算法加密的数据类似于用对称算法加密数据的过程。将CryptoStream类与.NET Framework 提供的对称加密类一起使用,来解密从任何托管流对象中读取的数据。
11.2 .NET密码术编程基础 11.2.2 .NET中的对称加密算法及编程 利用对称加密算法的加解密过程如下所述。 1.加密步骤 基于对称加密算法的加密步骤如下。 (1)创建对称加密算法实例,例如: m_CryptoService = new RijndaelManaged( ); m_CryptoService.Mode = CipherMode.CBC;//设置链接模式 (2)设置初始化参数,包括密钥和初始化向量等。在实际使用过程中,初始化参数可以由加密算法实例自动产生,也可以由用户显式设置,但是初始向量和密钥的长度必须满足加密算法的需要。例如: m_CryptoService.Key = GetLegalKey( );//设置密钥 m_CryptoService.IV = GetLegalIV( );//设置初始向量 (3)使用CreateEncryptor方法创建加密实例。例如: ICryptoTransform cryptoTransform = m_CryptoService.CreateEncryptor( ); (4)创建加密流。由于对称加密往往用于加密大量数据信息,因此采用了流式加密方法,以便支持对内存流和文件流等数据的加密和解密。例如: //创建内存流 MemoryStream ms = new MemoryStream( ); //利用内存流创建加密流 CryptoStream cs = new CryptoStream(ms, cryptoTransform, CryptoStreamMode.Write);
11.2 .NET密码术编程基础 11.2.2 .NET中的对称加密算法及编程 (5)利用加密流进行数据加密,即直接通过流的写操作实现对数据的加密。例如: //通过加密流加密数据 cs.Write(plainByte, 0, plainByte.Length); cs.FlushFinalBlock( ); //返回密文 byte[] cryptoByte = ms.ToArray( ); return Convert.ToBase64String(cryptoByte, 0, cryptoByte.GetLength(0)); 2.解密步骤 基于对称加密算法的解密步骤如下: (1)创建对称加密算法实例。 m_CryptoService = new RijndaelManaged( ); m_CryptoService.Mode = CipherMode.CBC;//设置链接模式 (2)设置初始化参数,包括密钥和初始化向量等。 m_CryptoService.Key = GetLegalKey( );//设置密钥 m_CryptoService.IV = GetLegalIV( );//设置初始向量 (3)创建解密实例。例如: ICryptoTransform cryptoTransform = m_CryptoService.CreateDecryptor( );
11.2 .NET密码术编程基础 11.2.2 .NET中的对称加密算法及编程 (4)创建解密流。 //创建内存流 MemoryStream ms = new MemoryStream(cryptoByte, 0, cryptoByte.Length); //创建密文流 CryptoStream cs = new CryptoStream(ms, cryptoTransform, CryptoStreamMode.Read); (5)利用解密流进行数据解密。 //解密并返回明文 StreamReader sr = new StreamReader(cs); return sr.ReadToEnd( ); 根据上述加密和解密步骤,实现一个具有加密和解密功能的通用对称加密类SymCryptography。该类可以通过简单名称来指定加密算法实例,同时利用属性来设置相应的初始参数,并通过调用其中的加密函数Encrypt和解密函数Decrypt实现数据的加密解密操作。如代码实例11.2所示。
11.2 .NET密码术编程基础 11.2.2 .NET中的对称加密算法及编程 代码实例11.2 对称加密算法实例 using System; using System.IO; using System.Collections.Generic; using System.Text; using System.Security.Cryptography; namespace Ex_11_2 { public class SymCryptography { private string m_Key = string.Empty; private string m_Salt = string.Empty; private string m_IV = string.Empty; private SymmetricAlgorithm m_CryptoService; private byte[] GetLegalIV( ) //获取合法初始向量 { string m_iv = m_IV.Substring(0, m_IV.Length); int n = m_CryptoService.BlockSize / 8; if (m_iv.Length < n) { m_iv = m_iv.PadRight(n, '0'); } return ASCIIEncoding.ASCII.GetBytes(m_iv); }
11.2 .NET密码术编程基础 11.2.2 .NET中的对称加密算法及编程 代码实例11.2 对称加密算法实例 private byte[] GetLegalKey( )//获取合法密钥 { //获取合法的密码(长度) if (m_CryptoService.LegalKeySizes.Length > 0) { int keySize = m_Key.Length * 8; int minSize = m_CryptoService.LegalKeySizes[0].MinSize; int maxSize = m_CryptoService.LegalKeySizes[0].MaxSize; int skipSize = m_CryptoService.LegalKeySizes[0].SkipSize; if (keySize > maxSize) { m_Key = m_Key.Substring(0, maxSize / 8); } else if (keySize < maxSize) { int validSize = (keySize <= minSize) ? minSize : (keySize - keySize % skipSize) + skipSize; if (keySize < validSize) { m_Key = m_Key.PadRight(validSize / 8, '*'); } } } //使用 PBKDF1 算法的扩展从密码派生密钥 PasswordDeriveBytes key = new PasswordDeriveBytes(m_Key, ASCIIEncoding.ASCII.GetBytes(m_Salt)); return key.GetBytes(m_Key.Length);//返回密钥 }
11.2 .NET密码术编程基础 11.2.2 .NET中的对称加密算法及编程 代码实例11.2 对称加密算法实例 public SymCryptography( ) { m_CryptoService = new RijndaelManaged( ); m_CryptoService.Mode = CipherMode.CBC; } public SymCryptography(string serviceProviderName)//根据算法名称创建加密对象 { switch (serviceProviderName.ToLower( )) { case "rijndael": serviceProviderName = "Rijndael"; break; case "rc2": serviceProviderName = "RC2"; break; case "des": serviceProviderName = "DES"; break; case "tripledes": serviceProviderName = "TripleDES"; break; default: serviceProviderName = "Rijndael"; break; } m_CryptoService= (SymmetricAlgorithm)CryptoConfig.CreateFromName(serviceProvider Name); m_CryptoService.Mode = CipherMode.CBC; }
11.2 .NET密码术编程基础 11.2.2 .NET中的对称加密算法及编程 代码实例11.2 对称加密算法实例 public string Encrypt(string plainText)//加密 { byte[] plainByte = ASCIIEncoding.ASCII.GetBytes(plainText); //初始化参数 m_CryptoService.Key = GetLegalKey( ); m_CryptoService.IV = GetLegalIV( ); //创建加密实例 ICryptoTransform cryptoTransform = m_CryptoService.CreateEncryptor( ); //创建内存流 MemoryStream ms = new MemoryStream( ); //创建加密流 CryptoStream cs = new CryptoStream(ms, cryptoTransform, CryptoStreamMode.Write); //通过加密流加密数据 cs.Write(plainByte, 0, plainByte.Length); cs.FlushFinalBlock( ); //返回密文 byte[] cryptoByte = ms.ToArray( ); return Convert.ToBase64String(cryptoByte, 0, cryptoByte.GetLength(0)); } public string Decrypt(string cryptoText)//解密 { byte[] cryptoByte = Convert.FromBase64String(cryptoText); // 设置密钥和初始向量 m_CryptoService.Key = GetLegalKey( ); m_CryptoService.IV = GetLegalIV( ); // 创建解密对象 ICryptoTransform cryptoTransform = m_CryptoService.CreateDecryptor( );
11.2 .NET密码术编程基础 11.2.2 .NET中的对称加密算法及编程 代码实例11.2 对称加密算法实例 try { //创建内存流 MemoryStream ms = new MemoryStream(cryptoByte, 0, cryptoByte.Length); //创建密文流 CryptoStream cs = new CryptoStream(ms, cryptoTransform, CryptoStreamMode.Read); //解密并返回明文 StreamReader sr = new StreamReader(cs); return sr.ReadToEnd( ); } catch { return null; } } public string Key { get { return m_Key; } set { m_Key = value; } } public string IV { { get { return m_IV; } set { m_IV = value; } }
11.2 .NET密码术编程基础 11.2.2 .NET中的对称加密算法及编程 代码实例11.2 对称加密算法实例 public string Salt { { get { return m_Salt; } set { m_Salt = value; } } } class Program//测试 { static void Main(string[] args) { string m_ServiceProviderName = "", m_Txt = "", m_Key = "", m_IV = "", m_Salt = ""; string m_CryptoTxt = "", m_DecrytoTxt = ""; bool rt = false; SymCryptography m_SymCryptography = null; //从命令行参数提取散列算法名和需要加密的字符串 if (args.Length < 5) { Console.WriteLine("Usage: SymCrypt对称加密算法名[Rijndael|RC2|DES|TripleDES] 密钥IV值salt值加密字符串"); } else { m_ServiceProviderName = args[0].ToString( ); m_Key = args[1].ToString( ); m_IV = args[2].ToString( ); m_Salt = args[3].ToString( ); m_Txt = args[4].ToString( ); rt = true; }
11.2 .NET密码术编程基础 11.2.2 .NET中的对称加密算法及编程 代码实例11.2 对称加密算法实例 if (rt) { m_SymCryptography = new SymCryptography(m_ServiceProviderName); if (m_SymCryptography != null) { //初始化参数 m_SymCryptography.Key = m_Key; m_SymCryptography.Salt = m_Salt; m_SymCryptography.IV = m_IV; //加密 m_CryptoTxt = m_SymCryptography.Encrypt(m_Txt); //解密 m_DecrytoTxt = m_SymCryptography.Decrypt(m_CryptoTxt); //显示结果 Console.WriteLine("源文:{0}", m_Txt); Console.WriteLine("密文:{0}", m_CryptoTxt); Console.WriteLine("明文:{0}", m_DecrytoTxt); } } } } }
11.2 .NET密码术编程基础 11.2.3 使用非对称密码术的.NET编程 与对称加密类似,System.Security.Cryptography命名空间支持RSA和DSAl两种不对称加密算法。这些算法类都是从抽象基类AsymmetricAlgorithm派生而来。AsymmetricAlgorithm类提供了一些非对称加密算法的公用方法和属性,其主要公用方法和属性分别如表11.7和表11.8所示。 表11.7 AsymmetricAlgorithm类常用属性 表11.8 AsymmetricAlgorithm类常用方法
11.2 .NET密码术编程基础 11.2.3 使用非对称密码术的.NET编程 在AsymmetricAlgorithm类的基础上,System.Security.Cryptography命名空间给出了抽象算法实现类RSA和DSA,并在此基础上给出了具体加密方案实现类。其对应关系如表11.9所示。 RSACryptoServiceProvider类是公钥算法的一个实现类,通常用于数据的加密;DSACryptoServiceProvider类是数字签名算法的一个实现类。当然,也可以使用 RSACryptoServiceProvider创建和验证数字签名。 创建和管理密钥是加密过程的一个重要部分。不对称算法要求创建一个公钥和一个私钥。公钥可以对任何人公开,而私钥只有对用公钥加密的数据进行解密的一方知道。RSACryptoServiceProvider和DSACryptoServiceProvider类在创建新实例时将创建一个公钥/私钥对。并且可以用以下两种方法之一提取密钥信息。 (1)ToXMLString方法,它返回密钥信息的XML表示形式,其中参数为false时只返回公钥,而参数为true时则返回公钥/私钥对。 (2)ExportParameters方法,它返回RSAParameters结构以保存密钥信息,其中参数为false时只返回公钥,而参数为true时则返回公钥/私钥对。 由于私钥是只有解密方知道的密钥信息,不要以明文形式存储在本地计算机上。如果需要存储私钥,则应使用密钥容器。在.NET框架中提供了加密参数存储的容器类CspParameters,通过它可以保存密钥信息。
11.2 .NET密码术编程基础 11.2.3 使用非对称密码术的.NET编程 其步骤如下: 1.创建非对称密钥并将其保存在密钥容器中 (1)创建CspParameters类的一个新实例,并将您要密钥容器使用的名称传递给CspParameters.KeyContainerName字段。 (2)为非对称加密算法实现类创建一个新实例,并将先前创建的 CspParameters对象传递给其构造函数。 2.从密钥容器中删除密钥 (1)创建CspParameters类的一个新实例,并将您要密钥容器使用的名称传递给CspParameters.KeyContainerName字段。 (2)为非对称加密算法实现类创建一个新实例,并将先前创建的CspParameters对象传递给其构造函数。 (3)将非对称加密算法实现类的PersistKeyInCSP属性设置为false。 (4)调用非对称加密算法实现类的Clear方法。该方法释放该类所有的资源并清除密钥容器。 不对称算法通常用于加密少量数据,如加密对称密钥和初始向量IV等。在RSACryptoServiceProvider类提供了Encrypt和Decrypt来实现对数据的加密和解密。它们的原型声明如下: public byte[] Encrypt (byte[] rgb,bool fOAEP); public byte[] Decrypt (byte[] rgb,bool fOAEP); 其中参数rgb是要加密或解密的数据,参数fOAEP规定了填充方式,如果为true,则使用OAEP填充,否则使用PKCS#1 1.5版填充。 下面在RSACryptoServiceProvider的基础上实现了一个简单的不对称加密解密类,并对其进行了验证。其中包括密钥的产生、提取和设置。
11.2 .NET密码术编程基础 11.2.3 使用非对称密码术的.NET编程 代码实例11.3 非对称加密解密实例 using System; using System.Collections.Generic; using System.Text; using System.Security.Cryptography; namespace Ex_11_3 { public class ASymCryptography { private RSACryptoServiceProvider m_CryptoService; static private string m_PriKey, m_PubKey; public ASymCryptography( ) { m_CryptoService = new RSACryptoServiceProvider( ); } static public void GenKeys( ) { CspParameters m_CspParameters; m_CspParameters = new CspParameters( ); m_CspParameters.KeyContainerName = "MyKeyContainerName"; RSACryptoServiceProvider m_cp; m_cp = new RSACryptoServiceProvider(m_CspParameters); m_PriKey = m_cp.ToXmlString(true); m_PubKey = m_cp.ToXmlString(false); m_cp.PersistKeyInCsp = false; m_cp.Clear( ); }
11.2 .NET密码术编程基础 11.2.3 使用非对称密码术的.NET编程 代码实例11.3 非对称加密解密实例 public string Encrypt(string plainText)//加密 { byte[] plainByte = ASCIIEncoding.ASCII.GetBytes(plainText); //初始化参数 m_CryptoService.FromXmlString(m_PubKey); byte[] cryptoByte = m_CryptoService.Encrypt(plainByte, false); return Convert.ToBase64String(cryptoByte); } public string Decrypt(string cryptoText)//解密 { byte[] cryptoByte = Convert.FromBase64String(cryptoText); //初始化参数 m_CryptoService.FromXmlString(m_PriKey); byte[] plainByte = m_CryptoService.Decrypt(cryptoByte, false); return ASCIIEncoding.ASCII.GetString(plainByte,0,plainByte.Length); } public string PubKey { get { return m_PubKey; } } public string PriKey { get { return m_PriKey; } } }
11.2 .NET密码术编程基础 11.2.3 使用非对称密码术的.NET编程 代码实例11.3 非对称加密解密实例 class Program { static void Main(string[] args) { string m_Txt = "", m_CryptoTxt = "", m_DecrytoTxt = ""; ASymCryptography m_cp; bool rt = false; //从命令行参数提取需要加密的字符串 if (args.Length < 1) { Console.WriteLine("Usage: ASymCrypt 加密字符串"); } else { m_Txt = args[0].ToString( ); rt = true; } if (rt) { ASymCryptography.GenKeys( ); m_cp = new ASymCryptography( ); Console.WriteLine("PubKey is : {0}" + m_cp.PubKey); Console.WriteLine("PriKey is : {0}" + m_cp.PriKey); m_CryptoTxt = m_cp.Encrypt(m_Txt); m_DecrytoTxt = m_cp.Decrypt(m_CryptoTxt); //显示结果 Console.WriteLine("源文:{0}", m_Txt); Console.WriteLine("密文:{0}", m_CryptoTxt); Console.WriteLine("明文:{0}", m_DecrytoTxt); Console.Read( ); } } } }
11.3 综 合 实 例 在本书6.4.2节所实现的基于UDPClient的群组讨论工具实例的基础上,设计一个简单的具有加密数据传送功能的群组讨论工具。参加讨论者可以利用UDPClient客户端程序加入讨论群组,并从服务器端获取加密密钥;然后可以利用该密钥对其与服务器端的传送数据进行加密;服务器会将每个参加讨论者发送过来的信息根据其与每个讨论者约定的密钥进行加密然后转发给对应的讨论者。群组讨论工具客户程序采用了基于Windows的实现方案。而服务器程序还是基于命令行的实现方式。群组讨论工具的交互流程如图11.7所示。 图11.7群组讨论工具交互流程
11.3 综 合 实 例 其中申请加入群组的命令ADD和服务器接收请求并返回密钥的信息KEY都是以明文形式传递,在此基础上其他信息都是以密文形式传递。并且服务器与每个连接客户都单独维持着一个密钥。 在实现中,系统设计了六个类,其中客户端程序包括主程序类UDPClient和窗口类w_main;服务器程序主要包括服务器类UDPServer和客户端信息类ClientItem,其中ClientItem类中描述了客户端的IP地址和端口,以及加密实例信息;而UDPComm类中实现了客户端程序和服务器程序所需要的共性操作,包括对称加密类SymCryptography和数据编解码类UDPComm,其中UDPComm类中的编码函数EncodingASCII和解码函数DecodingASCII实现了重载,以支持数据加密和解密。整个系统的类图如图11.8所示。 图11.8群组讨论工具类图
11.3 综 合 实 例 整个程序包含了3个项目,即基于命令行的服务器程序项目UDPServer、基于动态类库的公共程序类库项目UDPComm,以及基于Windows的客户端程序项目UDPClient。运行时首先启动服务器程序,然后不同计算机上的参与者便可以通过客户端程序加入讨论组,利用该讨论工具进行讨论。代码实例11.4、代码实例11.5和代码实例11.6分别列出了UDPComm、UDPClient和UDPServer项目中的主要类的详细实现。 代码实例11.4 公共程序类库UDPComm using System; using System.IO; using System.Collections.Generic; using System.Text; using System.Security.Cryptography; namespace UDPComm { public class SymCryptography { private string m_IV = string.Empty; public SymmetricAlgorithm m_CryptoService; private byte[] GetLegalIV( ) //获取合法初始向量 { string m_iv = m_IV.Substring(0, m_IV.Length); int n = m_CryptoService.BlockSize / 8; if (m_iv.Length < n) { m_iv = m_iv.PadRight(n, '0'); } return ASCIIEncoding.ASCII.GetBytes(m_iv); }
11.3 综 合 实 例 代码实例11.4 公共程序类库UDPComm public SymCryptography( ) { m_CryptoService = new RijndaelManaged( ); m_CryptoService.Mode = CipherMode.CBC; m_CryptoService.GenerateKey( );//生成随机密钥 } public string Encrypt(string plainText)//加密 { byte[] plainByte = ASCIIEncoding.ASCII.GetBytes(plainText); //初始化参数 m_CryptoService.IV = GetLegalIV( ); //创建加密实例 ICryptoTransform cryptoTransform = m_CryptoService.CreateEncryptor( ); //创建内存流 MemoryStream ms = new MemoryStream( ); //创建加密流 CryptoStream cs = new CryptoStream(ms, cryptoTransform, CryptoStreamMode.Write); //通过加密流加密数据 cs.Write(plainByte, 0, plainByte.Length); cs.FlushFinalBlock( ); //返回密文 byte[] cryptoByte = ms.ToArray( ); return Convert.ToBase64String(cryptoByte, 0, cryptoByte.GetLength(0)); } public string Decrypt(string cryptoText)//解密 { byte[] cryptoByte = Convert.FromBase64String(cryptoText); // 设置初始向量 m_CryptoService.IV = GetLegalIV( ); // 创建解密对象 ICryptoTransform cryptoTransform = m_CryptoService.CreateDecryptor( );
11.3 综 合 实 例 代码实例11.4 公共程序类库UDPComm try { //创建内存流 MemoryStream ms = new MemoryStream(cryptoByte, 0, cryptoByte.Length); //创建密文流 CryptoStream cs = new CryptoStream(ms, cryptoTransform, CryptoStreamMode.Read); //解密并返回明文 StreamReader sr = new StreamReader(cs); return sr.ReadToEnd( ); } catch { return null; } } public string Key { get { return Convert.ToBase64String(m_CryptoService.Key, 0, m_CryptoService.Key.GetLength(0)); } set { m_CryptoService.Key = Convert.FromBase64String(value); } } } public class UDPComm { public static byte[] EncodingASCII(string buf, SymCryptography m_CryptoService) //加密编码 { byte[] data = Encoding.ASCII.GetBytes(m_CryptoService.Encrypt(buf)); return data; }
11.3 综 合 实 例 代码实例11.4 公共程序类库UDPComm public static string DecodingASCII(byte[] buf, SymCryptography m_CryptoService)//解密解码 { string st = Encoding.ASCII.GetString(buf); return m_CryptoService.Decrypt(st); } public static byte[] EncodingASCII(string buf) //编码 { byte[] data = Encoding.ASCII.GetBytes(buf); return data; } public static string DecodingASCII(byte[] buf)//解码 { string st = Encoding.ASCII.GetString(buf); return st; } } }
11.3 综 合 实 例 其中,对称加密类SymCryptography中的初始向量采用固定值,而密钥则由程序随机生成,同时也可以通过属性Key进行设置。以便确保在服务器和客户端程序中使用时两端的加密实例具有相同的初始向量和密钥。 代码实例11.5 客户端程序 using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Net; using System.Net.Sockets; using System.Threading; using UDPComm; namespace UDPClient { public partial class w_main : Form { string m_hostIP = "127.0.0.1"; int m_port = 6666; UdpClient m_client; volatile bool done = false; byte[] data; string m_SendData, m_ReturnData; IPEndPoint m_EndPoint; Thread m_ReceiveThread; private SymCryptography m_SymCrypt;
11.3 综 合 实 例 代码实例11.5 客户端程序 public w_main( ) { InitializeComponent( ); //初始化 m_client = null; textBox_HostIP.Text = m_hostIP; textBox_Port.Text = m_port.ToString( ); m_SymCrypt = null; } //接收数据 private void Listener( ) { done = false; try { while (!done) { IPEndPoint m_ep = null; byte[] buf = m_client.Receive(ref m_ep); m_ReturnData = UDPComm.UDPComm.DecodingASCII(buf, m_SymCrypt); this.Invoke(new MethodInvoker(DisplayReceiveMessage)); } } catch { return; } m_client.Close( ); m_client = null; }
11.3 综 合 实 例 代码实例11.5 客户端程序 //显示接收数据 private void DisplayReceiveMessage( ) { string time = DateTime.Now.ToString("t"); textBox_Msg.Text = time + " " + m_ReturnData + "\r\n" + textBox_Msg.Text; } //发送数据 private void button_Send_Click(object sender, EventArgs e) { if (m_client != null) { m_SendData = textBox_Input.Text; data = UDPComm.UDPComm.EncodingASCII(m_SendData,m_SymCrypt); m_client.Send(data, data.Length); } } //加入讨论组 private void button_Add_Click(object sender, EventArgs e) { try { m_hostIP = textBox_HostIP.Text; IPAddress m_ipA = IPAddress.Parse(m_hostIP); m_port = int.Parse(textBox_Port.Text.Trim( )); m_EndPoint = new IPEndPoint(m_ipA, m_port); m_client = new UdpClient( ); m_client.Connect(m_EndPoint); m_SendData = "ADD"; data = UDPComm.UDPComm.EncodingASCII(m_SendData); m_client.Send(data, data.Length); DateTime de,ds = DateTime.Now; de = ds;
11.3 综 合 实 例 代码实例11.5 客户端程序 TimeSpan ts; while ((ts = (de-ds)).TotalMilliseconds<2000) { IPEndPoint m_ep = null; byte[] buf = m_client.Receive(ref m_ep); m_ReturnData = UDPComm.UDPComm.DecodingASCII(buf); if (m_ReturnData.IndexOf("KEY") > -1) { m_SymCrypt = new SymCryptography( ); m_SymCrypt.Key = m_ReturnData.Substring(4, m_ReturnData.Length - 4); string time = DateTime.Now.ToString("t"); textBox_Msg.Text = time + " " + "Add sucessfully\r\n" + textBox_Msg.Text; break; } else { m_client = null; break; } de = DateTime.Now; } if ((ts = (de - ds)).TotalMilliseconds >= 2000) m_client = null; if (m_client != null) { m_ReceiveThread = new Thread(new ThreadStart(Listener)); m_ReceiveThread.Start( ); } }
11.3 综 合 实 例 代码实例11.5 客户端程序 catch (Exception ex) {MessageBox.Show(ex.Message);} } //离开讨论组 private void button_Del_Click(object sender, EventArgs e) { if (m_client != null) { m_SendData = "DEL"; data = UDPComm.UDPComm.EncodingASCII(m_SendData, m_SymCrypt); m_client.Send(data, data.Length); done = true; } } private void button_Exit_Click(object sender, EventArgs e) { if (m_client != null) { m_SendData = "DEL"; data = UDPComm.UDPComm.EncodingASCII(m_SendData, m_SymCrypt); m_client.Send(data, data.Length); done = true; } Thread.Sleep(1); if (m_ReceiveThread != null) m_ReceiveThread.Join(1); this.Close( ); } } }
11.3 综 合 实 例 客户端程序在申请加入讨论组时,采用明文方式发送请求,同时服务器也以明文形式将密钥发送给客户端程序。在此基础上所有的传送信息都以密文形式传递。同时,客户端程序中引入了多线程技术实现对服务器所发送过来数据的接收、处理和显示,以方便用户与操作窗口的交互。 代码实例11.6 服务器程序 using System; using System.Collections; using System.Net; using System.Net.Sockets; using System.Collections.Generic; using System.Text; using UDPComm; namespace UDPServer { class ClientItem { private IPEndPoint m_IPEndPoint; private SymCryptography m_SymCrypt; public ClientItem( ) { m_IPEndPoint = null; m_SymCrypt = null; } public IPEndPoint EndPoint { get { return m_IPEndPoint; } set { m_IPEndPoint = value; } }
11.3 综 合 实 例 代码实例11.6 服务器程序 public SymCryptography SymCrypt { get { return m_SymCrypt; } set { m_SymCrypt = value; } } public override bool Equals(object obj) { return (m_IPEndPoint.ToString( ) == (obj as ClientItem).EndPoint.ToString( )); } } class UDPServer { static UdpClient m_server; static ArrayList mblist; static ClientItem FindItem(IPEndPoint rep) { ClientItem m_Item = new ClientItem( ); m_Item.EndPoint = rep; if (mblist.Contains(m_Item)) { return (ClientItem)mblist[mblist.IndexOf(m_Item)]; } return null; }
11.3 综 合 实 例 代码实例11.6 服务器程序 static bool AddMember(IPEndPoint rep)//加入组 { bool rt = false; ClientItem m_Item = new ClientItem( ); m_Item.EndPoint = rep; if (!mblist.Contains(m_Item)) { m_Item.SymCrypt = new SymCryptography( ); mblist.Add(m_Item); byte[] data = UDPComm.UDPComm.EncodingASCII("KEY:" + m_Item.SymCrypt.Key); m_server.Send(data, data.Length, m_Item.EndPoint); rt = true; } else { byte[] data = UDPComm.UDPComm.EncodingASCII("ERR"); m_server.Send(data, data.Length, m_Item.EndPoint); } return rt; } static void DelMember(IPEndPoint rep)//离开组 { ClientItem m_Item = FindItem(rep); if (m_Item != null) { byte[] data = UDPComm.UDPComm.EncodingASCII("OK", m_Item.SymCrypt); m_server.Send(data, data.Length, m_Item.EndPoint); mblist.Remove(m_Item); } }
11.3 综 合 实 例 代码实例11.6 服务器程序 static void SendToMember(string buf)//组内转发数据 { foreach (ClientItem m_Item in mblist) { byte[] data = UDPComm.UDPComm.EncodingASCII(buf,m_Item.SymCrypt); m_server.Send(data, data.Length, m_Item.EndPoint); } } static void Main(string[] args) { string m_hostIP = "127.0.0.1"; int m_port = 6666; IPEndPoint m_EndPoint; ArrayList memberlist = new ArrayList( ); bool rt = false; byte[] data; string m_ReturnData; //从命令行提取主机IP和端口 if (args.Length < 2) { Console.WriteLine("Usage: UDPServer hostIP port"); } else { m_hostIP = args[0].ToString( ); m_port = int.Parse(args[1].ToString( )); rt = true; }
11.3 综 合 实 例 代码实例11.6 服务器程序 if (rt) { mblist = new ArrayList( );//组成员列表 IPAddress m_ipA = IPAddress.Parse(m_hostIP); m_EndPoint = new IPEndPoint(m_ipA, m_port); m_server = new UdpClient(m_EndPoint); Console.WriteLine("Ready for Connect......"); while (true) { data = m_server.Receive(ref m_EndPoint);//接收数据 m_ReturnData = UDPComm.UDPComm.DecodingASCII(data); if ((m_ReturnData.IndexOf("ADD") > -1) && (AddMember(m_EndPoint)))//加入组 { Console.WriteLine(m_EndPoint.ToString( ) + " has added to group!"); } else { m_ReturnData = UDPComm.UDPComm.DecodingASCII(data, FindItem(m_EndPoint).SymCrypt); if (m_ReturnData.IndexOf("DEL") > -1)//退出组 { DelMember(m_EndPoint); Console.WriteLine(m_EndPoint.ToString( ) + " has deleted from group!"); } else { if (FindItem(m_EndPoint) != null) //转发数据 { SendToMember(m_ReturnData+"[" + m_EndPoint.ToString( ) + "]"); Console.WriteLine(m_ReturnData+"["+m_EndPoint.ToString( )+"]"+ " has resented to members!"); } } } } m_server.Close( ); } } } }