openssl学习之ccm,gcm 模式

openssl中添加了对AES ccm 和gcm模式的支持。下面的内容主要是对这两个模式相关资料的收集以及整理。

一,CCM

CCM (counter with CBC-MAC)定义在分组长度为128位的加密算法中,如,AES 的分组长度为128。组成AES-CCM算法的关键组成是CTR工作模式以及CMAC认证算法。Wifi 的WPE协议中使用了AES-CCM。在HMAC中我们介绍CCM是属于一种E&M(认证并且加密),首先我们来看一下AES-CCM模式的输入输出。

首先介绍两个参数设置:

L:长度域,取值为2~8 ,openssl中缺省的为8。

M:tag的长度,合法的值为:4,6,8,10,12,14 和16。openssl中缺省的为12

key16,24,32
None15-L
Message to authenticate and encryptlen(Msg)
Additional authenticated datalen(AAD)
其中对消息长度有:0<= len(Msg)<= 2^(8L);

对附加数据长度有:0<= len(AAD)< 2^64;

/* Simple AES CCM test program, uses the same NIST data used for the FIPS
 * self test but uses the application level EVP APIs.
 */
#include <stdio.h>
#include <openssl/bio.h>
#include <openssl/evp.h>

/* AES-CCM test data from NIST public test vectors */

static const unsigned char ccm_key[] = {
	0xce,0xb0,0x09,0xae,0xa4,0x45,0x44,0x51,0xfe,0xad,0xf0,0xe6,
	0xb3,0x6f,0x45,0x55,0x5d,0xd0,0x47,0x23,0xba,0xa4,0x48,0xe8
};
// 随机数,每次加密针对相同的KEY使用不同的NONCE。否则会破坏CCM模式的安全性(RFC3610)
static const unsigned char ccm_nonce[] = {
	0x76,0x40,0x43,0xc4,0x94,0x60,0xb7
};
//附加数据
static const unsigned char ccm_adata[] = {
	0x6e,0x80,0xdd,0x7f,0x1b,0xad,0xf3,0xa1,0xc9,0xab,0x25,0xc7,
	0x5f,0x10,0xbd,0xe7,0x8c,0x23,0xfa,0x0e,0xb8,0xf9,0xaa,0xa5,
	0x3a,0xde,0xfb,0xf4,0xcb,0xf7,0x8f,0xe4
};
//plaintext 表示明文
static const unsigned char ccm_pt[] = {
	0xc8,0xd2,0x75,0xf9,0x19,0xe1,0x7d,0x7f,0xe6,0x9c,0x2a,0x1f,
	0x58,0x93,0x9d,0xfe,0x4d,0x40,0x37,0x91,0xb5,0xdf,0x13,0x10
};
//ciphertext 表示密文
static const unsigned char ccm_ct[] = {
	0x8a,0x0f,0x3d,0x82,0x29,0xe4,0x8e,0x74,0x87,0xfd,0x95,0xa2,
	0x8a,0xd3,0x92,0xc8,0x0b,0x36,0x81,0xd4,0xfb,0xc7,0xbb,0xfd
};
//tag 表示tag数据
static const unsigned char ccm_tag[] = {
	0x2d,0xd6,0xef,0x1c,0x45,0xd4,0xcc,0xb7,0x23,0xdc,0x07,0x44,
	0x14,0xdb,0x50,0x6d
};

void aes_ccm_encrypt(void)
{
	EVP_CIPHER_CTX *ctx;
	int outlen, tmplen;
	unsigned char outbuf[1024];
	printf("AES CCM Encrypt:\n");
	printf("Plaintext:\n");
	BIO_dump_fp(stdout, ccm_pt, sizeof(ccm_pt));
	ctx = EVP_CIPHER_CTX_new();
	/* Set cipher type and mode */
	EVP_EncryptInit_ex(ctx, EVP_aes_192_ccm(), NULL, NULL, NULL);
	/* Set nonce length if default 96 bits is not appropriate */
	EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, sizeof(ccm_nonce), NULL);
	/* Set tag length */
	EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, sizeof(ccm_tag), NULL);
	/* Initialise key and IV */
	EVP_EncryptInit_ex(ctx, NULL, NULL, ccm_key, ccm_nonce);
	/* Set plaintext length: only needed if AAD is used*/
        //输入输出需设置为NULL
	EVP_EncryptUpdate(ctx, NULL, &outlen, NULL, sizeof(ccm_pt));
	/* Zero or one call to specify any AAD */
        //设置AAD,out参数需设置为NULL
	EVP_EncryptUpdate(ctx, NULL, &outlen, ccm_adata, sizeof(ccm_adata));
	/* Encrypt plaintext: can only be called once */
	EVP_EncryptUpdate(ctx, outbuf, &outlen, ccm_pt, sizeof(ccm_pt));
	/* Output encrypted block */
	printf("Ciphertext:\n");
	BIO_dump_fp(stdout, outbuf, outlen);
	/* Finalise: note get no output for CCM */
	EVP_EncryptFinal_ex(ctx, outbuf, &outlen);
	/* Get tag */
	EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_GET_TAG, 16, outbuf);
	/* Output tag */
	printf("Tag:\n");
	BIO_dump_fp(stdout, outbuf, 16);
	EVP_CIPHER_CTX_free(ctx);
}

