C#加密与解密(DES\RSA)学习笔记
本笔记摘抄自:,记录一下学习过程以备后续查用。
数据加密技术是网络中最基本的安全技术,主要是通过对网络中传输的信息进行数据加密来保障其安全性,这是一种主动安全防御策略,用很小的代价
即可为信息提供相当大的安全保护。
一、加密的基本概念
"加密",是一种限制对网络上传输数据的访问权的技术。原始数据(也称为明文,plaintext)被加密设备(硬件或软件)和密钥加密而产生的经过编码的数据
称为密文(ciphertext)。将密文还原为原始明文的过程称为解密,它是加密的反向处理,但解密者必须利用相同类型的加密设备和密钥对密文进行解密。
加密的基本功能包括:
1)防止不速之客查看机密的数据文件。
2)防止机密数据被泄露或篡改。
3)防止特权用户(如系统管理员)查看私人数据文件。
4)使入侵者不能轻易地查找一个系统的文件。
数据加密是确保计算机网络安全的一种重要机制,虽然由于成本、技术和管理上的复杂性等原因,目前尚未在网络中普及,但数据加密的确是实现分布
式系统和网络环境下数据安全的重要手段之一。
数据加密可在网络osi七层协议(osi是open system interconnect的缩写,意为开放式系统互联。国际标准组织(国际标准化组织)制定了osi模型。这
个模型把网络通信的工作分为7层,分别是物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。)的多层上实现,所以从加密技术应用的逻
辑位置看,有三种方式:
1)链路加密:通常把网络层以下的加密叫链路加密,主要用于保护通信节点间传输的数据,加解密由置于线路上的密码设备实现。根据传递的数据的同
步方式又可分为同步通信加密和异步通信加密两种,同步通信加密又包含字节同步通信加密和位同步通信加密。
2)节点加密:是对链路加密的改进。在协议传输层上进行加密,主要是对源节点和目标节点之间传输数据进行加密保护,与链路加密类似,只是加密算
法要结合在依附于节点的加密模件中,克服了链路加密在节点处易遭非法存取的缺点。
3)端对端加密:网络层以上的加密称为端对端加密,是面向网络层主体。对应用层的数据信息进行加密,易于用软件实现,且成本低,但密钥管理问题
困难,主要适合大型网络系统中信息在多个发方和收方之间传输的情况。
二、数据加密的应用
1)媒体加密:drm
2)文件加密:文本加密、pdf、word
3)数据加密:c#中的数据加密
4)硬件加密:加密狗
三、加密技术发展趋势
1)私用密钥加密技术与公开密钥加密技术相结合:鉴于两种密码*加密的特点,在实际应用中可以采用折衷方案,即结合使用des/idea和rsa,以
des为"内核",rsa为"外壳",对于网络中传输的数据可用des或idea加密,而加密用的密钥则用rsa加密传送,此种方法既保证了数据安全又提高了加密
和解密的速度,这也是目前加密技术发展的新方向之一。
2)寻求新算法:跳出以常见的迭代为基础的构造思路,脱离基于某些数学问题复杂性的构造方法。如刘尊全先生提出的刘氏算法,是一种基于密钥的公
开密钥*。它采用随机性原理构造加解密变换,并将其全部运算控制隐匿于密钥中,密钥长度可变;它采用选取一定长度的分割来构造大的搜索空间,从
而实现一次非线性变换。此种加密算法加密强度高、速度快、计算开销低。
3)加密最终将被集成到系统和网络中,例如ipv6协议就已有了内置加密的支持,在硬件方面,intel公司正研制一种加密协处理器,它可以集成到微机的
主板上。
四、加密技术的分类
加密类型可以简单地分为四种:
1)根本不考虑解密问题。
2)私用密钥加密技术--对称式加密(symmetric key encryption):对称式加密方式对加密和解密使用相同的密钥。通常,这种加密方式在应用中难以实
施,因为用同一种安全方式共享密钥很难,如:rc4、rc2、des和aes系列加密算法。
3)公开密钥加密技术--非对称密钥加密(asymmetric key encryption):非对称密钥加密使用一组公共/私人密钥系统,加密时使用一种密钥,解密时使
用另一种密钥。公共密钥可以广泛的共享和透露,当需要用加密方式向服务器外部传送数据时,这种加密方式更方便,如: rsa。
4)数字证书(certificate):数字证书是一种非对称密钥加密,但是,一个组织可以使用证书并通过数字签名将一组公钥和私钥与其拥有者相关联。
五、对称加密之des加密与解密
5.1对称加密简述
对称加密,是一种比较传统的加密方式,其加密运算、解密运算使用的是同样的密钥,信息的发送者和信息的接收者在进行信息的传输与处理时,必须共
同持有该密码(称为对称密码)。因此,通信双方都必须获得这把钥匙,并保持钥匙的秘密。
单钥密码系统的安全性依赖于以下两个因素:
第一、加密算法必须是足够强,仅仅基于密文本身去解密信息在实践上是不可能的。
第二、加密方法的安全性依赖于密钥的秘密性,而不是算法的秘密性,因此,我们没有必要确保算法的秘密性(事实上,现实中使用的很多单钥密码系统
的算法都是公开的),但是我们一定要保证密钥的秘密性。
des(data encryption standard)和tripledes是对称加密的两种实现:
1)des和tripledes基本算法一致,只是tripledes算法提供的key位数更多,加密可靠性更高。
2)des使用的密钥key为8字节,初始向量iv也是8字节。
3)tripledes的密钥key为24字节,初始向量iv也是8字节。
4)两种算法都是以8字节为一个块进行加密,一个数据块一个数据块的加密,一个8字节的明文加密后的密文也是8字节。如果明文长度不为8字节的整数
倍,添加值为0的字节凑满8字节整数倍,所以加密后的密文长度一定为8字节的整数倍。
5.2加密解密过程
上图是整个des和tripledes算法的加密解密过程,下面以tripledes为例,结合dotnet分析加密解密的各个步骤,并给出相关实现代码。
5.2.1生成key和iv
system.security.cryptography.tripledescryptoserviceprovider类是.net中实现tripledes算法的主要类。
tripledescryptoserviceprovider类只有一个构造方法tripledescryptoserviceprovider(),这个方法把一些属性初始化:
keysize(加密密钥长度,以位为单位)= 192(24字节)
blocksize(加密处理的数据块大小,以位为单位)= 64(8字节)
feedbacksize(加密数据块后返回的数据大小,以位为单位)= 64(8字节)
tripledescryptoserviceprovider构造方法同时会初始化一组随机的key和iv。
默认的tripledescryptoserviceprovider的key为24字节,iv为8字节,加密数据块为8字节。
生成key和iv的代码很简单:
tripledescryptoserviceprovider tripledes = new tripledescryptoserviceprovider(); byte[] keyarray = tripledes.key; byte[] ivarray = tripledes.iv;
5.2.2字符串明文转成编码字节流
待加密的数据可能有两种形式:一种是二进制的数据,本身就是一组字节流,这样的数据可以跳过这一步,直接进入加密步骤;还有一种情况是字符串数
据,字符串中同样的字符使用不同的编码会生成不同的字节码,所以从字符串到字节流的转换是需要指定使用的编码。在解密之后,要从字节流转换到字符
串就要使用相同的编码解码,否则会出现乱码。
//待加密的字符串 string plaintextstring = "here is some data to encrypt."; //使用utf-8编码(也可以使用其它的编码) encoding encoding = encoding.getencoding("utf-8"); //把字符串明文转换成utf-8编码的字节流 byte[] plaintextarray = encoding.getbytes(plaintextstring);
5.2.3加密操作
加密的原料是明文字节流,tripledes算法对字节流进行加密,返回的是加密后的字节流,同时要给定加密使用的key和iv。
/// <summary> /// 加密解密帮助类 /// </summary> public static class cryptohelper { /// <summary> /// 对称加密之tripledes加密 /// </summary> /// <param name="plaintextarray">明文字节数组</param> /// <param name="key">key</param> /// <param name="iv">iv</param> /// <returns>返回字节数组</returns> public static byte[] tripledesencrypt(string plaintext, byte[] key, byte[] iv) { //将明文字符串转成明文字节数组 encoding encoding = encoding.getencoding("utf-8"); byte[] plaintextarray = encoding.getbytes(plaintext); //新建一个memorystream对象存放加密后的数据流 memorystream memorystream = new memorystream(); //新建一个cryptostream对象 cryptostream cryptostream = new cryptostream ( memorystream, new tripledescryptoserviceprovider().createencryptor(key, iv), cryptostreammode.write ); //将加密后的字节流写入到memorystream cryptostream.write(plaintextarray, 0, plaintextarray.length); //把缓冲区中的最后状态更新到memorystream,并清除cryptostream的缓存区。 cryptostream.flushfinalblock(); //把加密后的数据流转成字节流 byte[] result = memorystream.toarray(); //关闭两个stream cryptostream.close(); memorystream.close(); //返回结果 return result; } }
5.2.4解密操作
解密5.2.3生成的密文byte[],需要使用到加密步骤使用的同一组key和iv。
/// <summary> /// 加密解密帮助类 /// </summary> public static class cryptohelper { /// <summary> /// 对称加密之tripledes解密 /// </summary> /// <param name="encrypttextarray">加密字节数组</param> /// <param name="key">key</param> /// <param name="iv">iv</param> /// <returns>返回字符串</returns> public static string tripledesdecrypt(byte[] encrypttextarray, byte[] key, byte[] iv) { //将加密字符串转成加密字节数组 encoding encoding = encoding.getencoding("utf-8"); //新建一个memorystream对象存放解密后的数据流 memorystream memorystream = new memorystream(encrypttextarray); //新建一个cryptostream对象 cryptostream cryptostream = new cryptostream ( memorystream, new tripledescryptoserviceprovider().createdecryptor(key, iv), cryptostreammode.read ); //新建一个存放解密后的明文字节数组(可能比加密前的明文长) byte[] decrypttextarray = new byte[encrypttextarray.length]; //把解密后的数据流读到 cryptostream.read(decrypttextarray, 0, decrypttextarray.length); //关闭两个stream memorystream.close(); cryptostream.close(); return encoding.getstring(decrypttextarray); } }
有一点需要注意,des加密是以数据块为单位加密的,8个字节一个数据块。如果待加密明文byte[]的长度不是8字节的整数倍,算法先用值为“0”的byte补
足8个字节,然后再进行加密,所以加密后的密文长度一定是8的整数倍。这样的密文解密后如果补了0值的byte,则解密后这些0值的byte依然存在。
5.2.5示例
class program { static void main(string[] args) { #region 对称加密之tripledes加密与解密 //明文数据 string plaintext = "人生如戏,全靠演技。"; //生成key和iv tripledescryptoserviceprovider tripledes = new tripledescryptoserviceprovider(); byte[] keyarray = tripledes.key; byte[] ivarray = tripledes.iv; //加密 byte[] encrypttextarray = cryptohelper.tripledesencrypt(plaintext, keyarray, ivarray); //解密 string decrypttext = cryptohelper.tripledesdecrypt(encrypttextarray, keyarray, ivarray); //输出 console.writeline($"明文数据:{plaintext}"); console.writeline($"解密后数据:{decrypttext}"); console.read(); #endregion } }
运行结果如下:
六、非对称加密之rsa加密和解密的讲解
rsa公钥加密算法是1977年由ron rivest、adi shamirh和lenadleman在(美国麻省理工学院)开发的。rsa取名来自开发他们三者的名字。rsa是目
前最有影响力的公钥加密算法,它能够抵抗到目前为止已知的所有密码攻击,已被iso推荐为公钥数据加密标准;rsa算法基于一个十分简单的数论事实:
将两个大素数相乘十分容易,但是想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥;rsa算法是第一个能同时用于加密和数字签
名的算法,也易于理解和操作。
rsa算法是被研究得最广泛的公钥算法,从提出到现在已近二十年,经历了各种攻击的考验,逐渐为人们所接受,被普遍认为是目前最优秀的公钥方案之
一。rsa的安全性依赖于大数的因子分解,但并没有从理论上证明破译rsa的难度与大数分解难度等价,即rsa的重大缺陷是无法从理论上把握它的保密性
能如何,而且密码学界多数人士倾向于因子分解不是npc问题。
rsa的缺点主要有:
1)产生密钥很麻烦,受到素数产生技术的限制,因而难以做到一次一密。
2)分组长度太大,为保证安全性,n至少也要600bits以上,导致运算代价很高,尤其是速度较慢(较对称密码算法慢几个数量级),且随着大数分解技术
的发展,这个长度还在增加,不利于数据格式的标准化。目前,set(secure electronic transaction)协议中要求ca采用2048bits长的密钥,其它实体使用
1024比特的密钥。
3)rsa密钥长度随着保密级别提高,增加很快。
下表列出了对同一安全级别所对应的密钥长度:
保密级别 |
对称密钥长度(bit) |
rsa密钥长度(bit) |
ecc密钥长度(bit) |
保密年限 |
80 |
80 |
1024 |
160 |
2010 |
112 |
112 |
2048 |
224 |
2030 |
128 |
128 |
3072 |
256 |
2040 |
192 |
192 |
7680 |
384 |
2080 |
256 |
256 |
15360 |
512 |
2120 |
rsa算法是一种非对称密码算法,所谓非对称,就是指该算法需要一对密钥,使用其中一个加密,则需要用另一个才能解密。
rsa的算法涉及三个参数n、e1、e2:
1)n是两个大质数p、q的积,n的二进制表示时所占用的位数,就是所谓的密钥长度。
2)e1和e2是一对相关的值,e1可以任意取,但要求e1与(p-1)*(q-1)互质,再选择e2,要求(e2*e1)mod((p-1)*(q-1))=1。
3)(n及e1)、(n及e2)就是密钥对。
rsa加解密的算法完全相同,设a为明文,b为密文,则:a=b^e1 mod n;b=a^e2 mod n;
e1和e2可以互换使用,即:a=b^e2 mod n;b=a^e1 mod n;
6.1生成一对公钥和密钥
/// <summary> /// 加密解密帮助类 /// </summary> public static class cryptohelper { /// <summary> /// 非对称加密之rsa产生公钥密钥 /// </summary> public static void rsacreatekey() { rsacryptoserviceprovider rsa = new rsacryptoserviceprovider(); //公钥 using (streamwriter writer = new streamwriter(@"..\..\publickey.xml")) { writer.writeline(rsa.toxmlstring(false)); } //密钥(请注意保密) using (streamwriter writer = new streamwriter(@"..\..\privatekey.xml")) { writer.writeline(rsa.toxmlstring(true)); } } }
6.2加密操作
/// <summary> /// 加密解密帮助类 /// </summary> public static class cryptohelper { /// <summary> /// 非对称加密之rsa加密 /// </summary> /// <param name="publickey">公钥</param> /// <param name="plaintext">明文字符串</param> /// <returns>加密字符串</returns> public static string rsaencrypt(string publickey, string plaintext) { streamreader reader = new streamreader(@"..\..\publickey.xml", encoding.utf8); publickey = reader.readtoend(); reader.close(); rsacryptoserviceprovider rsa = new rsacryptoserviceprovider(); rsa.fromxmlstring(publickey); byte[] cipherbytes = rsa.encrypt(encoding.utf8.getbytes(plaintext), false); return convert.tobase64string(cipherbytes); } }
6.3解密操作
/// <summary> /// 加密解密帮助类 /// </summary> public static class cryptohelper { /// <summary> /// 非对称加密之rsa解密 /// </summary> /// <param name="privatekey">密钥</param> /// <param name="encrypttext">加密字符串</param> /// <returns>解密字符串</returns> public static string rsadecrypt(string privatekey, string encrypttext) { streamreader reader = new streamreader(@"..\..\privatekey.xml", encoding.utf8); privatekey = reader.readtoend(); reader.close(); rsacryptoserviceprovider rsa = new rsacryptoserviceprovider(); rsa.fromxmlstring(privatekey); byte[] cipherbytes = rsa.decrypt(convert.frombase64string(encrypttext), false); return encoding.utf8.getstring(cipherbytes); } }
6.4示例
class program { static void main(string[] args) { #region 非对称加密之rsa加密与解密 //明文数据 string plaintext = "人生如戏,全靠演技。"; //生成公钥和密钥 cryptohelper.rsacreatekey(); //加密 string encrypttext = cryptohelper.rsaencrypt("", plaintext); //解密 string decrypttext = cryptohelper.rsadecrypt("", encrypttext); console.writeline($"明文数据:{plaintext}\n"); console.writeline($"加密后数据:{encrypttext}\n"); console.writeline($"解密后数据:{decrypttext}"); console.read(); #endregion } }
运行结果如下: