引子

最近想研究STM32的 BootLoader 实现IAP升级,所以就有hex转bin且加密方面研究
这里主要是根据文章 **“STM32 IAP升级AES256加密 应用及源码” ** 来验证加密功能
最开始是看到 IAP+YMODEM+CRC16+AES256+PC端软件+hex合并 这里讲述的内容查询到方法
只是在这边加密步骤发现有些不一样,他们编译的 AES256.DLL 动态文件是 win32 ,而自己平台是 X64,简单调整一下即可!

AES算法详解

一、什么是AES?

AES(Advanced Encryption Standard)是一种对称加密算法,广泛应用于数据加密。它在2001年被美国国家标准与技术研究所(NIST)选为数据加密的标准,替代了之前的DES(Data Encryption Standard)。AES具有高效、安全和灵活的特点,适用于多种平台和环境。

二、AES的工作原理

AES是一种对称密钥加密算法,这意味着加密和解密使用同一个密钥。AES算法可以处理128位的块大小,支持128、192和256位的密钥长度。

  1. 加密过程
    AES的加密过程包括多个步骤,主要分为以下几个阶段:

  2. 解密过程
    解密过程与加密过程相似,但步骤的顺序和操作的内容会有所不同,具体步骤为:

三、AES的安全性

AES被认为是非常安全的加密标准,主要原因如下:

  1. 密钥长度:AES支持多种密钥长度,256位的密钥提供了极高的安全性。
  2. 抵抗力:AES设计抵抗多种攻击(如暴力破解、差分攻击、线性攻击等)。
  3. 标准化:作为NIST标准,AES得到了广泛的审查和验证。

四、AES的应用

AES被广泛应用于各种领域,包括但不限于:

  • 数据保护:用于保护敏感数据,如金融信息、个人身份信息等。
  • VPN和TLS:在虚拟私人网络和传输层安全(TLS)协议中用于数据加密。
  • 文件加密:用于加密文件和存储设备,保护数据隐私。
  • 电子商务:在在线支付和交易中保障数据安全。

语言实现(后边的文章使用了C++自己实现-这里基本使用库实现)

Go语言实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package main

import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"fmt"
"io"
)

// AES加密
func encrypt(plainText, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}

// 填充明文
plainText = pad(plainText)
cipherText := make([]byte, len(plainText))

iv := make([]byte, aes.BlockSize)
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return nil, err
}

mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(cipherText, plainText)

// 返回IV + 密文
return append(iv, cipherText...), nil
}

// AES解密
func decrypt(cipherText, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}

// 获取IV
if len(cipherText) < aes.BlockSize {
return nil, fmt.Errorf("ciphertext too short")
}
iv := cipherText[:aes.BlockSize]
cipherText = cipherText[aes.BlockSize:]

mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(cipherText, cipherText)

// 去除填充
return unpad(cipherText), nil
}

// 填充
func pad(src []byte) []byte {
padding := aes.BlockSize - len(src)%aes.BlockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(src, padtext...)
}

// 去除填充
func unpad(src []byte) []byte {
length := len(src)
unpadding := int(src[length-1])
return src[:(length - unpadding)]
}

func main() {
key := []byte("thisisaverystrongkey1234") // 32字节密钥
plainText := []byte("Hello, AES encryption!")

// 加密
cipherText, err := encrypt(plainText, key)
if err != nil {
fmt.Println("Encryption error:", err)
return
}
fmt.Println("Cipher Text:", hex.EncodeToString(cipherText))

// 解密
decryptedText, err := decrypt(cipherText, key)
if err != nil {
fmt.Println("Decryption error:", err)
return
}
fmt.Println("Decrypted Text:", string(decryptedText))
}

Python实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import os
import binascii

def encrypt(plain_text, key):
cipher = AES.new(key, AES.MODE_CBC)
iv = cipher.iv
cipher_text = cipher.encrypt(pad(plain_text.encode(), AES.block_size))
return iv + cipher_text

def decrypt(cipher_text, key):
iv = cipher_text[:AES.block_size]
cipher = AES.new(key, AES.MODE_CBC, iv)
decrypted_text = unpad(cipher.decrypt(cipher_text[AES.block_size:]), AES.block_size)
return decrypted_text.decode()

