使用OpenSSL库函数测试RFC 3610的AES-CCM加密算法
CCM(Counter with CBC-MAC)
CCM(Counter with CBC-MAC) 定义在 RFC 3610 中,是一个在AES算法基础上发展出的算法,主要应用在无线安全领域。
RFC 3610的原文可以查看这里:https://tools.ietf.org/html/rfc3610
有博主已经翻译成中文了,写的很棒,可以查看这里:https://www.cnblogs.com/efzju/p/3199330.html
OpenSSL的AES-CCM加密算法库函数
OpenSSL已经支持了AES-CCM加密算法,下面是官方提供的例子:
- https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption
测试RFC 3610的AES-CCM加密算法
官方例子使用的算法与RFC 3610规定的有些配置上的差异,下面是我根据RFC 3610写的AES-CCM加密算法测试例子
RFC3610中规定的算法信息:
AES块大小:128位
输出的认证字段长度(M):8位
明文长度(L):2 个字节(表示16位长)
随机数长度:15 - L = 13
测试代码 - openssl_ccm.c
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>
void handleErrors()
{
return;
}
int ccm_encrypt(unsigned char *plaintext, int plaintext_len,
unsigned char *aad, int aad_len,
unsigned char *key,
unsigned char *iv,
unsigned char *ciphertext,
unsigned char *tag)
{
EVP_CIPHER_CTX *ctx;
int len;
int ciphertext_len;
/* Create and initialise the context */
if(!(ctx = EVP_CIPHER_CTX_new()))
handleErrors();
/* Initialise the encryption operation. */
if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_ccm(), NULL, NULL, NULL)) //改成128位
handleErrors();
/*
* Setting IV len to 7. Not strictly necessary as this is the default
* but shown here for the purposes of this example.
*/
if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, 13, NULL)) //随机数长度改为13
handleErrors();
/* Set tag length */
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, 8, NULL); //认证消息长度改为8
/* Initialise key and IV */
if(1 != EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv))
handleErrors();
/* Provide the total plaintext length */
if(1 != EVP_EncryptUpdate(ctx, NULL, &len, NULL, plaintext_len))
handleErrors();
/* Provide any AAD data. This can be called zero or one times as required */
if(1 != EVP_EncryptUpdate(ctx, NULL, &len, aad, aad_len))
handleErrors();
/*
* Provide the message to be encrypted, and obtain the encrypted output.
* EVP_EncryptUpdate can only be called once for this.
*/
if(1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len))
handleErrors();
ciphertext_len = len;
/*
* Finalise the encryption. Normally ciphertext bytes may be written at
* this stage, but this does not occur in CCM mode.
*/
if(1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len))
handleErrors();
ciphertext_len += len;
/* Get the tag */
if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_GET_TAG, 8, tag)) //认证消息长度改为8
handleErrors();
/* Clean up */
EVP_CIPHER_CTX_free(ctx);
return ciphertext_len;
}
int ccm_decrypt(unsigned char *ciphertext, int ciphertext_len,
unsigned char *aad, int aad_len,
unsigned char *tag,
unsigned char *key,
unsigned char *iv,
unsigned char *plaintext)
{
EVP_CIPHER_CTX *ctx;
int len;
int plaintext_len;
int ret;
/* Create and initialise the context */
if(!(ctx = EVP_CIPHER_CTX_new()))
handleErrors();
/* Initialise the decryption operation. */
if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_128_ccm(), NULL, NULL, NULL)) //改成128位
handleErrors();
/* Setting IV len to 7. Not strictly necessary as this is the default
* but shown here for the purposes of this example */
if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, 13, NULL)) //随机数长度改为13
handleErrors();
/* Set expected tag value. */
if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, 8, tag)) //认证消息长度改为8
handleErrors();
/* Initialise key and IV */
if(1 != EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv))
handleErrors();
/* Provide the total ciphertext length */
if(1 != EVP_DecryptUpdate(ctx, NULL, &len, NULL, ciphertext_len))
handleErrors();
/* Provide any AAD data. This can be called zero or more times as required */
if(1 != EVP_DecryptUpdate(ctx, NULL, &len, aad, aad_len))
handleErrors();
/*
* Provide the message to be decrypted, and obtain the plaintext output.
* EVP_DecryptUpdate can be called multiple times if necessary
*/
ret = EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len);
plaintext_len = len;
/* Clean up */
EVP_CIPHER_CTX_free(ctx);
if(ret > 0) {
/* Success */
return plaintext_len;
} else {
/* Verify failed */
return -1;
}
}
int main()
{
uint8_t key[] = "0123456789abcdef";
uint8_t nonce[] = "0123456789abc";
uint8_t plain[] = "0123456789abcdef";
uint8_t add[] = "0123456789abcdef";
uint8_t crypt[128] = {0};
uint8_t auth[128] = {0};
uint8_t decrypt[128] = {0};
int i, result;
ccm_encrypt(plain, strlen(plain), add, strlen(add), key, nonce, crypt, auth);
printf("crypt:");
for (i = 0; i < 16; i++)
printf("%02x", crypt[i]);
printf("\n");
printf("auth:");
for (i = 0; i < 16; i++)
printf("%02x", auth[i]);
printf("\n");
result = ccm_decrypt(crypt, strlen(crypt), add, strlen(add), auth, key, nonce, decrypt);
printf("decrypt:");
for (i = 0; i < 16; i++)
printf("%02x", decrypt[i]);
printf("\n");
printf("result:%d\n", result);
return 0;
}
编译命令
$ gcc -o openssl_ccm openssl_ccm.c -lcrypto
测试结果
crypt:24f0453def51015b0fce44b521f16bf4
auth:d744936cc8e5df400000000000000000
decrypt:30313233343536373839616263646566
result:16
Ubuntu 12.04.5系统上测试通过