AES(Advanced Encryption Standard),即高级加密标准,由美国国家标准与技术协会(NIST)于2000年公布,是一种对称加密算法。 关于AES的更多信息,请参考:
AES的GCM(Galois/Counter Mode)模式本质上是AES的CTR模式(计数器模式)加上用于哈希估计的GMAC(Galois Message Authentication Code,伽罗瓦消息认证码)的组合模式。 GCM可以提供消息的加密和完整性校准。 此外php 对称加密算法,它还可以提供附加消息的完整性校准。
OpenSSL 中的 EVP 套接字支持执行经过身份验证的加密和解密的能力,以及将未加密的关联数据附加到消息的选项。 这种带有关联数据的认证加密 (AEAD) 方案通过加密数据来提供机密性,并通过在加密数据上创建 MAC 标签来提供真实性保证。
Key:对称密钥,其厚度可以是128、192、256位,用于加密明文密码。
IV(Initialization Vector):初始化向量,其选择必须是随机的。 通常以明文和密文形式发送。 它的功能有点类似于MD5的“加盐”。 目的是避免相同的明文块并始终将其加密成相同的密文块。
AAD(附加验证数据):附加验证数据。 AAD 数据不需要加密,通常以明文和密文的形式传送给接收者。
Mac标签(MAC tag):将确保数据在传输和存储过程中不会被意外修改或恶意篡改。 然后在揭示操作期间使用该标签,以确保密文和 AAD 未被篡改。 加密过程中,Mac标签由明文、密钥、IV和AAD组成。
以下是测试代码:
namespace {
static const unsigned char gcm_key[] = { // 32 bytes, Key
0xee, 0xbc, 0x1f, 0x57, 0x48, 0x7f, 0x51, 0x92, 0x1c, 0x04, 0x65, 0x66,
0x5f, 0x8a, 0xe6, 0xd1, 0x65, 0x8b, 0xb2, 0x6d, 0xe6, 0xf8, 0xa0, 0x69,
0xa3, 0x52, 0x02, 0x93, 0xa5, 0x72, 0x07, 0x8f
};
static const unsigned char gcm_iv[] = { // 12 bytes, IV(Initialisation Vector)
0x99, 0xaa, 0x3e, 0x68, 0xed, 0x81, 0x73, 0xa0, 0xee, 0xd0, 0x66, 0x84
};
// Additional Authenticated Data(AAD): it is not encrypted, and is typically passed to the recipient in plaintext along with the ciphertext
static const unsigned char gcm_aad[] = { // 16 bytes
0x4d, 0x23, 0xc3, 0xce, 0xc3, 0x34, 0xb4, 0x9b, 0xdb, 0x37, 0x0c, 0x43,
0x7f, 0xec, 0x78, 0xde
};
std::unique_ptr aes_gcm_encrypt(const char* plaintext, int& length, unsigned char* tag)
{
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
// Set cipher type and mode
EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr, nullptr, nullptr);
// Set IV length if default 96 bits is not appropriate
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, sizeof(gcm_iv), nullptr);
// Initialise key and IV
EVP_EncryptInit_ex(ctx, nullptr, nullptr, gcm_key, gcm_iv);
// Zero or more calls to specify any AAD
int outlen;
EVP_EncryptUpdate(ctx, nullptr, &outlen, gcm_aad, sizeof(gcm_aad));
unsigned char outbuf[1024];
// Encrypt plaintext
EVP_EncryptUpdate(ctx, outbuf, &outlen, (const unsigned char*)plaintext, strlen(plaintext));
length = outlen;
std::unique_ptr ciphertext(new unsigned char[length]);
memcpy(ciphertext.get(), outbuf, length);
// Finalise: note get no output for GCM
EVP_EncryptFinal_ex(ctx, outbuf, &outlen);
// Get tag
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, outbuf);
memcpy(tag, outbuf, 16);
// Clean up
EVP_CIPHER_CTX_free(ctx);
return ciphertext;
}
std::unique_ptr aes_gcm_decrypt(const unsigned char* ciphertext, int& length, const unsigned char* tag)
{
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
// Select cipher
EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr, nullptr, nullptr);
// Set IV length, omit for 96 bits
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, sizeof(gcm_iv), nullptr);
// Specify key and IV
EVP_DecryptInit_ex(ctx, nullptr, nullptr, gcm_key, gcm_iv);
int outlen;
// Zero or more calls to specify any AAD
EVP_DecryptUpdate(ctx, nullptr, &outlen, gcm_aad, sizeof(gcm_aad));
unsigned char outbuf[1024];
// Decrypt plaintext
EVP_DecryptUpdate(ctx, outbuf, &outlen, ciphertext, length);
// Output decrypted block
length = outlen;
std::unique_ptr plaintext(new unsigned char[length]);
memcpy(plaintext.get(), outbuf, length);
// Set expected tag value
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, (void*)tag);
// Finalise: note get no output for GCM
int rv = EVP_DecryptFinal_ex(ctx, outbuf, &outlen);
// Print out return value. If this is not successful authentication failed and plaintext is not trustworthy.
fprintf(stdout, "Tag Verify %sn", rv > 0 ? "Successful!" : "Failed!");
EVP_CIPHER_CTX_free(ctx);
return plaintext;
}
} // namespace
int test_openssl_aes_gcm()
{
/* reference:
https://github.com/openssl/openssl/blob/master/demos/evp/aesgcm.c
https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption
*/
fprintf(stdout, "Start AES GCM 256 Encrypt:n");
const char* plaintext = "1234567890ABCDEFG!@#$%^&*()_+[]{};':,./?|";
fprintf(stdout, "src plaintext: %s, length: %dn", plaintext, strlen(plaintext));
int length = 0;
std::unique_ptr tag(new unsigned char[16]);
std::unique_ptr ciphertext = aes_gcm_encrypt(plaintext, length, tag.get());
fprintf(stdout, "length: %d, ciphertext: ", length);
for (int i = 0; i < length; ++i)
fprintf(stdout, "x ", ciphertext.get()[i]);
fprintf(stdout, "nTag: ");
for (int i = 0; i < 16; ++i)
fprintf(stdout, "x ", tag.get()[i]);
fprintf(stdout, "n");
fprintf(stdout, "nStart AES GCM 256 Decrypt:n");
std::unique_ptr result = aes_gcm_decrypt(ciphertext.get(), length, tag.get());
fprintf(stdout, "length: %d, decrypted plaintext: ", length);
for (int i = 0; i < length; ++i)
fprintf(stdout, "%c", result.get()[i]);
fprintf(stdout, "n");
if (strncmp(plaintext, (const char*)result.get(), length) == 0) {
fprintf(stdout, "decrypt successn");
return 0;
} else {
fprintf(stderr, "decrypt failn");
return -1;
}
}
Windows下执行结果如下:
以上是在老版本的OpenSSL 1.0.1g上执行的。 上述代码在最新版本1.1.1g上也可以执行。
要在Windows上编译1.1.1g版本源代码,请执行以下命令:no-asm选项不启用汇编模式。 要编译发行版,请将 debug-VC-WIN64A 更改为 VC-WIN64。
perl Configure debug-VC-WIN64A no-asm --prefix=D:DownLoadopensslwin64_debug
nmake
nmake install
在Linux上编译1.1.1g版本源码,执行以下命令:
./config --prefix=/home/sensetime/Downloads/openssl/install
make
make insall
gcm 示例 aesgcm.c 在 1.1.1g 源代码的 demos/evp 目录中给出。 这里,将上述测试代码添加为aesgcm2.cpp。 编译脚本如下:
#! /bin/bash
g++ -o test_aesgcm aesgcm.c -L ../../ -lcrypto -lssl -I../../include
g++ -o test_aesgcm2 aesgcm2.cpp -std=c++11 -L ../../ -lcrypto -lssl -I../../include
g++ -o test_aesccm aesccm.c -L ../../ -lcrypto -lssl -I../../include
执行结果如下:注意,如果在Windows上执行aesgcm2.cppphp 对称加密算法,需要额外添加#include
GitHub:
发表评论