void aes_ccm_decrypt(void)
{
	EVP_CIPHER_CTX *ctx;
	int outlen, tmplen, rv;
	unsigned char outbuf[1024];
	printf("AES CCM Derypt:\n");
	printf("Ciphertext:\n");
	BIO_dump_fp(stdout, ccm_ct, sizeof(ccm_ct));
	ctx = EVP_CIPHER_CTX_new();
	/* Select cipher */
	EVP_DecryptInit_ex(ctx, EVP_aes_192_ccm(), NULL, NULL, NULL);
	/* Set nonce length, omit for 96 bits */
	EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, sizeof(ccm_nonce), NULL);
	/* Set expected tag value */
	EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG,
					sizeof(ccm_tag), (void *)ccm_tag);
	/* Specify key and IV */
	EVP_DecryptInit_ex(ctx, NULL, NULL, ccm_key, ccm_nonce);
	/* Set ciphertext length: only needed if we have AAD */
	EVP_DecryptUpdate(ctx, NULL, &outlen, NULL, sizeof(ccm_ct));
	/* Zero or one call to specify any AAD */
	EVP_DecryptUpdate(ctx, NULL, &outlen, ccm_adata, sizeof(ccm_adata));
	/* Decrypt plaintext, verify tag: can only be called once */
	rv = EVP_DecryptUpdate(ctx, outbuf, &outlen, ccm_ct, sizeof(ccm_ct));
	/* Output decrypted block: if tag verify failed we get nothing */
	if (rv > 0)
	{
		printf("Plaintext:\n");
		BIO_dump_fp(stdout, outbuf, outlen);
	}
	else
		printf("Plaintext not available: tag verify failed.\n");
	EVP_CIPHER_CTX_free(ctx);
}

int main(int argc, char **argv)
{
	aes_ccm_encrypt();
	aes_ccm_decrypt();
}


2,GCM 

GCM基于并行化设计,因此可以提供高效的吞吐率和低成本、低时延。本质是消息在变形的CTR模式下加密,密文结果与密钥以及消息长度在GF(2^128)域上相乘,计算流程如下所示。其输入输出和CCM基本一致。CCM和GCM的具体计算过程可以参看《密码学与网络安全》第五版,书中有详细的介绍。FRC5288 中介绍了TLS1.2 中的GCM应用。下面贴出openssl中AES-GCM的实例。


/* Simple AES GCM test program, uses the same NIST data used for the FIPS
 * self test but uses the application level EVP APIs.
 */
#include <stdio.h>
#include <openssl/bio.h>
#include <openssl/evp.h>

/* AES-GCM test data from NIST public test vectors */

static const unsigned char gcm_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[] = {
        0x99,0xaa,0x3e,0x68,0xed,0x81,0x73,0xa0,0xee,0xd0,0x66,0x84
};

static const unsigned char gcm_pt[] = {
        0xf5,0x6e,0x87,0x05,0x5b,0xc3,0x2d,0x0e,0xeb,0x31,0xb2,0xea,
        0xcc,0x2b,0xf2,0xa5
};

static const unsigned char gcm_aad[] = {
        0x4d,0x23,0xc3,0xce,0xc3,0x34,0xb4,0x9b,0xdb,0x37,0x0c,0x43,
        0x7f,0xec,0x78,0xde
};

static const unsigned char gcm_ct[] = {
        0xf7,0x26,0x44,0x13,0xa8,0x4c,0x0e,0x7c,0xd5,0x36,0x86,0x7e,
        0xb9,0xf2,0x17,0x36
};

static const unsigned char gcm_tag[] = {
        0x67,0xba,0x05,0x10,0x26,0x2a,0xe4,0x87,0xd7,0x37,0xee,0x62,
        0x98,0xf7,0x7e,0x0c
};

void aes_gcm_encrypt(void)
{
        EVP_CIPHER_CTX *ctx;
        int outlen, tmplen;
        unsigned char outbuf[1024];
        printf("AES GCM Encrypt:\n");
        printf("Plaintext:\n");
        BIO_dump_fp(stdout, gcm_pt, sizeof(gcm_pt));
        ctx = EVP_CIPHER_CTX_new();
        /* Set cipher type and mode */
        EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL);
        /* Set IV length if default 96 bits is not appropriate */
        EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, sizeof(gcm_iv), NULL);
        /* Initialise key and IV */
        EVP_EncryptInit_ex(ctx, NULL, NULL, gcm_key, gcm_iv);
        /* Zero or more calls to specify any AAD */
        EVP_EncryptUpdate(ctx, NULL, &outlen, gcm_aad, sizeof(gcm_aad));
        /* Encrypt plaintext */
        EVP_EncryptUpdate(ctx, outbuf, &outlen, gcm_pt, sizeof(gcm_pt));
        /* Output encrypted block */
        printf("Ciphertext:\n");
        BIO_dump_fp(stdout, outbuf, outlen);
        /* Finalise: note get no output for GCM */
        EVP_EncryptFinal_ex(ctx, outbuf, &outlen);
        /* Get tag */
        EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, outbuf);
        /* Output tag */
        printf("Tag:\n");
        BIO_dump_fp(stdout, outbuf, 16);
        EVP_CIPHER_CTX_free(ctx);
}

