php 对称加密算法-对称加密算法AES的GCM模式介绍及其在OpenSSL中的使用示例

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位,用于加密明文密码。

php 对称加密算法_对称加密算法有哪些_对称加密算法的特点

IV(Initialization Vector):初始化向量,其选择必须是随机的。 通常以明文和密文形式发送。 它的功能有点类似于MD5的“加盐”。 目的是避免相同的明文块并始终将其加密成相同的密文块。

AAD(附加验证数据):附加验证数据。 AAD 数据不需要加密,通常以明文和密文的形式传送给接收者。

Mac标签(MAC tag):将确保数据在传输和存储过程中不会被意外修改或恶意篡改。 然后在揭示操作期间使用该标签,以确保密文和 AAD 未被篡改。 加密过程中,Mac标签由明文、密钥、IV和AAD组成。

对称加密算法的特点_php 对称加密算法_对称加密算法有哪些

以下是测试代码:

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下执行结果如下:

对称加密算法的特点_php 对称加密算法_对称加密算法有哪些

以上是在老版本的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

对称加密算法有哪些_php 对称加密算法_对称加密算法的特点

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: