AES字节数组加密解密流程
AES类时微软MSDN中最常用的加密类,微软官网也有例子,参考链接:https://docs.microsoft.com/zh-cn/dotnet/api/system.security.cryptography.aes?view=netframework-4.8
但是这个例子并不好用,限制太多,通用性差,实际使用中,我遇到的更多情况需要是这样:
1、输入一个字节数组,经AES加密后,直接输出加密后的字节数组。
2、输入一个加密后的字节数组,经AES解密后,直接输出原字节数组。
对于我这个十八流业务爱好者来说,AES我是以用为主的,所以具体的AES是怎么运算的,我其实并不关心,我更关心的是AES的处理流程。结果恰恰这一方面,网上的信息差强人意,看了网上不少的帖子,但感觉都没有说完整说透,而且很多帖子有错误。
因此,我自己绘制了一张此种方式下的流程图:
按照此流程图进行了核心代码的编写,验证方法AesCoreSingleTest既是依照此流程的产物,实例化类AesCoreSingle后调用此方法即可验证。
至于类中的异步方法EnOrDecryptFileAsync,则是专门用于文件加解密的处理,此异步方法参考自《C# 6.0学习笔记》(周家安 著)最后的示例,这本书写得真棒。
1 using System;
2 using System.Collections.Generic;
3 using System.IO;
4 using System.Linq;
5 using System.Security.Cryptography;
6 using System.Text;
7 using System.Threading;
8 using System.Threading.Tasks;
9 using System.Windows;
10
11 namespace AesSingleFile
12 {
13 class AesCoreSingle
14 {
15 /// <summary>
16 /// 使用用户口令,生成符合AES标准的key和iv。
17 /// </summary>
18 /// <param name="password">用户输入的口令</param>
19 /// <returns>返回包含密钥和向量的元组</returns>
20 private (byte[] Key, byte[] IV) GenerateKeyAndIV(string password)
21 {
22 byte[] key = new byte[32];
23 byte[] iv = new byte[16];
24 byte[] hash = default;
25 if (string.IsNullOrWhiteSpace(password))
26 throw new ArgumentException("必须输入口令!");
27 using (SHA384 sha = SHA384.Create())
28 {
29 byte[] buffer = Encoding.UTF8.GetBytes(password);
30 hash = sha.ComputeHash(buffer);
31 }
32 //用SHA384的原因:生成的384位哈希值正好被分成两段使用。(32+16)*8=384。
33 Array.Copy(hash, 0, key, 0, 32);//生成256位密钥(32*8=256)
34 Array.Copy(hash, 32, iv, 0, 16);//生成128位向量(16*8=128)
35 return (Key: key, IV: iv);
36 }
37
38 public byte[] EncryptByte(byte[] buffer, string password)
39 {
40 byte[] encrypted;
41 using (Aes aes = Aes.Create())
42 {
43 //设定密钥和向量
44 (aes.Key, aes.IV) = GenerateKeyAndIV(password);
45 //设定运算模式和填充模式
46 aes.Mode = CipherMode.CBC;//默认
47 aes.Padding = PaddingMode.PKCS7;//默认
48 //创建加密器对象(加解密方法不同处仅仅这一句话)
49 var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
50 using (MemoryStream msEncrypt = new MemoryStream())
51 {
52 using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))//选择Write模式
53 {
54 csEncrypt.Write(buffer, 0, buffer.Length);//对原数组加密并写入流中
55 csEncrypt.FlushFinalBlock();//使用Write模式需要此句,但Read模式必须要有。
56 encrypted = msEncrypt.ToArray();//从流中写入数组(加密之后,数组变长,详见方法AesCoreSingleTest内容)
57 }
58 }
59 }
60 return encrypted;
61 }
62 public byte[] DecryptByte(byte[] buffer, string password)
63 {
64 byte[] decrypted;
65 using (Aes aes = Aes.Create())
66 {
67 //设定密钥和向量
68 (aes.Key, aes.IV) = GenerateKeyAndIV(password);
69 //设定运算模式和填充模式
70 aes.Mode = CipherMode.CBC;//默认
71 aes.Padding = PaddingMode.PKCS7;//默认
72 //创建解密器对象(加解密方法不同处仅仅这一句话)
73 var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
74 using (MemoryStream msDecrypt = new MemoryStream(buffer))
75 {
76 using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))//选择Read模式
77 {
78 byte[] buffer_T = new byte[buffer.Length];/*--s1:创建临时数组,用于包含可用字节+无用字节--*/
79
80 int i = csDecrypt.Read(buffer_T, 0, buffer.Length);/*--s2:对加密数组进行解密,并通过i确定实际多少字节可用--*/
81
82 //csDecrypt.FlushFinalBlock();//使用Read模式不能有此句,但write模式必须要有。
83
84 decrypted = new byte[i];/*--s3:创建只容纳可用字节的数组--*/
85
86 Array.Copy(buffer_T, 0, decrypted, 0, i);/*--s4:从bufferT拷贝出可用字节到decrypted--*/
87 }
88 }
89 return decrypted;
90 }
91 }
92 public byte[] EnOrDecryptByte(byte[] buffer, string password, ActionDirection direction)
93 {
94 if (buffer == null)
95 throw new ArgumentNullException("buffer为空");
96 if (password == null || password == "")
97 throw new ArgumentNullException("password为空");
98 if (direction == ActionDirection.EnCrypt)
99 return EncryptByte(buffer, password);
100 else
101 return DecryptByte(buffer, password);
102 }
103 public enum ActionDirection//该枚举说明是加密还是解密
104 {
105 EnCrypt,//加密
106 DeCrypt//解密
107 }
108 public static void AesCoreSingleTest(string s_in, string password)//验证加密解密模块正确性方法
109 {
110 byte[] buffer = Encoding.UTF8.GetBytes(s_in);
111 AesCoreSingle aesCore = new AesCoreSingle();
112 byte[] buffer_ed = aesCore.EncryptByte(buffer, password);
113 byte[] buffer_ed2 = aesCore.DecryptByte(buffer_ed, password);
114 string s = Encoding.UTF8.GetString(buffer_ed2);
115 string s2 = "下列字符串\n" + s + '\n' + $"原buffer长度 → {buffer.Length}, 加密后buffer_ed长度 → {buffer_ed.Length}, 解密后buffer_ed2长度 → {buffer_ed2.Length}";
116 MessageBox.Show(s2);
117 /* 字符串在加密前后的变化(默认CipherMode.CBC运算模式, PaddingMode.PKCS7填充模式)
118 * 1、如果数组长度为16的倍数,则加密后的数组长度=原长度+16
119 如对于下列字符串
120 D:\User\Documents\Administrator - DOpus Config - 2020-06-301.ocb
121 使用UTF8编码转化为字节数组后,
122 原buffer → 64, 加密后buffer_ed → 80, 解密后buffer_ed2 → 64
123 * 2、如果数组长度不为16的倍数,则加密后的数组长度=16倍数向上取整
124 如对于下列字符串
125 D:\User\Documents\cc_20200630_113921.reg
126 使用UTF8编码转化为字节数组后
127 原buffer → 40, 加密后buffer_ed → 48, 解密后buffer_ed2 → 40
128 参考文献:
129 1-《AES补位填充PaddingMode.Zeros模式》http://blog.chinaunix.net/uid-29641438-id-5786927.html
130 2-《关于PKCS5Padding与PKCS7Padding的区别》https://www.cnblogs.com/midea0978/articles/1437257.html
131 3-《AES-128 ECB 加密有感》http://blog.sina.com.cn/s/blog_60cf051301015orf.html
132 */
133 }
134
135 /***---声明CancellationTokenSource对象--***/
136 private CancellationTokenSource cts;//using System.Threading;引用
137 public Task EnOrDecryptFileAsync(Stream inStream, long inStream_Seek, Stream outStream, long outStream_Seek, string password, ActionDirection direction, IProgress<int> progress)
138 {
139 /***---实例化CancellationTokenSource对象--***/
140 cts?.Dispose();//cts为空,不动作,cts不为空,执行Dispose。
141 cts = new CancellationTokenSource();
142
143 Task mytask = new Task(
144 () =>
145 {
146 EnOrDecryptFile(inStream, inStream_Seek, outStream, outStream_Seek, password, direction, progress);
147 }, cts.Token, TaskCreationOptions.LongRunning);
148 mytask.Start();
149 return mytask;
150 }
151 public void EnOrDecryptFile(Stream inStream, long inStream_Seek, Stream outStream, long outStream_Seek, string password, ActionDirection direction, IProgress<int> progress)
152 {
153 if (inStream == null || outStream == null)
154 throw new ArgumentException("输入流与输出流是必须的");
155 //--调整流的位置(通常是为了避开文件头部分)
156 inStream.Seek(inStream_Seek, SeekOrigin.Begin);
157 outStream.Seek(outStream_Seek, SeekOrigin.Begin);
158 //用于记录处理进度
159 long total_Length = inStream.Length - inStream_Seek;
160 long totalread_Length = 0;
161 //初始化报告进度
162 progress.Report(0);
163
164 using (Aes aes = Aes.Create())
165 {
166 //设定密钥和向量
167 (aes.Key, aes.IV) = GenerateKeyAndIV(password);
168 //创建加密器解密器对象(加解密方法不同处仅仅这一句话)
169 ICryptoTransform cryptor;
170 if (direction == ActionDirection.EnCrypt)
171 cryptor = aes.CreateEncryptor(aes.Key, aes.IV);
172 else
173 cryptor = aes.CreateDecryptor(aes.Key, aes.IV);
174 using (CryptoStream cstream = new CryptoStream(outStream, cryptor, CryptoStreamMode.Write))
175 {
176 byte[] buffer = new byte[512 * 1024];//每次读取512kb的数据
177 int readLen = 0;
178 while ((readLen = inStream.Read(buffer, 0, buffer.Length)) != 0)
179 {
180 // 向加密流写入数据
181 cstream.Write(buffer, 0, readLen);
182 totalread_Length += readLen;
183 //汇报处理进度
184 if (progress != null)
185 {
186 long per = 100 * totalread_Length / total_Length;
187 progress.Report(Convert.ToInt32(per));
188 }
189 }
190 }
191 }
192 }
193 }
194 }
AES字节数组加密解密流程的更多相关文章
- golang AES/ECB/PKCS5 加密解密 url-safe-base64
因为项目的需要用到golang的一种特殊的加密解密算法AES/ECB/PKCS5,但是算法并没有包含在标准库中,经过多次失败的尝试,终于解码成功,特此分享: /* 描述 : golang AES/EC ...
- 转 关于Https协议中的ssl加密解密流程
关于Https协议中的ssl加密解密流程 2016年09月28日 09:51:15 阅读数:14809 转载自:http://www.cnblogs.com/P_Chou/archive/2010/1 ...
- 微信小程序aes前后端加密解密交互
aes前后端加密解密交互 小程序端 1. 首先引入aes.js /** * [description] CryptoJS v3.1.2 * [description] zhuangzhudada so ...
- Crypto++ AES 加密解密流程
// aesdemo.cpp : 定义控制台应用程序的入口点. // #include <stdio.h>#include <tchar.h>#include <iost ...
- 自己写的AES和RSA加密解密工具
package com.sdyy.common.utils; import java.security.Key; import java.security.KeyFactory; import jav ...
- Java使用AES算法进行加密解密
一.加密 /** * 加密 * @param src 源数据字节数组 * @param key 密钥字节数组 * @return 加密后的字节数组 */ public static byte[] En ...
- NET实现RSA AES DES 字符串 加密解密以及SHA1 MD5加密
本文列举了 数据加密算法(Data Encryption Algorithm,DEA) 密码学中的高级加密标准(Advanced EncryptionStandard,AES)RSA公钥加密算法 ...
- python AES 双向对称加密解密
高级加密标准(Advanced Encryption Standard,AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准.这个标准用来替代原先的DES,已经被多方分 ...
- openssl对数组加密解密的完整实现代码
本例是用C实现的对一个数组进行加密,加密到第二个数组,然后解密到另一个数组的完整实现代码. #include <stdio.h> #include <string.h> #in ...
随机推荐
- C#LeetCode刷题之#122-买卖股票的最佳时机 II(Best Time to Buy and Sell Stock II)
问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/4032 访问. 给定一个数组,它的第 i 个元素是一支给定股票第 ...
- Css 图片自适应
设置 CSS .container{ overflow:auto; } img{ width:100%; height:auto; overflow:hidden; } 设置 template < ...
- ES6语法学习(一)-let和const
1.let 和 const 变量提升: 在声明变量或者函数时,被声明的变量和函数会被提升到函数最顶部: 但是如果声明的变量或者函数被初始化了,则会失去变量提升: 示例代码: param2 = &quo ...
- NumPy速查笔记(持续更新中)
目录 1 总览 2 ndarray 3 常用API 3.1 创建ndarray (1)将Python类似数组的对象转化成Numpy数组 (2)numpy内置的数组创建 (3)从磁盘中读取标准格式或者自 ...
- 树上的等差数列 [树形dp]
树上的等差数列 题目描述 给定一棵包含 \(N\) 个节点的无根树,节点编号 \(1\to N\) .其中每个节点都具有一个权值,第 \(i\) 个节点的权值是 \(A_i\) . 小 \(Hi\) ...
- 【WC2013】 糖果公园 - 树上莫队
问题描述 Candyland 有一座糖果公园,公园里不仅有美丽的风景.好玩的游乐项目,还有许多免费糖果的发放点,这引来了许多贪吃的小朋友来糖果公园游玩.糖果公园的结构十分奇特,它由 n 个游览点构成, ...
- 虚拟化技术之kvm管理工具virsh常用基础命令(一)
在上一篇博客中,我们了解了KVM基础架构和部署以及图形管理工具virt-manager安装虚拟机的过程,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/13499 ...
- Pycharm 2020.01亲测激活到2089年
Pycharm 2020.01亲测激活到2089年,注意是2020.01版本. 下载jetbrains-agent.jar 链接: https://pan.baidu.com/s/1w1i15pT38 ...
- C++ Templates(1.3 多模板参数 Multiple Template Parameters)
返回完整目录 目录 1.3 多模板参数 Multiple Template Parameters 1.3.1 为返回类型设置模板参数参数 Template Parameters for Return ...
- 「完整案例」基于Socket开发TCP传输客户端
1 程序界面设计 TCP客户端在上位机开发中应用很广,大多数情况下,上位机软件都是作为一个TCP客户端来与PLC或其他服务器进行通信的.TCP客户端的主要功能就是连接服务器.发送数据.接收数据.断开 ...