void aes_gcm_decrypt(void)
{
        EVP_CIPHER_CTX *ctx;
        int outlen, tmplen, rv;
        unsigned char outbuf[1024];
        printf("AES GCM Derypt:\n");
        printf("Ciphertext:\n");
        BIO_dump_fp(stdout, gcm_ct, sizeof(gcm_ct));
        ctx = EVP_CIPHER_CTX_new();
        /* Select cipher */
        EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL);
        /* Set IV length, omit for 96 bits */
        EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, sizeof(gcm_iv), NULL);
        /* Specify key and IV */
        EVP_DecryptInit_ex(ctx, NULL, NULL, gcm_key, gcm_iv);
#if 0
        /* Set expected tag value. A restriction in OpenSSL 1.0.1c and earlier
         * required the tag before any AAD or ciphertext */
        EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, sizeof(gcm_tag), gcm_tag);
#endif
        /* Zero or more calls to specify any AAD */
        EVP_DecryptUpdate(ctx, NULL, &outlen, gcm_aad, sizeof(gcm_aad));
        /* Decrypt plaintext */
        EVP_DecryptUpdate(ctx, outbuf, &outlen, gcm_ct, sizeof(gcm_ct));
        /* Output decrypted block */
        printf("Plaintext:\n");
        BIO_dump_fp(stdout, outbuf, outlen);
        /* Set expected tag value. Works in OpenSSL 1.0.1d and later */
        EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, sizeof(gcm_tag), gcm_tag);
        /* Finalise: note get no output for GCM */
        rv = EVP_DecryptFinal_ex(ctx, outbuf, &outlen);
        /* Print out return value. If this is not successful authentication
         * failed and plaintext is not trustworthy.
         */
        printf("Tag Verify %s\n", rv > 0 ? "Successful!" : "Failed!");
        EVP_CIPHER_CTX_free(ctx);
 }

int main(int argc, char **argv)
{
        aes_gcm_encrypt();
        aes_gcm_decrypt();
 }



  • 5
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
OpenSSL is a widely-used open-source cryptographic library that provides support for various cryptographic algorithms, including the GCM (Galois/Counter Mode) encryption mode. GCM is an authenticated encryption mode that provides both confidentiality and integrity. It combines the Counter (CTR) mode of encryption with a universal hash function called Galois Message Authentication Code (GMAC). GCM is commonly used for secure communication protocols like TLS. To use GCM with OpenSSL, you can utilize the EVP (Envelope) API provided by OpenSSL. Here's an example of how you can use OpenSSL to perform GCM encryption and decryption: ```c #include <openssl/evp.h> void encrypt_decrypt_gcm(const unsigned char* key, const unsigned char* iv, const unsigned char* aad, const unsigned char* ciphertext, int ciphertext_len, unsigned char* tag, unsigned char* plaintext) { EVP_CIPHER_CTX* ctx; int len; int plaintext_len; // Create and initialize the context ctx = EVP_CIPHER_CTX_new(); EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL); // Set the key and IV EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_KEY_LEN, 256, NULL); EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv); // Set the AAD (Additional Authenticated Data) EVP_EncryptUpdate(ctx, NULL, &len, aad, sizeof(aad)); // Perform the encryption EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len); // Finalize the encryption (generates the authentication tag) EVP_EncryptFinal_ex(ctx, ciphertext + len, &len); EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag); // Cleanup EVP_CIPHER_CTX_free(ctx); } int main() { unsigned char key[32]; // 256-bit key unsigned char iv[12]; // 96-bit IV unsigned char aad[16]; // Additional Authenticated Data unsigned char ciphertext[128]; unsigned char tag[16]; unsigned char plaintext[128]; // Initialize the key, IV, AAD, and plaintext encrypt_decrypt_gcm(key, iv, aad, ciphertext, sizeof(ciphertext), tag, plaintext); return 0; } ``` In this example, you would need to replace the placeholders for the key, IV, AAD, ciphertext, and plaintext with the actual data you want to use. Additionally, make sure to include the necessary OpenSSL headers and link against the OpenSSL library when compiling. This is just a basic example and it's important to use proper cryptographic practices and ensure the security of your implementation. It's recommended to refer to the OpenSSL documentation and consult cryptographic experts for more guidance on using GCM with OpenSSL.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值