目录
介绍AES加密以及代码示例
基础介绍
高级加密标准(AES,全称:Advanced Encryption Standard)是一种对称加密算法,由美国国家标准与技术研究院(NIST)于2001年发布。它取代了数据加密标准(DES),成为许多政府、企业和机构广泛使用的标准加密方法。AES的主要特点如下:
一、特点
-
对称加密:AES是一种对称密钥算法,这意味着加密和解密使用的是同一把密钥。
-
块加密:它是一种块加密算法,将数据分割成固定大小的块进行处理。每个数据块都是128位(16字节)。
-
可变的密钥长度:AES支持128位、192位和256位的密钥长度。密钥长度越长,安全性越高,但处理的复杂性和计算开销也随之增加。
-
结构:AES基于替换-置换网络结构,经过多次重复的轮次进行加密。每一轮包含多种操作,如字节替换、行移位、列混淆和轮密钥加法等。
-
轮次数:加密过程的轮次取决于密钥长度,128位密钥需要10轮,192位密钥需要12轮,256位密钥需要14轮。
-
安全性:AES经过多年的严格审查,被认为是目前最安全和有效的加密标准之一。其设计使其对多种已知攻击方式具有很好的抵抗能力。
AES适用于各种需要加密的场合,如互联网通信、无线网络、云存储、数据库保护等。
二、原理
AES(高级加密标准)是一种分组加密算法,基于替代-置换网络原理。它通过多轮的数学运算,将明文转换为密文。以下是AES的主要原理:
-
初始密钥扩展:密钥扩展算法将用户输入的密钥扩展成多个轮密钥,供每一轮加密和解密操作使用。
-
分组与轮次数:AES以固定长度128位(16字节)的分组为单位处理数据。根据密钥长度不同,AES的轮数分别是:
- 10轮:128位密钥
- 12轮:192位密钥
- 14轮:256位密钥
-
初始轮密钥加:在加密的第一步,原始的明文分组与第一个轮密钥进行异或操作(AddRoundKey),以打乱原始数据。
-
主要轮次操作:每轮的操作包含四个主要步骤:
- 字节代替(SubBytes):使用一个预计算的S盒(替换盒)替换每个字节,将每个输入字节映射到另一个字节,从而增加混淆性。
- 行移位(ShiftRows):将数据块中每一行的数据循环移位,行号越高移动的位数越多,从而进一步混淆数据。
- 列混合(MixColumns):将每一列作为一个多项式,在一个预定义的有限域上与一个固定的多项式相乘,进一步打乱数据。此操作不适用于最后一轮加密。
- 轮密钥加(AddRoundKey):将当前轮次的密钥与经过前面操作处理的数据分组进行异或运算。
-
最后一轮:与之前的轮次类似,但不进行列混合操作,以简化最后一轮处理。
经过所有轮次操作后,最终的输出即为密文。解密过程则反向执行上述步骤,使用逆向的S盒和列混合矩阵。
具体过程可以看:
https://www.cnblogs.com/ffy11/p/16882128.html
代码示例
Crypto++库
Crypto++ Library 8.9 | Free C++ Class Library of Cryptographic Schemes
#include <iostream>
#include <cryptopp/aes.h>
#include <cryptopp/modes.h>
#include <cryptopp/filters.h>
int main() {
using namespace CryptoPP;
// 设置密钥和初始化向量,AES的密钥长度可以是16、24或32字节。
byte key[AES::DEFAULT_KEYLENGTH], iv[AES::BLOCKSIZE];
memset(key, 0x01, AES::DEFAULT_KEYLENGTH);
memset(iv, 0x01, AES::BLOCKSIZE);
// 明文字符串
std::string plain = "Hello, World!";
std::string cipher, recovered;
// 加密
try {
CBC_Mode< AES >::Encryption encryptor;
encryptor.SetKeyWithIV(key, sizeof(key), iv);
// 使用StringSink和StreamTransformationFilter来处理加密
StringSource(plain, true,
new StreamTransformationFilter(encryptor,
new StringSink(cipher)
)
);
} catch(const CryptoPP::Exception& e) {
std::cerr << e.what() << std::endl;
exit(1);
}
// 解密
try {
CBC_Mode< AES >::Decryption decryptor;
decryptor.SetKeyWithIV(key, sizeof(key), iv);
// 使用StringSink和StreamTransformationFilter来处理解密
StringSource(cipher, true,
new StreamTransformationFilter(decryptor,
new StringSink(recovered)
)
);
} catch(const CryptoPP::Exception& e) {
std::cerr << e.what() << std::endl;
exit(1);
}
// 输出结果
std::cout << "Plain Text: " << plain << std::endl;
std::cout << "Cipher Text: " << cipher << std::endl;
std::cout << "Recovered Text: " << recovered << std::endl;
return 0;
}
在给出的C++示例代码中,密钥(key)和初始化向量(iv)是在程序的开始部分定义和初始化的。这里使用的是非常简单的示例值,通常在实际应用中,你需要使用更加安全的方式来生成和存储这些密钥。以下是代码中相关部分的细节:
byte key[AES::DEFAULT_KEYLENGTH], iv[AES::BLOCKSIZE];
memset(key, 0x01, AES::DEFAULT_KEYLENGTH);
memset(iv, 0x01, AES::BLOCKSIZE);
在这段代码中:
key
是用于AES加密和解密的密钥,其长度由AES::DEFAULT_KEYLENGTH
定义,对于AES通常是16字节(128位)。iv
是初始化向量,用于块密码的工作模式,如CBC模式。它的大小是AES::BLOCKSIZE
,对于AES是16字节。
为什么需要安全的密钥管理?
在实际应用中,密钥的生成、存储和管理是非常关键的,因为密钥的安全性直接影响到加密系统的整体安全性。以下是一些密钥管理的最佳实践:
- 密钥生成:应该使用安全的随机数生成器来生成密钥。
- 密钥存储:密钥不应该硬编码在源代码中,而应该使用安全的密钥存储解决方案,例如使用硬件安全模块(HSM)、密钥管理服务(KMS)或其他加密存储方式。
- 密钥周期:密钥应该定期更换,以减少被破解的风险。
- 密钥的分发和撤销:应有安全的机制来控制密钥的分发给使用者以及在不再需要时撤销密钥。
生成密钥
以下是一个符合AES加密标准的256位(32字节)的密钥示例,以十六进制形式表示
9F74ED4BF9A45F990F9058818C559C51A2887A1C2D82B7ADC42619F908ABAC01
为了安全地生成一个密钥,最好使用一个加密安全的伪随机数生成器(CSPRNG)。Crypto++库提供了多种安全的随机数生成器可以使用。
#include <cryptopp/osrng.h> // 包含随机数生成器的头文件
#include <iostream>
int main() {
using namespace CryptoPP;
// 创建一个随机数生成器
AutoSeededRandomPool prng;
// 生成密钥
SecByteBlock key(AES::DEFAULT_KEYLENGTH);
prng.GenerateBlock(key, key.size());
// 输出密钥,以十六进制形式查看(实际使用时不应打印密钥)
std::cout << "Generated AES key: ";
for (int i = 0; i < key.size(); ++i) {
std::cout << std::hex << (int)key[i];
}
std::cout << std::endl;
return 0;
}
用秘钥加解密
#include <iostream>
#include <cryptopp/aes.h>
#include <cryptopp/modes.h>
#include <cryptopp/filters.h>
#include <cryptopp/hex.h>
#include <cryptopp/secblock.h>
int main() {
using namespace CryptoPP;
// 用于AES的256位密钥(以十六进制字符串形式表示)
std::string hexKey = "9F74ED4BF9A45F990F9058818C559C51A2887A1C2D82B7ADC42619F908ABAC01";
// 将十六进制字符串转换为字节数组
SecByteBlock key(AES::MAX_KEYLENGTH);
StringSource(hexKey, true, new HexDecoder(new ArraySink(key, key.size())));
// 初始化向量(固定16字节),此处为了简单演示使用全0
byte iv[AES::BLOCKSIZE] = {0};
// 要加密的明文
std::string plaintext = "This is a secret message that needs encryption!";
std::string ciphertext, decryptedtext;
// 加密
try {
CBC_Mode<AES>::Encryption encryption;
encryption.SetKeyWithIV(key, key.size(), iv);
StringSource(plaintext, true,
new StreamTransformationFilter(encryption,
new StringSink(ciphertext)
)
);
} catch (const Exception &e) {
std::cerr << "Encryption error: " << e.what() << std::endl;
return 1;
}
// 解密
try {
CBC_Mode<AES>::Decryption decryption;
decryption.SetKeyWithIV(key, key.size(), iv);
StringSource(ciphertext, true,
new StreamTransformationFilter(decryption,
new StringSink(decryptedtext)
)
);
} catch (const Exception &e) {
std::cerr << "Decryption error: " << e.what() << std::endl;
return 1;
}
// 输出结果
std::cout << "Plaintext: " << plaintext << std::endl;
std::cout << "Ciphertext (hex): ";
std::string hexCiphertext;
StringSource(ciphertext, true, new HexEncoder(new StringSink(hexCiphertext)));
std::cout << hexCiphertext << std::endl;
std::cout << "Decrypted text: " << decryptedtext << std::endl;
return 0;
}
Microsoft CryptoAPI
使用Microsoft CryptoAPI进行AES加密和解密涉及到Windows平台特有的API调用。下面是一个C++示例代码,展示如何使用你提供的256位AES密钥在Windows平台上使用CryptoAPI进行数据的加密和解密。
首先,确保你的项目链接了Crypt32.lib
和Advapi32.lib
库。
#include <windows.h>
#include <wincrypt.h>
#include <iostream>
#include <vector>
#include <string>
// 用于处理错误的帮助函数
void HandleError(const char* message) {
std::cerr << message << " Error Code: " << GetLastError() << std::endl;
exit(1);
}
int main() {
HCRYPTPROV hProv = 0;
HCRYPTKEY hKey = 0;
HCRYPTHASH hHash = 0;
// 密钥(以十六进制字符串形式提供)
std::string hexKey = "9F74ED4BF9A45F990F9058818C559C51A2887A1C2D82B7ADC42619F908ABAC01";
std::vector<BYTE> key;
for (size_t i = 0; i < hexKey.length(); i += 2) {
std::string byteString = hexKey.substr(i, 2);
BYTE b = (BYTE)strtol(byteString.c_str(), nullptr, 16);
key.push_back(b);
}
// 初始化CryptoAPI
if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {
HandleError("Error during CryptAcquireContext.");
}
// 创建哈希对象
if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)) {
HandleError("Error during CryptCreateHash.");
}
// 哈希密钥数据
if (!CryptHashData(hHash, key.data(), key.size(), 0)) {
HandleError("Error during CryptHashData.");
}
// 从哈希生成密钥
if (!CryptDeriveKey(hProv, CALG_AES_256, hHash, 0, &hKey)) {
HandleError("Error during CryptDeriveKey.");
}
// 明文数据
std::string plaintext = "This is a secret message!";
std::vector<BYTE> data(plaintext.begin(), plaintext.end());
DWORD dataSize = data.size();
// 加密数据
if (!CryptEncrypt(hKey, 0, TRUE, 0, data.data(), &dataSize, data.size())) {
HandleError("Error during CryptEncrypt.");
}
// 解密数据
if (!CryptDecrypt(hKey, 0, TRUE, 0, data.data(), &dataSize)) {
HandleError("Error during CryptDecrypt.");
}
std::string decryptedText((char*)data.data(), dataSize);
std::cout << "Decrypted text: " << decryptedText << std::endl;
// 清理
if (hKey) CryptDestroyKey(hKey);
if (hHash) CryptDestroyHash(hHash);
if (hProv) CryptReleaseContext(hProv, 0);
return 0;
}
注意点
- 密钥处理:上述代码直接从十六进制字符串转换密钥。在实际使用中,可能需要更复杂的密钥管理策略。
- 错误处理:这个示例中的
HandleError
函数会输出错误信息并退出程序。实际应用中可能需要更复杂的错误恢复逻辑。 - 库链接:确保在项目配置中添加
Crypt32.lib
和Advapi32.lib
,以便正确链接所需的Windows加密库。
此代码示例演示了如何使用CryptoAPI在Windows平台上执行AES加密和解密操作。这种方法利用了Windows内置的安全功能,可以方便地集成到基于Windows的应用程序中。
OpenSSL 1.1版本
在使用OpenSSL 1.1版本进行AES加密和解密时,首先确保你的开发环境配置了OpenSSL库。下面是一个C++示例,展示如何使用提供的256位AES密钥(以十六进制字符串形式表示)进行AES加密和解密。
#include <iostream>
#include <openssl/aes.h>
#include <openssl/evp.h>
#include <vector>
#include <string>
// Helper function to convert hex string to byte array
// 辅助函数:用于将十六进制字符串转换为字节数组
std::vector<unsigned char> hex_to_bytes(const std::string& hex) {
std::vector<unsigned char> bytes;
// 每两个字符代表一个字节
for (unsigned int i = 0; i < hex.length(); i += 2) {
std::string byteString = hex.substr(i, 2);
unsigned char byte = (unsigned char)strtol(byteString.c_str(), nullptr, 16);
bytes.push_back(byte); // 将字节添加到结果数组中
}
return bytes;
}
int main() {
// 定义并初始化AES的256位密钥,以十六进制字符串形式表示
std::string hexKey = "9F74ED4BF9A45F990F9058818C559C51A2887A1C2D82B7ADC42619F908ABAC01";
// 使用辅助函数将密钥从十六进制字符串转换为字节数组
std::vector<unsigned char> key = hex_to_bytes(hexKey);
// 初始化向量(IV):AES块大小为16字节
// 这里为了演示,初始化为全0。实际应用中应使用随机生成的IV。
unsigned char iv[AES_BLOCK_SIZE] = {};
// 明文需要被加密的内容
std::string plaintext = "This is a secret message!";
// 将明文字符串转换为字节数组
std::vector<unsigned char> plaintext_bytes(plaintext.begin(), plaintext.end());
// 为密文和解密后的明文预留空间,长度至少比明文长一个块
std::vector<unsigned char> ciphertext(plaintext.size() + AES_BLOCK_SIZE);
std::vector<unsigned char> decryptedtext(plaintext.size() + AES_BLOCK_SIZE);
// 使用OpenSSL EVP接口进行加密操作
// 创建EVP加密上下文
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
if (!ctx) {
std::cerr << "Failed to create EVP_CIPHER_CTX" << std::endl;
return 1;
}
// 初始化加密操作,指定使用AES-256-CBC算法,传入密钥和IV
if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key.data(), iv)) {
std::cerr << "Encryption initialization failed" << std::endl;
EVP_CIPHER_CTX_free(ctx);
return 1;
}
int len; // 存储每次操作的结果长度
int ciphertext_len; // 最终密文长度
// 加密明文并将结果存储到密文数组中
if (1 != EVP_EncryptUpdate(ctx, ciphertext.data(), &len, plaintext_bytes.data(), plaintext_bytes.size())) {
std::cerr << "Encryption failed" << std::endl;
EVP_CIPHER_CTX_free(ctx);
return 1;
}
ciphertext_len = len; // 保存已加密的字节数
// 处理最终的填充块
if (1 != EVP_EncryptFinal_ex(ctx, ciphertext.data() + len, &len)) {
std::cerr << "Final encryption block failed" << std::endl;
EVP_CIPHER_CTX_free(ctx);
return 1;
}
ciphertext_len += len; // 更新密文的最终长度
// 释放EVP加密上下文
EVP_CIPHER_CTX_free(ctx);
// 创建EVP解密上下文
ctx = EVP_CIPHER_CTX_new();
if (!ctx) {
std::cerr << "Failed to create EVP_CIPHER_CTX for decryption" << std::endl;
return 1;
}
// 初始化解密操作,使用与加密时相同的算法、密钥和IV
if (1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key.data(), iv)) {
std::cerr << "Decryption initialization failed" << std::endl;
EVP_CIPHER_CTX_free(ctx);
return 1;
}
// 解密密文
if (1 != EVP_DecryptUpdate(ctx, decryptedtext.data(), &len, ciphertext.data(), ciphertext_len)) {
std::cerr << "Decryption failed" << std::endl;
EVP_CIPHER_CTX_free(ctx);
return 1;
}
int decryptedtext_len = len; // 更新已解密的长度
// 处理最后的填充块
if (1 != EVP_DecryptFinal_ex(ctx, decryptedtext.data() + len, &len)) {
std::cerr << "Final decryption block failed" << std::endl;
EVP_CIPHER_CTX_free(ctx);
return 1;
}
decryptedtext_len += len; // 更新解密后的最终长度
// 释放EVP解密上下文
EVP_CIPHER_CTX_free(ctx);
// 输出加密和解密结果
std::cout << "Plaintext: " << plaintext << std::endl;
std::cout << "Decrypted text: " << std::string(decryptedtext.begin(), decryptedtext.begin() + decryptedtext_len) << std::endl;
return 0;
}
代码解释:
- 初始化上下文:加密和解密的核心是
EVP_CIPHER_CTX
结构,通过EVP_CIPHER_CTX_new()
来分配和初始化上下文。 - 加密初始化:通过
EVP_EncryptInit_ex()
指定加密算法、密钥和IV。 - 加密数据:使用
EVP_EncryptUpdate()
加密主要部分,EVP_EncryptFinal_ex()
处理填充块。 - 解密初始化:类似加密操作,通过
EVP_DecryptInit_ex()
初始化。 - 解密数据:用
EVP_DecryptUpdate()
解密主要部分,用EVP_DecryptFinal_ex()
处理填充块。 - 错误处理:代码在每个主要操作后都有错误处理,输出错误消息并释放资源。
这样使用OpenSSL可以确保数据的加密和解密安全有效。
总结
AES (Advanced Encryption Standard) 是一种对称加密算法,由 NIST 于 2001 年发布,取代了 DES。它通过多轮数学操作将明文转换为密文,密钥长度为 128、192 或 256 位。主要步骤包括字节代替、行移位、列混合和轮密钥加。关键的密钥管理实践包括随机生成、妥善存储、定期更换和安全分发。可通过 C++ 的 Crypto++ 库、Microsoft CryptoAPI 和 OpenSSL 实现 AES 加密与解密。加密技术应确保数据安全,代码示例强调了正确的上下文初始化和错误处理。