if __name__ == "__main__":
key = b'thisisaverystrong' # 16字节密钥
plain_text = "Hello, AES encryption!"

# 加密
cipher_text = encrypt(plain_text, key)
print("Cipher Text:", binascii.hexlify(cipher_text).decode())

# 解密
decrypted_text = decrypt(cipher_text, key)
print("Decrypted Text:", decrypted_text)

Java实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Arrays;

public class AESExample {
private static final String ALGORITHM = "AES";
private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";

public static byte[] encrypt(String plainText, byte[] key) throws Exception {
SecretKeySpec secretKey = new SecretKeySpec(key, ALGORITHM);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
byte[] iv = new byte[cipher.getBlockSize()];
System.arraycopy(key, 0, iv, 0, iv.length);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));
return cipher.doFinal(plainText.getBytes());
}

public static String decrypt(byte[] cipherText, byte[] key) throws Exception {
SecretKeySpec secretKey = new SecretKeySpec(key, ALGORITHM);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
byte[] iv = new byte[cipher.getBlockSize()];
System.arraycopy(key, 0, iv, 0, iv.length);
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
return new String(cipher.doFinal(cipherText));
}

public static void main(String[] args) {
try {
byte[] key = "thisisaverystrong".getBytes(); // 16字节密钥
String plainText = "Hello, AES encryption!";

// 加密
byte[] cipherText = encrypt(plainText, key);
System.out.println("Cipher Text: " + Arrays.toString(cipherText));

// 解密
String decryptedText = decrypt(cipherText, key);
System.out.println("Decrypted Text: " + decryptedText);
} catch (Exception e) {
e.printStackTrace();
}
}
}

STM32 IAP升级AES256加密 应用及源码

我用的万利199元的板。
AES256加密算法,用的是shaoziyang的源码,由于不会用C++,上位机用了C#开发,搞了一个多星期,C#加密出来的,老是和shaoziyang的AVRUB加密出来的不一样,就干脆用C++做了DLL,用C#调用。
不多说了,直接上源码。

上位机效果图

STM32下位机IAP升级里面,我把固件上传部分删掉了,既然加密,就是为了防破_解,读出来就没必要了。

C++里的DLL加密文件包
点击此处下载 ourdev_678831FMG4A4.zip(文件大小:11K) (原文件名:DLLTest.zip)

C#写的调用DLL加密的HEX转Bin文件包
点击此处下载 ourdev_678832NPWI4U.zip(文件大小:45K) (原文件名:Hex2Bin.zip)

MDK下的万利199元开发板的IAP源码
点击此处下载 ourdev_678833DAXD3A.zip(文件大小:521K) (原文件名:Usart.zip)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
好像有问题把?

这是你写的程序:
/*----------------------------------------------------------------------------------------------*/
BufferIn=buf;
for (j = 0; j < packet_length; j += 16)
{
//解密数据包
aesDecrypt(BufferIn,bufferOut);
BufferIn+=16;
}
/*----------------------------------------------------------------------------------------------*/
RamSource = (uint32_t)buf;

你最后的赋值又是没解密的内容啊?

是不是应该是RamSource = (uint32_t)bufferOut;

没搞过AES,你的函数也没什么仔细的说明,求解?

谢谢指导。


楼主| 发表于 2011-9-23 11:13:25 | 只看该作者
这个源码是从shaoziyang的AVR单片机通用BootLoader里提取出来的。

当时我也是你这种理解,参数1为输入,参数2为输出。但实际不是的,解密后的数据,实际还是在参数1的位置。

你仔细试试就知道了。

忘记说明了,加密Key设置如图。
Key字符串可用空格分隔,也可以不用。

注意: 最后使用的是

1
9D D2 00 24 84 60 2E DA 0C DD 52 7B 05 C1 6B 01 FF 17 CD 6F 8C 1E 3E 09 CF 1F 0C 78 87 EF 8A EC

编译出现的问题–调用DLL时 报“ 试图加载格式不正确的程序“

