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 ...
随机推荐
- 使用 VMware Workstation Pro 让 PC 提供云桌面服务——学习笔记(一)
最终效果: 能够通过xshell等终端设备, 远程访问虚拟机 操作步骤 1. 安装VMware 网上自行下载VMware 软件,这里不给出详细操作 2. 下载安装系统 这里使用 Centos 的lin ...
- 洛谷P1149.火柴棒等式(暴力搜索)
题目描述 给你n根火柴棍,你可以拼出多少个形如"A+B=C"的等式?等式中的A.B.C是用火柴棍拼出的整数(若该数非零,则最高位不能是0).用火柴棍拼数字0-9的拼法如图所示: 注 ...
- STM32 重启之后程序丢失
1 BOOT1 BOOT0都已经接10K接地,晶振波形正常 2 在主程序最开始运行的地方加入5秒的延时,程序不会丢失.原因可能为单片机其它外设没有准备好 int main(void) { delay_ ...
- redis读写分离及可用性设计
Redis缓存架构设计 对于下面两个架构图,有如下想法: 1)redis主从复制模式,为了解决master读写压力,对master进行写操作,对slave进行读操作. 2)而在分片集群中,如果对部分分 ...
- 封装Vue Element的table表格组件
上周分享了几篇关于React组件封装方面的博文,这周就来分享几篇关于Vue组件封装方面的博文,也好让大家能更好地了解React和Vue在组件封装方面的区别. 在封装Vue组件时,我依旧会交叉使用函数式 ...
- 运行SQL文件报错:Got a packet bigger than 'max_allowed_packet' bytes With statement:
英文意思:需要使用一个和现在相比较大的空间,可能mysql中的默认空间比文件需要的空间要小 解决方法: 1.修改配置文件中mysql的默认空间大小:在MYSQL的配置文件 my.in ...
- cni-ipam-etcd demo
链接:https://github.com/jeremyxu2010/cni-ipam-etcd 测试demo: package main import ( "fmt" " ...
- Java数据结构——红黑树
红黑树介绍红黑树(Red-Black Tree),它一种特殊的二叉查找树.执行查找.插入.删除等操作的时间复杂度为O(logn). 红黑树是特殊的二叉查找树,意味着它满足二叉查找树的特征:任意一个节点 ...
- Mac 系统安装robot framework
1.安装Python3 版本 2.安装robotframework:pip3 install robotframework 3.安装Pypubsub:pip3 install Pypubsub 4.安 ...
- webpack 打包的具体步骤
webpack打包的方法: 方法一 创建一个src文件夹(存放自己的代码),dist文件夹(打包到此文件夹) 2编写自己的代码,在src文件夹中创建一个主模块main.js和若干个js文件,将模块js ...