在 《基于openssl的EVP对称加密C语言实战案例》这篇博客的基础上将代码提出到独立的.c文件,可以单独进行编译和运行。
代码实现
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <openssl/conf.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <errno.h>
#define CIPHER_TEXT_LEN 128
#define ETH_ALEN 6
#define ESP_OK 0
#define ESP_ERR -1
const char gszKey[] = "111112222333344445555666677";
/***************************************************************************
function: ESP_GetKey
input:
output:
Description:获取解密需要的key,key的长度是32实际使用27个
代码混淆生成key防止直接硬编码被看出来
****************************************************************************/
void ESP_GetKey(char *pcKey)
{
pcKey[0]='3';
pcKey[1]=pcKey[0]-1;
pcKey[2]='a';
pcKey[3]=pcKey[2]+1;
pcKey[4]=pcKey[3]+1;
pcKey[5]='G';
pcKey[6]=pcKey[4]+1;
pcKey[7]=pcKey[1]+1;
pcKey[8]=pcKey[0]+1;
pcKey[9]=pcKey[1];
pcKey[10]=pcKey[6]-1;
pcKey[11]=pcKey[7]+1;
pcKey[12]=pcKey[5]+1;
pcKey[13]=pcKey[5]-1;
pcKey[14]=pcKey[4]-1;
pcKey[15]=pcKey[0];
pcKey[16]=pcKey[6]-1;
pcKey[17]=pcKey[5]-1;
pcKey[18]=pcKey[17]+1;
pcKey[19]=pcKey[18]-1;
pcKey[20]=pcKey[5]-1;
pcKey[21]=pcKey[3];
pcKey[22]=pcKey[1]+1;
pcKey[23]=pcKey[22]+1;
pcKey[24]=pcKey[18]-1;
pcKey[25]=pcKey[5]+1;
pcKey[26]=pcKey[16];
pcKey[27]=pcKey[0]+1;
pcKey[28]=pcKey[12]+1;
pcKey[29]=pcKey[5]+1;
pcKey[30]=pcKey[11]-1;
pcKey[31]=pcKey[6]-1;
return;
}
int ESP_Encrypt(unsigned char *ucPlaintext, int ucPlaintextLen, unsigned char *ucCiphertext)
{
int ret = ESP_ERR;
int p_len = 0;
int f_len = 0;
int ciphertextLen = 0;
int nrounds = 5;
char szKey[32] = {0};
unsigned char key[32] = {0};
unsigned char iv[32] = {0};
unsigned int szSalt[] = {11111,11111};
printf("ucPlaintext:%s, ucPlaintextLen:%d.\n", ucPlaintext, ucPlaintextLen);
EVP_CIPHER_CTX *pMdCtx;
pMdCtx = EVP_CIPHER_CTX_new();
if(NULL == pMdCtx)
{
printf("alloc ctx failed.\n");
return ESP_ERR;
}
ESP_GetKey(szKey);
ret = EVP_BytesToKey(EVP_aes_256_cbc(),
EVP_sha1(),
(unsigned char*)szSalt,
(unsigned char*)szKey,
strlen(gszKey),
nrounds,
key,
iv);
if(ret != 32)
{
printf("Key size is %d bits, it should be 256 bits\n", ret);
EVP_CIPHER_CTX_free(pMdCtx);
return ESP_ERR;
}
EVP_EncryptInit_ex(pMdCtx, EVP_aes_256_cbc(), NULL, key, iv);
EVP_EncryptUpdate(pMdCtx, ucCiphertext, &p_len, ucPlaintext, ucPlaintextLen);
ciphertextLen = p_len;
EVP_EncryptFinal_ex(pMdCtx, ucCiphertext + p_len, &f_len);
ciphertextLen += f_len;
ucCiphertext[p_len+f_len] = 0;
printf("p_len is %d, f_len is %d, ucCiphertext is %s.\n", p_len, f_len, ucCiphertext);
EVP_CIPHER_CTX_free(pMdCtx);
//写入文件中
FILE *fp = fopen("/home/lyp/key", "w");
if(fp == NULL)
{
printf("generate key file failed!\n");
return ESP_ERR;
}
//fprintf(fp, "%s", ucCiphertext);
fwrite(ucCiphertext, p_len+f_len, 1, fp);
fclose(fp);
return ciphertextLen;
}
unsigned char *ESP_DecryptKey(unsigned char *pcCipherText, int lCipherLen)
{
int ret;
int nrounds = 5;
char szKey[32] = {0};
int p_len = 0;
int f_len = 0;
unsigned int szSalt[] = {11111,11111};
unsigned char key[32] = {0};
unsigned char iv[32] = {0};
EVP_CIPHER_CTX *pMdCtx = NULL;
unsigned char *pcPlaintext = NULL;
// 0 准备明文空间和上下文
pcPlaintext = (unsigned char *)malloc(lCipherLen);
if(NULL == pcPlaintext)
{
printf("System have no more memory failed.\n");
return NULL;
}
pMdCtx = EVP_CIPHER_CTX_new();
if(NULL == pMdCtx)
{
free(pcPlaintext);
printf("alloc ctx failed.\n");
return NULL;
}
// 1 获取key和iv 然后初始化ctx
ESP_GetKey(szKey);
ret = EVP_BytesToKey(EVP_aes_256_cbc(),
EVP_sha1(),
(unsigned char*)szSalt,
(unsigned char*)szKey,
strlen(gszKey),
nrounds,
key,
iv);
if(ret != 32)
{
printf("Key size is %d bits, it should be 256 bits\n", ret);
EVP_CIPHER_CTX_free(pMdCtx);
free(pcPlaintext);
return NULL;
}
EVP_DecryptInit_ex(pMdCtx, EVP_aes_256_cbc(), NULL, key, iv);
// 2解密
EVP_DecryptUpdate(pMdCtx, pcPlaintext, &p_len, pcCipherText, lCipherLen);
EVP_DecryptFinal_ex(pMdCtx, pcPlaintext+p_len, &f_len);
pcPlaintext[p_len+f_len]=0;
printf("p_len is %d, f_len is %d, pcPlaintext is %s.\n", p_len, f_len, pcPlaintext);
// 3释放上下文
EVP_CIPHER_CTX_free(pMdCtx);
return pcPlaintext;
}
int main(int argc, char *szMacAddr[])
{
unsigned char szCipherTxt[CIPHER_TEXT_LEN] = {0};
char szBuffer[CIPHER_TEXT_LEN] = {0};
char szBufferTmp[CIPHER_TEXT_LEN] = {0};
char *pcKey = NULL;
sprintf(szBuffer, "%sFC-TOP-ROOF", szMacAddr[1]);
printf("szBuffer:%s, sizeof(szBuffer):%ld.\n", szBuffer, sizeof(szBuffer));
strncpy(szBufferTmp, szBuffer, sizeof(szBufferTmp));
//加密,生成key文件,并返回出参加密字符串szCipherTxt
ESP_Encrypt(szBuffer, sizeof(szBuffer), szCipherTxt);
//解密,并返回解密字符串
pcKey = (char *)ESP_DecryptKey(szCipherTxt, sizeof(szCipherTxt));
if(NULL == pcKey)
{
printf("Decrypt failed.\n");
return ESP_ERR;
}
//校验结果,比较解密字符串与加密前字符串是否一样
if(strcmp(szBufferTmp, pcKey))
{
printf("check key is invaild, buffer:%s, key:%s.\n", szBufferTmp ,pcKey);
free(pcKey);
return ESP_ERR;
}
else
{
printf("check key success.\n");
}
free(pcKey);
return ESP_OK;
}
运行结果
lyp@ubuntu:~$ gcc key.c -lcrypto
lyp@ubuntu:~$ ./a.out 11-22-33-44-55-66
szBuffer:11-22-33-44-55-66FC-TOP-ROOF, sizeof(szBuffer):128.
ucPlaintext:11-22-33-44-55-66FC-TOP-ROOF, ucPlaintextLen:128.
p_len is 128, f_len is 16, ucCiphertext is @q⚌1|
Z
⚌
⚌
⚌
P
⚌
⚌
氤
l
]
⚌
⚌
⚌
Z⚌⚌⚌P⚌⚌氤l]⚌⚌⚌
Z⚌⚌⚌P⚌⚌氤l]⚌⚌⚌⚌⚌1H⚌⚌⚌z⚌⚌⚌dDJ⚌⚌~r⚌⚌⚌"ф⚌⚌⚌p⚌ߡ?⚌˿⚌w2⚌%⚌’⚌s⚌⚌⚌⚌⚌U⚌⚌⚌V奫L7⚌⚌?v⚌⚌⚌⚌,⚌O⚌
⚌⚌`⚌⚌⚌)⚌uL⚌⚌v⚌զ⚌2⚌iT⚌⚌⚌⚌⚌⚌6A⚌z⚌].
p_len is 112, f_len is 0, pcPlaintext is 11-22-33-44-55-66FC-TOP-ROOF.
check key success.
lyp@ubuntu:~$
说明
1.编译时需要链接动态库 libcrypt.so,这个库是软链接的libcrypto.so.1.1。
库文件获取:
openssl编译需要链接的库libcrypto.so
2.运行 a.out时可以带一个字符串参数,上述运行结果模拟的是mac地址。
3.代码中的 szSalt,nrounds,gszKey,szKey变量可自行修改,保证自己代码的保密性和独立性(要注意加密接口与解密接口中相同变量的一致)。
4.头文件可能引用大于实际需要。
5.CIPHER_TEXT_LEN 长度设置成32时,解密key文件得到的字符串不完整,具体情况如下:
lyp@ubuntu:~$ ./a.out 11-22-33-44-55-66
szBuffer:11-22-33-44-55-66FC-TOP-ROOF, sizeof(szBuffer):32.
ucPlaintext:11-22-33-44-55-66FC-TOP-ROOF, ucPlaintextLen:32.
p_len is 32, f_len is 16, ucCiphertext is @q⚌1|
Z
⚌
⚌
⚌
P
⚌
⚌
氤
l
]
⚌
⚌
⚌
Z⚌⚌⚌P⚌⚌氤l]⚌⚌⚌
Z⚌⚌⚌P⚌⚌氤l]⚌⚌⚌⚌⚌"⚌⚌⚌⚌⚌1⚌u/D⚌p⚌⚌.
p_len is 16, f_len is 0, pcPlaintext is 11-22-33-44-55-6.
check key is invaild, buffer:11-22-33-44-55-66FC-TOP-ROOF, key:11-22-33-44-55-6.
但是在上一篇文章中就没有出现该问题;当CIPHER_TEXT_LEN设置成128时,生成的key又不可以应用于上一篇文章中的项目,希望有心人可以帮忙解惑。
答:加密接口调用ESP_Encrypt(szBuffer, sizeof(szBuffer), szCipherTxt);参数传的有问题,将sizeof(szBuffer)改成strlen(szBuffer),问题解决。
6.代码生成的key文件在下面链接可以获取。
openssl加密生成的key文件