运行时报“试图加载格式不正确的程序。 (0x8007000B)”

主要是因为在 x64 架构上运行了 win32 的DLL导致

C#调用dll提示”试图加载格式不正确的程序”原因及解决方法

程序在32位操作系统上运行正常,在64位操作系统上运行读卡功能提示”试图加载格式不正确“。

出错原因:因为’任何 CPU’编译运行的程序在64位的机器上就会用运行为64位,而64位程序是不能加载32位dll的

解决方法:项目右键属性->项目设计器->生成->平台->把’默认设置(任何 CPU)’改为x86。

当将 DLL 库程序设置成 X86 之后编译会报错,主要还是因为使用了 C 语言编写

报错:

1
1>LINK : fatal error LNK1561: 必须定义入口点

解决64位dll“error LNK1561: 必须定义入口点”

在很多的时候需要生成一个64位的dll,但是明显在32位平台编译正确的代码,改成64位系统就不准确了,报“error LNK1561: 必须定义入口点”的错误。

  • 一、右键项目–>属性
  • 二、选择“常规”–>“配置类型”
  • 三、选择“动态连接库”
  • 四、重新生成。右键项目“重新生成”
  • 五、然后到项目所在单位的win64目录下就可以找到生成的dll文件。

最终效果

根据文章得到了加密的 bin 文件了

C#中实现AES算法加密解读(直接使用库方式)

效果图

文件和加密文件之间的转换。

添加辅助类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
public class AES_EnorDecrypt
{
//定义默认密钥
private static byte[] _aesKeyByte = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF, 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF };
private static string _aesKeyStr = Encoding.UTF8.GetString(_aesKeyByte);

/// <summary>
/// 随机生成密钥,默认密钥长度为32,不足在加密时自动填充空格
/// </summary>
/// <param name="n">密钥长度</param>
/// <returns></returns>
public static string GetIv(int n)
{
string s = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char[] arrChar = new char[s.Length];
for (int i = 0; i < s.Length; i++)
{
arrChar[i] = Convert.ToChar(s.Substring(i, 1));
}
StringBuilder num = new StringBuilder();
Random rnd = new Random(DateTime.Now.Millisecond);
for (int i = 0; i < n; i++)
{
num.Append(arrChar[rnd.Next(0, arrChar.Length)].ToString());
}
_aesKeyByte = Encoding.UTF8.GetBytes(num.ToString());
return _aesKeyStr = Encoding.UTF8.GetString(_aesKeyByte);
}

/// <summary>
/// AES加密,针对文本类文件
/// </summary>
/// <param name="Data">被加密的明文</param>
/// <param name="Key">密钥</param>
/// <param name="Vector">密钥向量</param>
/// <returns>密文</returns>
public static string AESEncrypt(string Data, string Key, string Vector)
{
byte[] plainBytes = Encoding.UTF8.GetBytes(Data);
byte[] bKey = new byte[32];
Array.Copy(Encoding.UTF8.GetBytes(Key.PadRight(bKey.Length)), bKey, bKey.Length);
byte[] bVector = new byte[16];
Array.Copy(Encoding.UTF8.GetBytes(Vector.PadRight(bVector.Length)), bVector, bVector.Length);
byte[] Cryptograph = null;//加密后的密文
Rijndael Aes = Rijndael.Create();
try
{
using (MemoryStream Memory = new MemoryStream())
{
//把内存流对象包装成加密流对象
using (CryptoStream Encryptor = new CryptoStream(Memory, Aes.CreateEncryptor(bKey, bVector), CryptoStreamMode.Write))
{
Encryptor.Write(plainBytes, 0, plainBytes.Length);
Encryptor.FlushFinalBlock();
Cryptograph = Memory.ToArray();
}
}
}
catch
{
Cryptograph = null;
}
return Convert.ToBase64String(Cryptograph);
}

