ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

基于openssl的EVP对称加密C语言实战案例

2022-01-20 11:06:53  阅读:166  来源: 互联网

标签:LOG openssl len C语言 key EVP szBuffer pcKey


根据解密算法代码反推实现加密算法

说明

为保证项目安全,本文章使用的加解密相关的代码变量szSalt,szKey,nrounds,gszKey等变量为修改后的,未经实际应用检测。自测时打印函数请自行修改。

先上已经实现的解密代码

#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/socket.h>
#include <net/if.h>
#include <errno.h>

const char gszKey[]= "aaabbbcccdddeeefffggg";
#define  OK     0
#define  ERR   -1
#define CIPHER_TEXT_LEN 32

/***************************************************************************
function: GetKey
input:
output:
Description:获取解密需要的key,key的长度是32实际使用27个
            代码混淆生成key防止直接硬编码被看出来
****************************************************************************/
void GetKey(char *pcKey)
{
    pcKey[0]='6';
    pcKey[1]=pcKey[0]-1;
    pcKey[2]='1';
    pcKey[3]=pcKey[2]+1;
    pcKey[4]=pcKey[3]+1;
    pcKey[5]='F';
    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[6];
    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[1];
    pcKey[22]=pcKey[1]+1;
    pcKey[23]=pcKey[22]+1;
    pcKey[24]=pcKey[18]-1;
    pcKey[25]=pcKey[5]+1;
    pcKey[26]=pcKey[1];
    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;
}
/***************************************************************************
function: DecryptKey
input:
output:
Description:解密key
****************************************************************************/
unsigned char *DecryptKey(unsigned char *pcCipherText, int lCipherLen)
{
    int   ret;
    int   nrounds  = 7;
    char  szKey[32]= {0};
    uint  szSalt[] = {11111,11111};
    uchar key[32]  = {0};
    uchar iv[32]   = {0};
    int   p_len    = 0;
    int   f_len    = 0;
    EVP_CIPHER_CTX *pMdCtx = NULL;
    uchar *pcPlaintext     = NULL;

    // 0 准备明文空间和上下文
    pcPlaintext = (uchar *)malloc(lCipherLen);
    if(NULL == pcPlaintext)
    {
      LOG(LOG_ERROR, "System have no more memory failed.\n");
      return NULL;
    }

    pMdCtx = EVP_CIPHER_CTX_new();
    if(NULL == pMdCtx)
    {
        free(pcPlaintext);
        LOG(LOG_ERROR, "alloc ctx failed.\n");
        return NULL;
    }

    // 1 获取key和iv 然后初始化ctx
    GetKey(szKey);
    ret = EVP_BytesToKey(EVP_aes_256_cbc(), 
                          EVP_sha1(), 
                          (unsigned char*)szSalt, 
                          (unsigned char*)szKey, 
                          strlen(gszKey),
                          nrounds, 
                          key, 
                          iv);

    LOG(LOG_DEBUG, "key is %d, iv is %d.\n", key, iv);
    if(ret != 32)
    {
        LOG(LOG_ERROR, "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;

    LOG(LOG_DEBUG, "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;
}

/***************************************************************************
function: GetMacAddr
input:
output:
Description:获取系统mac地址
****************************************************************************/
int GetMacAddr(char *pcDevName, char *pcBuffer)
{
    struct ifreq req;
    int s         = -1;
    int err       = -1;
    unsigned char szMacAddr[6];
    
    s = socket(AF_INET,SOCK_DGRAM,0);
    if(s < 0)
    {
        LOG(LOG_ERROR, "alloc socket failed.\n");
        return ERR;
    }
    
    strcpy(req.ifr_name, pcDevName); 
    err = ioctl(s, SIOCGIFHWADDR, &req); 
    if(err < 0)
    { 
        LOG(LOG_ERROR, "ioctl failed err:%d.\n", errno);
        return ERR;
    }
    
    memcpy(szMacAddr, req.ifr_hwaddr.sa_data, 6);
    sprintf(pcBuffer,"%02x-%02x-%02x-%02x-%02x-%02xFC-TOP-ROOF", 
            szMacAddr[0],szMacAddr[1],szMacAddr[2],
            szMacAddr[3],szMacAddr[4],szMacAddr[5]);
    close(s);
    return OK;
}
/***************************************************************************
function: CheckKey
input:
output:
Description:检查设备是否合法
            openssl的一些新版本必须要使用EVP_MD_CTX_new申请控制结构,
            老版本不支持这里预留新版本的使用方式
****************************************************************************/
int CheckKey()
{
    uchar szCipherTxt[CIPHER_TEXT_LEN] = {0};
    char *pcKey           = NULL;
    char  szBuffer[32]    = {0};
    char  szPlainText[32] = {0};
    int   ret             = 0;
    int   fd              = 0;

    //获取本地MAC
    #ifdef A
    ret = GetMacAddr("enp3s0", szBuffer);
    #endif
   
    #ifdef B
    ret = GetMacAddr("eth0", szBuffer);
    #endif

    #ifdef C
    ret = GetMacAddr("eth0", szBuffer);
    #endif
    if(ret < 0)
    {
        LOG(LOG_ERROR, "Get Mac failed.\n");
        return ERR;
    }

    //读取key, 
    // #if 1时支持版本检测到key文件不存在时自动生成KEY
    // #if 0则要求key文件必须存在版本才能起
    #if 0
    fd = open("/mnt/key", O_RDWR);
    if(fd < 0)
    {
        LOG(LOG_ERROR, "original key not found.\n");
        strncpy(szPlainText, szBuffer, sizeof(szPlainText));
        LOG(LOG_ERROR, "szPlainText:%s, szBuffer:%s.\n", szPlainText, szBuffer);
        if(0 < Encrypt(szPlainText, strlen(szPlainText), szCipherTxt))
        {
            fd = open("/mnt/key", O_RDWR);
            if(fd < 0)
            {
                LOG(LOG_ERROR, "open key file failed.\n");
                return ERR;
            }
        }
    }
    #else 
    fd = open("/mnt/key", O_RDWR);
    if(fd < 0)
    {
        LOG(LOG_ERROR, "open key file failed.\n");
        return ERR;
    }
    #endif
    ret = read(fd, szCipherTxt, CIPHER_TEXT_LEN);
    if(ret != CIPHER_TEXT_LEN)
    {
        close(fd);
        LOG(LOG_ERROR, "can't read key\n");
        return ERR;
    }
    close(fd);
    LOG(LOG_DEBUG, "szPlainText:%s, szBuffer:%s.\n", szPlainText, szBuffer);

    //解密
    pcKey = (char *)DecryptKey(szCipherTxt, CIPHER_TEXT_LEN);
    if(NULL == pcKey)
    {
        LOG(LOG_ERROR, "Decrypt failed.\n");
        return ERR;
    }  
    LOG(LOG_DEBUG, "szPlainText:%s, szBuffer:%s.\n", szPlainText, szBuffer);

    //校验结果
    if(strcmp(szBuffer, pcKey))
    {
        LOG(LOG_ERROR, "check key is invaild, buffer:%s, key:%s.\n",szBuffer ,pcKey);
        free(pcKey);
        return ERR;
    }
    free(pcKey);
    return OK;
}

代码略长,稍作解释。CheckKey()通过ESP_GetMacAddr()获取设备的MAC地址,读取/mnt/key文件,用DecryptKey()接口对文件中的密文进行解密,将解密获取的字符串与MAC地址进行比较,若一致,接口返回成功,反之返回失败。

key文件用notepad++打开,更换多种编码方式打开,始终是乱码。

当/mnt/key文件不存在时有如下打印:
open key file fail.
当/mnt/key文件存在时有如下打印:
szPlainText:, szBuffer:11-22-33-44-55-66FA-TOP-ROOF.
key is -108535744, iv is -108535712.
p_len is 16, f_len is 12, pcPlaintext is 11-22-33-44-55-66FA-TOP-ROOF.
szPlainText:, szBuffer:11-22-33-44-55-66FA-TOP-ROOF.

加密接口实现

现在需要实现CheckKey()接口 “#if 0” 中的Encrypt()加密接口内容,实现后改为“#if 1”

上代码

/***************************************************************************
function: Encrypt
input:
output:
Description:加密获取key
****************************************************************************/
int Encrypt(unsigned char *ucPlaintext, int ucPlaintextLen, unsigned char *ucCiphertext)
{
    int   ret           = ESP_ERR;
    int   p_len         = 0;
    int   f_len         = 0;
    int   ciphertextLen = 0;
    uchar key[32]       = {0};
    uchar iv[32]        = {0};
    uint  szSalt[]      = {11111,11111};
    int   nrounds       = 7;
    char  szKey[32]     = {0};

    EVP_CIPHER_CTX *pMdCtx;

    pMdCtx = EVP_CIPHER_CTX_new();
    if(NULL == pMdCtx)
    {
        LOG(LOG_ERROR, "alloc ctx failed.\n");
        return ERR;
    }

    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)
    {
        LOG(LOG_ERROR, "Key size is %d bits, it should be 256 bits\n", ret);
        EVP_CIPHER_CTX_free(pMdCtx);
        return ERR;
    }
    LOG(LOG_DEBUG, "key is %d, iv is %d.\n", key, iv);

    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;

    LOG(LOG_DEBUG, "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("/mnt/key", "w");
    if(fp == NULL)
    {
        LOG(LOG_ERROR, "generate key file failed!\n");
        return ERR;
    }

    fprintf(fp, "%s", ucCiphertext);
    fclose(fp);

    return ciphertextLen;
}

接口实现后,打印如下:
original key not found.
szPlainText:11-22-33-44-55-66FA-TOP-ROOF, szBuffer:11-22-33-44-55-66FA-TOP-ROOF.
key is -22147120, iv is -22147088.
p_len is 16, f_len is 16, ucCiphertext is ⚌r⚌A⚌⚌6⚌}⚌48⚌⚌⚌⚌c<P⚌⚌⚌⚌@P⚌_||.
szPlainText:11-22-33-44-55-66FA-TOP-ROOF, szBuffer:.
key is -22147088, iv is -22147056.
p_len is 16, f_len is 12, pcPlaintext is 11-22-33-44-55-66FA-TOP-ROOF.
szPlainText:11-22-33-44-55-66FA-TOP-ROOF, szBuffer:.
check key is invaild, buffer:, key:11-22-33-44-55-66FA-TOP-ROOF.
check key failed.

加密接口中怀疑有数组越界,导致buffer变量丢失,进而check key failed.但是接口成功生成了/mnt/key文件。(热心的朋友可以帮忙查一下哪里越界了)

再次运行程序,打印如下,
szPlainText:, szBuffer:11-22-33-44-55-66FA-TOP-ROOF.
key is -939246448, iv is -939246416.
p_len is 16, f_len is 12, pcPlaintext is 11-22-33-44-55-66FA-TOP-ROOF.
szPlainText:, szBuffer:11-22-33-44-55-66FA-TOP-ROOF.

ESP_CheckKey()接口返回成功,验证上一次生成的/mnt/key文件可用。

参考

https://blog.csdn.net/substitute_coder/article/details/53072100

标签:LOG,openssl,len,C语言,key,EVP,szBuffer,pcKey
来源: https://blog.csdn.net/qqq1112345/article/details/122594530

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有