/// <summary>
/// AES加密,任意文件
/// </summary>
/// <param name="Data">被加密的明文</param>
/// <param name="Key">密钥</param>
/// <param name="Vector">密钥向量</param>
/// <returns>密文</returns>
public static byte[] AESEncrypt(byte[] Data, string Key, string Vector)
{
byte[] bKey = new byte[32];
Array.Copy(Encoding.UTF8.GetBytes(Key.PadRight(bKey.Length)), bKey, bKey.Length);
byte[] bVector = new byte[16];
Array.Copy(Encoding.UTF8.GetBytes(Vector.PadRight(bVector.Length)), bVector, bVector.Length);
byte[] Cryptograph = null;//加密后的密文
Rijndael Aes = Rijndael.Create();
try
{
using (MemoryStream Memory = new MemoryStream())
{
//把内存流对象包装成加密流对象
using (CryptoStream Encryptor = new CryptoStream(Memory, Aes.CreateEncryptor(bKey, bVector), CryptoStreamMode.Write))
{
Encryptor.Write(Data, 0, Data.Length);
Encryptor.FlushFinalBlock();
Cryptograph = Memory.ToArray();
}
}
}
catch
{
Cryptograph = null;
}
return Cryptograph;
}

/// <summary>
/// AES解密,针对文本文件
/// </summary>
/// <param name="Data">被解密的密文</param>
/// <param name="Key">密钥</param>
/// <param name="Vector">密钥向量</param>
/// <returns>明文</returns>
public static string AESDecrypt(string Data, string Key, string Vector)
{
byte[] encryptedBytes = Convert.FromBase64String(Data);
byte[] bKey = new byte[32];
Array.Copy(Encoding.UTF8.GetBytes(Key.PadRight(bKey.Length)), bKey, bKey.Length);
byte[] bVector = new byte[16];
Array.Copy(Encoding.UTF8.GetBytes(Vector.PadRight(bVector.Length)), bVector, bVector.Length);
byte[] original = null;//解密后的明文
Rijndael Aes = Rijndael.Create();
try
{
using (MemoryStream Memory = new MemoryStream(encryptedBytes))
{
//把内存流对象包装成加密对象
using (CryptoStream Decryptor = new CryptoStream(Memory, Aes.CreateDecryptor(bKey, bVector), CryptoStreamMode.Read))
{
//明文存储区
using (MemoryStream originalMemory = new MemoryStream())
{
byte[] Buffer = new byte[1024];
int readBytes = 0;
while ((readBytes = Decryptor.Read(Buffer, 0, Buffer.Length)) > 0)
{
originalMemory.Write(Buffer, 0, readBytes);
}
original = originalMemory.ToArray();
}
}
}
}
catch
{
original = null;
}
return Encoding.UTF8.GetString(original);
}

/// <summary>
/// AES解密,任意文件
/// </summary>
/// <param name="Data">被解密的密文</param>
/// <param name="Key">密钥</param>
/// <param name="Vector">密钥向量</param>
/// <returns>明文</returns>
public static byte[] AESDecrypt(byte[] Data, string Key, string Vector)
{
byte[] bKey = new byte[32];
Array.Copy(Encoding.UTF8.GetBytes(Key.PadRight(bKey.Length)), bKey, bKey.Length);
byte[] bVector = new byte[16];
Array.Copy(Encoding.UTF8.GetBytes(Vector.PadRight(bVector.Length)), bVector, bVector.Length);
byte[] original = null;//解密后的明文
Rijndael Aes = Rijndael.Create();
try
{
using (MemoryStream Memory = new MemoryStream(Data))
{
//把内存流对象包装成加密对象
using (CryptoStream Decryptor = new CryptoStream(Memory, Aes.CreateDecryptor(bKey, bVector), CryptoStreamMode.Read))
{
//明文存储区
using (MemoryStream originalMemory = new MemoryStream())
{
byte[] Buffer = new byte[1024];
int readBytes = 0;
while ((readBytes = Decryptor.Read(Buffer, 0, Buffer.Length)) > 0)
{
originalMemory.Write(Buffer, 0, readBytes);
}
original = originalMemory.ToArray();
}
}
}
}
catch
{
original = null;
}
return original;
}
}

AES是块加密,块的长度是16字节,如果原文不到16的字节,就会进行填充至16个字节。

开始实现

界面布局

textbox1为文件位置,textbox2为密码。

backgroundWorker2也需要设置一样才行

设置好后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
public partial class FormAes : Form
{
#region 属性
private static string _aesKeyVector = "q2T_=R/*33vc";
#endregion

public FormAes()
{
InitializeComponent();
}

/// <summary>
/// 选择文件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void textBox1_Click(object sender, EventArgs e)
{
OpenFileDialog dialog = new OpenFileDialog();
dialog.Multiselect = true;//该值确定是否可以选择多个文件
dialog.Title = "请选择文件夹";
dialog.Filter = "所有文件(*.*)|*.*";
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
textBox1.Text = dialog.FileName;
}
}

/// <summary>
/// 加密
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(textBox1.Text.Trim()) || string.IsNullOrWhiteSpace(textBox2.Text.Trim()))
return;
backgroundWorker1.RunWorkerAsync();
}

/// <summary>
/// 解密
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button2_Click(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(textBox1.Text.Trim()) || string.IsNullOrWhiteSpace(textBox2.Text.Trim()))
return;
backgroundWorker2.RunWorkerAsync();
}


/// <summary>
/// 后台线程执行的方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{

string KeyVector = _aesKeyVector;//密钥向量
string path = Path.GetDirectoryName(textBox1.Text);
string name = Path.GetFileName(textBox1.Text);
name += ".En";
#region 加密
FileStream sr = new FileStream(textBox1.Text, FileMode.Open, FileAccess.Read);
FileStream sw = new FileStream(path + "\\" + name, FileMode.Create, FileAccess.Write);

if (sr.Length > 50 * 1024 * 1024)//如果文件大于50M,采取分块加密,按50MB读写
{
byte[] mybyte = new byte[52428800];//每50MB加密一次
int numBytesRead = 52428800;//每次加密的流大小
long leftBytes = sr.Length;//剩余需要加密的流大小
long readBytes = 0;//已经读取的流大小
//每50MB加密后会变成50MB+16B
byte[] encrpy = new byte[52428816];
while (true)
{
if (leftBytes > numBytesRead)
{
sr.Read(mybyte, 0, mybyte.Length);
encrpy = AES_EnorDecrypt.AESEncrypt(mybyte, textBox2.Text, KeyVector);
sw.Write(encrpy, 0, encrpy.Length);
leftBytes -= numBytesRead;
readBytes += numBytesRead;
backgroundWorker1.ReportProgress((int)(readBytes * 100 / sr.Length));
}
else//重新设定读取流大小,避免最后多余空值
{
byte[] newByte = new byte[leftBytes];
sr.Read(newByte, 0, newByte.Length);
byte[] newWriteByte;
newWriteByte = AES_EnorDecrypt.AESEncrypt(newByte, textBox2.Text, KeyVector);
sw.Write(newWriteByte, 0, newWriteByte.Length);
readBytes += leftBytes;
backgroundWorker1.ReportProgress((int)(readBytes * 100 / sr.Length));
break;
}
}
}
else
{
byte[] mybyte = new byte[sr.Length];
sr.Read(mybyte, 0, (int)sr.Length);
mybyte = AES_EnorDecrypt.AESEncrypt(mybyte, textBox2.Text, KeyVector);
sw.Write(mybyte, 0, mybyte.Length);
backgroundWorker1.ReportProgress(100);
}

sr.Close();
sw.Close();

#endregion
}

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}

/// <summary>
/// 执行完成时触发
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("加密成功!");
}

/// <summary>
/// 后台线程执行的方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e)
{
try
{
string KeyVector = _aesKeyVector;//密钥向量
string path = Path.GetDirectoryName(textBox1.Text);
string name = Path.GetFileName(textBox1.Text);
if (name.EndsWith(".En"))
{
name = name.Remove(name.Length - 3, 3);
}

#region 解密
FileStream sr = new FileStream(textBox1.Text, FileMode.Open, FileAccess.Read);
FileStream sw = new FileStream(path + "\\" + name, FileMode.Create, FileAccess.Write);

if (sr.Length > 50 * 1024 * 1024)//如果文件大于50M,采取分块解密,按50MB读写
{
byte[] mybyte = new byte[52428816];//解密缓冲区50MB+16B
byte[] decrpt = new byte[52428800];//解密后的50MB
int numBytesRead = 52428816;//每次解密的流大小
long leftBytes = sr.Length;//剩余需要解密的流大小
long readBytes = 0;//已经读取的流大小
try
{
while (true)
{
if (leftBytes > numBytesRead)
{
sr.Read(mybyte, 0, mybyte.Length);
decrpt = AES_EnorDecrypt.AESDecrypt(mybyte, textBox2.Text, KeyVector);
sw.Write(decrpt, 0, decrpt.Length);
leftBytes -= numBytesRead;
readBytes += numBytesRead;
backgroundWorker2.ReportProgress((int)(readBytes * 100 / sr.Length));
}
else//重新设定读取流大小,避免最后多余空值
{
byte[] newByte = new byte[leftBytes];
sr.Read(newByte, 0, newByte.Length);
byte[] newWriteByte;
newWriteByte = AES_EnorDecrypt.AESDecrypt(newByte, textBox2.Text, KeyVector);
sw.Write(newWriteByte, 0, newWriteByte.Length);
readBytes += leftBytes;
backgroundWorker2.ReportProgress((int)(readBytes * 100 / sr.Length));
break;
}
}
}
catch
{
sr.Close();
sw.Close();
File.Delete(path + "\\" + name);
e.Cancel = true;
}
sr.Close();
sw.Close();
}
else
{
byte[] mybyte = new byte[(int)sr.Length];
sr.Read(mybyte, 0, (int)sr.Length);
try
{
mybyte = AES_EnorDecrypt.AESDecrypt(mybyte, textBox2.Text, KeyVector);
sw.Write(mybyte, 0, mybyte.Length);
backgroundWorker2.ReportProgress(100);
}
catch
{
sr.Close();
sw.Close();
File.Delete(path + "\\" + name);
e.Cancel = true;
}
sr.Close();
sw.Close();
}
#endregion
}
catch
{
e.Cancel = true;
}
}

private void backgroundWorker2_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}

/// <summary>
/// 执行完成时触发
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void backgroundWorker2_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
MessageBox.Show("解密失败\n密码错误或加密文件被篡改,无法解密");
}
else
{
MessageBox.Show("解密成功!");
}
}
}

用C#实现AES加密和解密(库引用-没测试过)

AES 是一个新的可以用于保护电子数据的加密算法。明确地说,AES 是一个迭代的、对称密钥分组的密码,它可以使用128、192 和 256 位密钥,并且用 128 位(16字节)分组加密和解密数据。与公共密钥密码使用密钥对不同,对称密钥密码使用相同的密钥加密和解密数据。通过分组密码返回的加密数据 的位数与输入数据相同。迭代加密使用一个循环结构,在该循环中重复置换(permutations )和替换(substitutions)输入数据。

需要加入引用 using System.Security.Cryptography;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
/// <summary>
/// 有密码的AES加密
/// </summary>
/// <param name="text">加密字符</param>
/// <param name="password">加密的密码</param>
/// <param name="iv">密钥</param>
/// <returns></returns>
public static string AESEncrypt(string text, string password, string iv)
{
RijndaelManaged rijndaelCipher = new RijndaelManaged();

rijndaelCipher.Mode = CipherMode.CBC;

rijndaelCipher.Padding = PaddingMode.PKCS7;

rijndaelCipher.KeySize = 128;

rijndaelCipher.BlockSize = 128;

byte[] pwdBytes = System.Text.Encoding.UTF8.GetBytes(password);

byte[] keyBytes = new byte[16];

int len = pwdBytes.Length;

if (len > keyBytes.Length) len = keyBytes.Length;

System.Array.Copy(pwdBytes, keyBytes, len);

rijndaelCipher.Key = keyBytes;


byte[] ivBytes = System.Text.Encoding.UTF8.GetBytes(iv);
rijndaelCipher.IV = ivBytes;

ICryptoTransform transform = rijndaelCipher.CreateEncryptor();

byte[] plainText = Encoding.UTF8.GetBytes(text);

byte[] cipherBytes = transform.TransformFinalBlock(plainText, 0, plainText.Length);

return Convert.ToBase64String(cipherBytes);

}

/// <summary>
/// 随机生成密钥
/// </summary>
/// <returns></returns>
public static string GetIv(int n)
{
char[] arrChar = new char[]{
'a','b','d','c','e','f','g','h','i','j','k','l','m','n','p','r','q','s','t','u','v','w','z','y','x',
'0','1','2','3','4','5','6','7','8','9',
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','Q','P','R','T','S','V','U','W','X','Y','Z'
};

StringBuilder num = new StringBuilder();

Random rnd = new Random(DateTime.Now.Millisecond);
for (int i = 0; i < n; i++)
{
num.Append(arrChar[rnd.Next(0, arrChar.Length)].ToString());

}

return num.ToString();
}

/// <summary>
/// AES解密
/// </summary>
/// <param name="text"></param>
/// <param name="password"></param>
/// <param name="iv"></param>
/// <returns></returns>
public static string AESDecrypt(string text, string password, string iv)
{
RijndaelManaged rijndaelCipher = new RijndaelManaged();

rijndaelCipher.Mode = CipherMode.CBC;

rijndaelCipher.Padding = PaddingMode.PKCS7;

rijndaelCipher.KeySize = 128;

rijndaelCipher.BlockSize = 128;

byte[] encryptedData = Convert.FromBase64String(text);

byte[] pwdBytes = System.Text.Encoding.UTF8.GetBytes(password);

byte[] keyBytes = new byte[16];

int len = pwdBytes.Length;

if (len > keyBytes.Length) len = keyBytes.Length;

System.Array.Copy(pwdBytes, keyBytes, len);

rijndaelCipher.Key = keyBytes;

byte[] ivBytes = System.Text.Encoding.UTF8.GetBytes(iv);
rijndaelCipher.IV = ivBytes;

ICryptoTransform transform = rijndaelCipher.CreateDecryptor();

byte[] plainText = transform.TransformFinalBlock(encryptedData, 0, encryptedData.Length);

return Encoding.UTF8.GetString(plainText);

}

AES加密算法详解(图文解释)

由于DES加密算法被破解了,3DES加密算法虽然没有被破解,但是3DES算法的加解密效率低,所有现在都使用AES算法。
AES加密算法是密码学中的高级加密标准,AES为分组加密法,把明文分成一组一组的,每组长度相等,每次加密一组数据,直到加密完整个明文,在AES标准规范中,分组长度只能是128位,AES是按照字节进行加密的,也就是说每个分组为16个字节(每个字节8位)。密钥的长度可以使用128位、192位或256位。这导致密钥长度不同,推荐加密的轮数也不同。

AES算法思想:
1)设计简单;
2)在多个平台上速度快,编码紧凑;
3)抵抗所有已知攻击;
4)没有采用Feistel结构,轮函数由3个不同的可逆均匀变换构成:非线性层、线性混合层和密钥加层。

明文P需要分组,称为状态,状态用以字节为元素的矩阵阵列表示,如图所示:

16字节明文按照此顺序放入矩阵,规则为:从上到下,从左到右,依次进行。

同样的,密钥K也需要分组,原理与明文P相同,下图为128位密钥矩阵:

下图为AES加密流程:

加密过程:C=E(K,P),其中C为加密后的密文,K为密钥,P为明文,E为加密函数。
解密过程:P=D(K,P),其中D为解密函数,解密过程为加密过程的逆运算。
AES的具体加密流程如下图所示:

AES加密过程通过四个步骤实现:字节替换、行移位、列混淆和轮密钥加。
注意: 根据密钥长度不同,加密的轮数也不同,在第一轮之前要进行轮密钥加,最后一轮没有进行列混淆操作。

我们来解释一下这四个步骤的具体含义:
字节替换: 属于非线性替换,具体原理就是通过一个替换表(S盒)对每个字节进行替换,实际上就是一个查表操作,并且此过程可逆,将每一个字节的前4位作为行值,后4位作为列值,去S盒查找,进行输出。

下图为S盒(x表示行,y表示列),例如字节为0x14,那么前四位的16进制为1,后四位的16进制为4,去查找s盒中的第1行第4列的值,可以看出为0xfa,就把原先的字节0x14替换为0xfa。

解密过程与此相同,唯一就是采用的是逆S盒。

接下来是行位移操作
对于4*4的矩阵,操作为:
第0行:保持不动;
第1行:循环左移1个字节;
第2行:循环左移2个字节;
第3行:循环左移3个字节。

解密过程变为循环右移,每行移动字节数与加密过程相同,下图为列位移示意图。

接下来是列混淆操作
实际上为44的矩阵与另一个44矩阵异或相乘(注意为右乘操作),重新得到一个4*4的矩阵,如下图所示。
解密过程为重新与此矩阵异或,因为两次异或得到的值为原数据本身。

最后为轮密钥加操作
轮密钥与状态矩阵进行逐比特异或操作。
这个轮密钥是由种子密钥通过密钥编排算法得到的,并且轮密钥长度与分组长度相同。
解密过程与之相同,两次异或得到原始数据。

密钥编排算法基本原则
1)轮密钥的比特数等于分组长度乘以轮数加1;
例如:将128位比特明文进行加密,总共需要(10+1)*128=1408比特密钥。
2)种子密钥扩展为扩展密钥;
3)轮密钥从扩展密钥中取,第一轮取扩展密钥的第0~3列,依次类推。

过程为:
定义:w[0]~w[3]为初始密钥

如果i=4的倍数,即i为每组的第一列,则执行以下3个步骤
1)将w[i-1]循环左移一个字节:

w[0] w[1] w[2] w[3] w[4]
2b 28 ab 09 cf
7e ae f7 cf 4f
15 d2 15 4f 3c
16 a6 88 3c 09

w’[i]=w[i-1]左移一个字节得到。
w’[4]=w[3]左移一个字节得到={09,cf,4f,3c}左移一个字节={cf,4f,3c,09}

2)分别对w’[i]的每个字节进行S盒替换,本质上就是查表,再替换为另一个字节。
即查表后为w’‘[i]。
例如,w’[4]查表后的w’’[4]={8a,84,eb,01},需s盒的自行查看。

3)将前两步的结果同轮常量Rcon[j]进行异或,j表示轮数,Rcon[j]如下图所示

j 1 2 3 4 5
Rcon[j] 01 00 00 00 02 00 00 00 04 00 00 00 08 00 00 00 10 00 00 00
j 6 7 8 9 10
Rcon[j] 20 00 00 00 40 00 00 00 80 00 00 00 1B 00 00 00 36 00 00 00

那么w[i]=w[i-4]⊕w’[i]⊕Rcon[j],此时j=1,因为为第一轮。
即w[4]=w[0]⊕w’‘[4]⊕Rcon[1],
那么w[8]=w[4]⊕w’’[8]⊕Rcon[2],……

如果i≠4的倍数,即i为每组的第二、三、四列,则执行以下一个步骤
即w[i]=w[i-4]⊕w[i-1]
例如w[5]=w[1]⊕w[4],w[6]=w[2]⊕[5],……

最终的到一个扩展密钥:{w[4],w[5],w[6],w[7]},之后的每一轮密钥都是在前一轮基础上形成的。

可能说的不是很明白,后续将会补充。


相关链接(侵删)

  1. STM32 IAP升级AES256加密 应用及源码
  2. 【算法】【AES】加密算法详解
  3. C#调用dll提示”试图加载格式不正确的程序”原因及解决方法
  4. 解决 C# 调用DLL时 报“ 试图加载格式不正确的程序“(亲自验证)
  5. 解决64位dll“error LNK1561: 必须定义入口点”
  6. 1>LINK : fatal error LNK1561: 必须定义入口点
  7. C#中实现AES算法加密解读
  8. 用C#实现AES加密和解密
  9. AES加密算法详解(图文解释)

=================我是分割线=================

欢迎到公众号来唠嗑: