OpenSSL EVP详解

OpenSSL EVP详解

  • Chapter1 OpenSSL EVP详解
    • 一、EVP基本介绍
      • 1. EVP 加密和解密
      • 2. EVP 签名和验证
      • 3. EVP 加解密文件
    • 二、源码结构
      • 2.1 全局函数
      • 2.2 BIO扩充
      • 2.3 摘要算法EVP封装
      • 2.4 对称算法EVP封装
      • 2.5 非对称算法EVP封装
      • 2.6 基于口令的加密
    • 三、开发实例
      • 3.1 示例1
      • 3.2 示例2
      • 3.3 示例3
      • 3.4 示例4
  • Chapter2 openssl之EVP实现哈希(md5,sha256,sm3)
    • 一、环境说明
    • 二、功能说明
    • 三、EVP接口说明
    • 四、使用实例
      • 4.1 MD5算法实现实例。
      • 4.2 sha256算法实现实例。
      • 4.3 sm3算法实现实例。
    • 五、源码地址
  • Chapter3 OpenSSL之EVP(二)——EVP系列函数介绍


国密算法
国密即国家密码局认定的国产密码算法。主要有SM1,SM2,SM3,SM4。密钥长度和分组长度均为128位。

1、SM1 为对称加密。其加密强度与AES相当。该算法不公开,调用该算法时,需要通过加密芯片的接口进行调用。
2、SM2为非对称加密,基于ECC。该算法已公开。由于该算法基于ECC,故其签名速度与秘钥生成速度都快于RSA。ECC 256位(SM2采用的就是ECC 256位的一种)安全强度比RSA 2048位高,但运算速度快于RSA。
3、SM3 消息摘要。可以用MD5作为对比理解。该算法已公开。校验结果为256位,本身是散列算法,官方叫杂凑类算法,根据SHA256算法改进而来。
4、SM4 无线局域网标准的分组数据算法。对称加密,密钥长度和分组长度均为128位。

5、SM7适用于非接触式IC卡的对称算法,秘钥长度128bit
6、SM9是标识算法,支持加密、签名、交换。

国密芯片算法一般是对称算法,密钥不公开,当使用特定的芯片进行SM1或其他国密算法加密时,若用多个线程调用加密卡的API时,要考虑芯片对于多线程的支持情况。


EVP函数提供一个高层次的接口OpenSSL加密功能。

他们提供以下功能:

一个一致的接口,不管底层算法或模式
支持众多的算法
加密/解密使用对称和非对称算法
签名/验证
导出密钥
安全散列函数
消息认证码
支持独立的加密引擎


BIO是IO函数的抽象,对应用屏蔽底层IO细节,有2种类型的BIO:数据源BIO,过滤器BIO。

数据源BIO:内存、文件、网络等;

过滤器BIO:消息摘要、加密、解密等;

EVP是高级加密接口,封装了消息摘要、加密解密、签名验签等,对外提供统一接口,推荐使用EVP接口。


Openssl EVP(high-level cryptographic functions[1])提供了丰富的密码学中的各种函数。Openssl 中实现了各种对称算法、摘要算法以及签名/验签算法。EVP 函数将这些具体的算法进行了封装。
  EVP系列的函数的声明包含在”evp.h”里面,这是一系列封装了openssl>加密库里面所有算法的函数。通过这样的统一的封装,使得只需要在初始化参数的时候做很少的改变,就可以使用相同的代码但采用不同的加密算法进行数据的加密和解密。
  EVP系列函数主要封装了加密、摘要、编码三大类型的算法,使用算法前需要调用OpenSSL_add_all_algorithms函数。
  其中以加密算法与摘要算法为基本,公开密钥算法是对数据加密采用了对称加密算法,对密钥采用非对称加密(公钥加密,私钥解密)。数字签名是非对称算法(私钥签名,公钥认证)。
  EVP 主要封装了如下功能函数:

实现了base64 编解码BIO;
实现了加解密BIO;
实现了摘要BIO;
实现了reliable BIO;
封装了摘要算法;
封装了对称加解密算法;
封装了非对称密钥的加密(公钥)、解密(私钥)、签名与验证以及辅助函数;
基于口令的加密(PBE);
对称密钥处理;
数字信封:数字信封用对方的公钥加密对称密钥,数据则用此对称密钥加密。发送给对方时,同时发送对称密钥密文和数据密文。接收方首先用自己的私钥解密密钥密文,得到对称密钥,然后用它解密数据。
其他辅助函数。


Chapter1 OpenSSL EVP详解

原文链接:https://blog.csdn.net/ARV000/article/details/134681320

EVP(Enveloped Public Key)是 OpenSSL 中用于提供对称加密、非对称加密和哈希功能的高级加
密接口。EVP 库提供了一个抽象的加密框架,使得可以在不同的算法实现之间进行切换,而不需要改变应用程序的代码。以下是一些 EVP 开发的主要方面:

一、EVP基本介绍

1. EVP 加密和解密

EVP 提供了通用的加密和解密函数,可以用于对称加密和非对称加密。一般的流程如下:

  • 选择加密算法,创建相应的 EVP_CIPHER 结构。
  • 初始化 EVP_CIPHER_CTX 上下文。
  • 使用 EVP_EncryptInit_ex 或 EVP_DecryptInit_ex 初始化加密或解密操作。
  • 使用 EVP_EncryptUpdate 或 EVP_DecryptUpdate 处理数据。
  • 使用 EVP_EncryptFinal_ex 或 EVP_DecryptFinal_ex 完成加密或解密操作。
EVP_CIPHER_CTX *ctx;
const EVP_CIPHER *cipher = EVP_aes_256_cbc();

ctx = EVP_CIPHER_CTX_new();

EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv);
EVP_EncryptUpdate(ctx, ciphertext, &outlen, plaintext, plaintext_len);
EVP_EncryptFinal_ex(ctx, ciphertext + outlen, &tmplen);

EVP_CIPHER_CTX_free(ctx);

2. EVP 签名和验证

EVP 提供了通用的签名和验证函数,可以用于不同的哈希算法和签名算法。一般的流程如下:

选择哈希算法和签名算法,创建相应的 EVP_MD 和 EVP_PKEY 结构。

  • 初始化 EVP_MD_CTX 上下文。
  • 使用 EVP_DigestInit_ex 初始化哈希操作。
  • 使用 EVP_DigestUpdate 处理数据。
  • 使用 EVP_DigestFinal_ex 完成哈希操作。
  • 使用 EVP_SignInit_ex 或 EVP_VerifyInit_ex 初始化签名或验证操作。
  • 使用 EVP_SignUpdate 或 EVP_VerifyUpdate 处理数据。
  • 使用 EVP_SignFinal 或 EVP_VerifyFinal 完成签名或验证操作。
EVP_MD_CTX *mdctx;
const EVP_MD *md = EVP_sha256();
EVP_PKEY *pkey;
unsigned char signature[256];
size_t sig_len;

mdctx = EVP_MD_CTX_new();
pkey = load_private_key();

EVP_DigestInit_ex(mdctx, md, NULL);
EVP_DigestUpdate(mdctx, data, data_len);
EVP_DigestFinal_ex(mdctx, digest, &digest_len);

EVP_SignInit_ex(mdctx, EVP_sha256(), NULL);
EVP_SignUpdate(mdctx, data, data_len);
EVP_SignFinal(mdctx, signature, &sig_len, pkey);

EVP_MD_CTX_free(mdctx);

3. EVP 加解密文件

EVP 还提供了一些便捷的函数用于对文件进行加解密操作,例如 EVP_EncryptInit_ex、EVP_DecryptUpdate、EVP_EncryptFinal_ex 等。

EVP_CIPHER_CTX *ctx;
const EVP_CIPHER *cipher = EVP_aes_256_cbc();

ctx = EVP_CIPHER_CTX_new();

EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv);
EVP_EncryptUpdate(ctx, ciphertext, &outlen, plaintext, plaintext_len);
EVP_EncryptFinal_ex(ctx, ciphertext + outlen, &tmplen);

EVP_CIPHER_CTX_free(ctx);

这些是 EVP 库的一些基本用法。具体的使用取决于你的需求,可以根据 OpenSSL 的文档和示例代码进一步了解。

二、源码结构

evp源码位于crypto/evp目录,可以分为如下几类:

2.1 全局函数

主要包括c_allc.c、c_alld.c、c_all.c以及names.c。他们加载openssl支持的所有的对称算法和摘要算法,放入到哈希表中。实现了OpenSSL_add_all_digests、OpenSSL_add_all_ciphers以及OpenSSL_add_all_algorithms(调用了前两个函数)函数。在进行计算时,用户也可以单独加载摘要函数(EVP_add_digest)和对称计算函数(EVP_add_cipher)。

2.2 BIO扩充

包括bio_b64.c、bio_enc.c、bio_md.c和bio_ok.c,各自实现了BIO_METHOD方法,分别用于base64编解码、对称加解密以及摘要。

2.3 摘要算法EVP封装

由digest.c实现,实现过程中调用了对应摘要算法的回调函数。各个摘要算法提供了自己的EVP_MD静态结构,对应源码为m_xxx.c。

2.4 对称算法EVP封装

由evp_enc.c实现,实现过程调用了具体对称算法函数,实现了Update操作。各种对称算法都提供了一个EVP_CIPHER静态结构,对应源码为e_xxx.c。需要注意的是,e_xxx.c中不提供完整的加解密运算,它只提供基本的对于一个block_size数据的计算,完整的计算由evp_enc.c来实现。当用户想添加一个自己的对称算法时,可以参考e_xxx.c的实现方式。一般用户至少需要实现如下功能:

Ø 构造一个新的静态的EVP_CIPHER结构;

Ø 实现EVP_CIPHER结构中的init函数,该函数用于设置iv,设置加解密标记、以及根据外送密钥生成自己的内部密钥;

Ø 实现do_cipher函数,该函数仅对block_size字节的数据进行对称运算;

Ø 实现cleanup函数,该函数主要用于清除内存中的密钥信息。

2.5 非对称算法EVP封装

主要是以p_开头的文件。其中,p_enc.c封装了公钥加密;p_dec.c封装了私钥解密;p_lib.c实现一些辅助函数;p_sign.c封装了签名函数;p_verify.c封装了验签函数;p_seal.c封装了数字信封;p_open.c封装了解数字信封。

2.6 基于口令的加密

包括p5_crpt2.c、p5_crpt.c和evp_pbe.c。

三、开发实例

3.1 示例1

#include <string.h>

#include <openssl/evp.h>

int    main()

{

       int                                ret,which=1;

       EVP_CIPHER_CTX             ctx;

       const EVP_CIPHER      *cipher;

       unsigned char        key[24],iv[8],in[100],out[108],de[100];

       int                                i,len,inl,outl,total=0;

      

       for(i=0;i<24;i++)

       {

              memset(&key[i],i,1);

       }

       for(i=0;i<8;i++)

       {

              memset(&iv[i],i,1);

       }

       for(i=0;i<100;i++)

       {

              memset(&in[i],i,1);

       }

       EVP_CIPHER_CTX_init(&ctx);

       printf("please select :\n");

       printf("1: EVP_des_ede3_ofb\n");

       printf("2: EVP_des_ede3_cbc\n");

       scanf("%d",&which);

       if(which==1)

              cipher=EVP_des_ede3_ofb();

       else

              cipher=EVP_des_ede3_cbc();

       ret=EVP_EncryptInit_ex(&ctx,cipher,NULL,key,iv);

       if(ret!=1)

       {

              printf("EVP_EncryptInit_ex err1!\n");

              return -1;

       }

       inl=50;

       len=0;

       EVP_EncryptUpdate(&ctx,out+len,&outl,in,inl);

       len+=outl;

       EVP_EncryptUpdate(&ctx,out+len,&outl,in+50,inl);

       len+=outl;

       EVP_EncryptFinal_ex(&ctx,out+len,&outl);

       len+=outl;

       printf("加密结果长度:%d\n",len);

       /* 解密 */

       EVP_CIPHER_CTX_cleanup(&ctx);

       EVP_CIPHER_CTX_init(&ctx);

       ret=EVP_DecryptInit_ex(&ctx,cipher,NULL,key,iv);

       if(ret!=1)

       {

              printf("EVP_DecryptInit_ex err1!\n");

              return -1;

       }

       total=0;

       EVP_DecryptUpdate(&ctx,de+total,&outl,out,44);

       total+=outl;

       EVP_DecryptUpdate(&ctx,de+total,&outl,out+44,len-44);

       total+=outl;

       ret=EVP_DecryptFinal_ex(&ctx,de+total,&outl);

       total+=outl;

       if(ret!=1)

       {

              EVP_CIPHER_CTX_cleanup(&ctx);

              printf("EVP_DecryptFinal_ex err\n");

              return -1;

       }

       if((total!=100) || (memcmp(de,in,100)))

       {

              printf("err!\n");

              return -1;

       }

       EVP_CIPHER_CTX_cleanup(&ctx);

       printf("test ok!\n");

       return 0;

}

输出结果如下:

please select :

1: EVP_des_ede3_ofb

2: EVP_des_ede3_cbc

1

加密结果长度:100

test ok!

please select :

1: EVP_des_ede3_ofb

2: EVP_des_ede3_cbc

2

加密结果长度:104

test ok!

3.2 示例2

#include <string.h>

#include <openssl/evp.h>

int    main()

{

       int                                cnid,ret,i,msize,mtype;

       int                                mpktype,cbsize,mnid,mbsize;

       const EVP_CIPHER      *type;

       const EVP_MD             *md;

       int                                datal,count,keyl,ivl;

       unsigned char        salt[20],data[100],*key,*iv;

       const      char        *cname,*mname;



       type=EVP_des_ecb();

       cnid=EVP_CIPHER_nid(type);

       cname=EVP_CIPHER_name(type);

       cbsize=EVP_CIPHER_block_size(type);

       printf("encrypto nid : %d\n",cnid);

       printf("encrypto name: %s\n",cname);

       printf("encrypto bock size : %d\n",cbsize);

       md=EVP_md5();

       mtype=EVP_MD_type(md);

       mnid=EVP_MD_nid(md);

       mname=EVP_MD_name(md);

       mpktype=EVP_MD_pkey_type(md);

       msize=EVP_MD_size(md);

       mbsize=EVP_MD_block_size(md);

       printf("md info : \n");

       printf("md type  : %d\n",mtype);

       printf("md nid  : %d\n",mnid);

       printf("md name : %s\n",mname);

       printf("md pkey type : %d\n",mpktype);

       printf("md size : %d\n",msize);

       printf("md block size : %d\n",mbsize);



       keyl=EVP_CIPHER_key_length(type);

       key=(unsigned char *)malloc(keyl);

       ivl=EVP_CIPHER_iv_length(type);

       iv=(unsigned char *)malloc(ivl);

       for(i=0;i<100;i++)

              memset(&data[i],i,1);

       for(i=0;i<20;i++)

              memset(&salt[i],i,1);

       datal=100;

       count=2;

       ret=EVP_BytesToKey(type,md,salt,data,datal,count,key,iv);

       printf("generate key value: \n");

       for(i=0;i<keyl;i++)

              printf("%x ",*(key+i));

       printf("\n");

       printf("generate iv value: \n");

       for(i=0;i<ivl;i++)

              printf("%x ",*(iv+i));

       printf("\n");

       return 0;

}

EVP_BytesToKey函数通过salt以及data数据来生成所需要的key和iv。

输出:

encrypto nid : 29

encrypto name: DES-ECB

encrypto bock size : 8

md info :

md type  : 4

md nid  : 4

md name : MD5

md pkey type : 8

md size : 16

md block size : 64

generate key value:

54 0 b1 24 18 42 8d dd

generate iv value:

ba 7d c3 97 a0 c9 e0 70

3.3 示例3

#include <openssl/evp.h>

#include <openssl/rsa.h>

int    main()

{

       int                         ret,inlen,outlen=0;

       unsigned long  e=RSA_3;

       char               data[100],out[500];

       EVP_MD_CTX             md_ctx,md_ctx2;

       EVP_PKEY            *pkey;

       RSA                      *rkey;

       BIGNUM               *bne;

 

       /* 待签名数据*/

       strcpy(data,"openssl 编程作者:赵春平");

       inlen=strlen(data);

       /* 生成RSA密钥*/

       bne=BN_new();

       ret=BN_set_word(bne,e);

       rkey=RSA_new();

       ret=RSA_generate_key_ex(rkey,1024,bne,NULL);

       if(ret!=1)  goto err;

       pkey=EVP_PKEY_new();

       EVP_PKEY_assign_RSA(pkey,rkey);

       /* 初始化*/

       EVP_MD_CTX_init(&md_ctx);

       ret=EVP_SignInit_ex(&md_ctx,EVP_md5(), NULL);

       if(ret!=1)goto err;

       ret=EVP_SignUpdate(&md_ctx,data,inlen);

       if(ret!=1)goto err;

       ret=EVP_SignFinal(&md_ctx,out,&outlen,pkey);

       /* 验证签名*/

       EVP_MD_CTX_init(&md_ctx2);

       ret=EVP_VerifyInit_ex(&md_ctx2,EVP_md5(), NULL);

       if(ret!=1) goto err;

       ret=EVP_VerifyUpdate(&md_ctx2,data,inlen);

       if(ret!=1) goto err;

       ret=EVP_VerifyFinal(&md_ctx2,out,outlen,pkey);

       if(ret==1)

              printf("验证成功\n");

       else

              printf("验证错误\n");

err:

       RSA_free(rkey);

       BN_free(bne);

       return 0;

}

3.4 示例4

#include <openssl/evp.h>

#include <openssl/rsa.h>

int    main()

{

       int                         ret,ekl[2],npubk,inl,outl,total=0,total2=0;

       unsigned long  e=RSA_3;

       char               *ek[2],iv[8],in[100],out[500],de[500];

       EVP_CIPHER_CTX      ctx,ctx2;

       EVP_CIPHER        *type;

       EVP_PKEY            *pubkey[2];

       RSA                      *rkey;

       BIGNUM               *bne;

      

       /* 生成RSA密钥*/

       bne=BN_new();

       ret=BN_set_word(bne,e);

       rkey=RSA_new();

       ret=RSA_generate_key_ex(rkey,1024,bne,NULL);

       pubkey[0]=EVP_PKEY_new();

       EVP_PKEY_assign_RSA(pubkey[0],rkey);

       type=EVP_des_cbc();

       npubk=1;

       EVP_CIPHER_CTX_init(&ctx);

       ek[0]=malloc(500);

       ek[1]=malloc(500);

       ret=EVP_SealInit(&ctx,type,ek,ekl,iv,pubkey,1);  /* 只有一个公钥*/

       if(ret!=1) goto err;

       strcpy(in,"openssl 编程");

       inl=strlen(in);

       ret=EVP_SealUpdate(&ctx,out,&outl,in,inl);

       if(ret!=1)goto err;

       total+=outl;

       ret=EVP_SealFinal(&ctx,out+outl,&outl);

       if(ret!=1) goto err;

       total+=outl;

 

       memset(de,0,500);

       EVP_CIPHER_CTX_init(&ctx2);

       ret=EVP_OpenInit(&ctx2,EVP_des_cbc(),ek[0],ekl[0],iv,pubkey[0]);

       if(ret!=1) goto err;

       ret=EVP_OpenUpdate(&ctx2,de,&outl,out,total);

       total2+=outl;

       ret=EVP_OpenFinal(&ctx2,de+outl,&outl);

       total2+=outl;

       de[total2]=0;

       printf("%s\n",de);

err:

       free(ek[0]);

       free(ek[1]);

       EVP_PKEY_free(pubkey[0]);

       BN_free(bne);

       getchar();

       return 0;

}
输出结果:openssl 编程

Chapter2 openssl之EVP实现哈希(md5,sha256,sm3)

原文链接:https://blog.csdn.net/arv002/article/details/130135979

一、环境说明

操作系统:linux(debian)

开发工具:Qt creator 4.8.2

Qt版本:5.11.3.45-1

openssl版本:openssl-3.1.0

二、功能说明

1、使用openssl的EVP接口开发对数据进行hash。算法包括:md5、sha256、sm3

2、使用openssl的EVP接口开发对文件进行hash。算法包括:md5、sha256、sm3

三、EVP接口说明

使用EVP的接口有以下几个:EVP_MD_CTX_new,EVP_DigestInit_ex,EVP_DigestUpdate,EVP_DigestFinal_ex,EVP_MD_CTX_free

函数名称 函数说明
EVP_MD_CTX 摘要上下文对象结构
EVP_MD_CTX * EVP_MD_CTX_new(void) 创建摘要上下文对象
int EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type) 初始化摘要上下文,type为摘要算法抽象集合。
int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *d, size_t cnt);
向摘要计算的海棉结构输入一段数据,多次输入表示数据累计。

成功返回1,失败返回0

int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *s); 生成最终摘要,输出摘要值和长度。
成功返回1,失败返回0。
int EVP_MD_CTX_free(EVP_MD_CTX *ctx) 释放摘要上下文对象

四、使用实例

4.1 MD5算法实现实例。

实现了对数据的MD5散列,以及对文件内容MD5散列。

#include "encytion_md5.h"
#include <QtDebug>
#include <QFile>
#include <QDataStream>
#include <openssl/dh.h>
#include <openssl/evp.h>
#include <openssl/md5.h>
#include "tools.h"
EncytionMD5::EncytionMD5(QObject *) {}
 
QString EncytionMD5::EncytonData(QString string)
{
    qInfo() << "EncytionMD5 " << string;
 
    unsigned int len = 0;
    EVP_MD_CTX *ctx = EVP_MD_CTX_new();
    EVP_DigestInit_ex(ctx, EVP_md5(), nullptr);
    // hash计算
    EVP_DigestUpdate(ctx, string.toStdString().c_str(),
                     string.toStdString().length());
    unsigned char result[MD5_DIGEST_LENGTH] = {};
    EVP_DigestFinal_ex(ctx, result, &len);
    QString res = Tools::CharToHex(result, len);
    EVP_MD_CTX_free(ctx);
    return res;
}
 
QString EncytionMD5::EncytonFile(QString inFilePath)
{
    qInfo() << "EncytionMD5 EncytonFile" << inFilePath;
    unsigned int len = 0;
 
    EVP_MD_CTX *ctx = EVP_MD_CTX_new();
    EVP_DigestInit_ex(ctx, EVP_md5(), nullptr);
    FILE *fp = nullptr;
    fp = fopen(inFilePath.toStdString().c_str(), "rb+");
    if (nullptr == fp) {
        return "";
    }
    char szDataBuff[MD5_DIGEST_LENGTH];
    unsigned long nLineLen;
    unsigned long a = 1;
    while (!feof(fp)) {
        memset(szDataBuff, 0x00, sizeof(szDataBuff));
        nLineLen = fread(szDataBuff, a,
                         static_cast<unsigned long>(MD5_DIGEST_LENGTH), fp);
        if (nLineLen) {
            EVP_DigestUpdate(ctx, szDataBuff, static_cast<int>(nLineLen));
        }
    }
    fclose(fp);
    unsigned char result[MD5_DIGEST_LENGTH] = {};
    EVP_DigestFinal_ex(ctx, result, &len);
    EVP_MD_CTX_free(ctx);
    QString res = Tools::CharToHex(result, len);
    return res;
}

4.2 sha256算法实现实例。

实现了对数据的sha256散列,以及对文件内容sha256散列。

#include "encytion_sha256.h"
#include <QtDebug>
#include <QtDebug>
#include <openssl/dh.h>
#include <openssl/evp.h>
#include <openssl/sha.h>
#include "tools.h"
EncytionSha256::EncytionSha256(QObject *) {}
// 数据加密
QString EncytionSha256::EncytonData(QString string)
{
    qInfo() << "EncytionSha256 " << string;
 
    unsigned int len = 0;
    EVP_MD_CTX *ctx = EVP_MD_CTX_new();
    EVP_DigestInit_ex(ctx, EVP_sha256(), nullptr);
    // hash计算
    EVP_DigestUpdate(ctx, string.toStdString().c_str(),
                     string.toStdString().length());
    unsigned char result[SHA256_DIGEST_LENGTH] = {};
    EVP_DigestFinal_ex(ctx, result, &len);
    QString res = Tools::CharToHex(result, len);
    EVP_MD_CTX_free(ctx);
    return res;
}
// 文件加密,返回加密内容
QString EncytionSha256::EncytonFile(QString inFilePath)
{
    qInfo() << "EncytionSha256 EncytonFile" << inFilePath;
    unsigned int len = 0;
 
    EVP_MD_CTX *ctx = EVP_MD_CTX_new();
    EVP_DigestInit_ex(ctx, EVP_sha256(), nullptr);
    FILE *fp = nullptr;
    fp = fopen(inFilePath.toStdString().c_str(), "rb+");
    if (nullptr == fp) {
        return "";
    }
    char szDataBuff[SHA256_DIGEST_LENGTH];
    unsigned long nLineLen;
    unsigned long a = 1;
    while (!feof(fp)) {
        memset(szDataBuff, 0x00, sizeof(szDataBuff));
        nLineLen = fread(szDataBuff, a,
                         static_cast<unsigned long>(SHA256_DIGEST_LENGTH), fp);
        if (nLineLen) {
            EVP_DigestUpdate(ctx, szDataBuff, static_cast<int>(nLineLen));
        }
    }
    fclose(fp);
    unsigned char result[SHA256_DIGEST_LENGTH] = {};
    EVP_DigestFinal_ex(ctx, result, &len);
    EVP_MD_CTX_free(ctx);
    QString res = Tools::CharToHex(result, len);
    return res;
}

4.3 sm3算法实现实例。

实现了对数据的sm3散列,以及对文件内容sm3散列。

#include "encytion_sm3.h"
#include <QtDebug>
#include <openssl/dh.h>
#include <openssl/evp.h>
#include <openssl/sm3.h>
#include "tools.h"
EncytionSM3::EncytionSM3(QObject *) {}
 
QString EncytionSM3::EncytonData(QString string)
{
    qInfo() << "EncytionSM3 " << string;
 
    unsigned int len = 0;
    EVP_MD_CTX *ctx = EVP_MD_CTX_new();
    EVP_DigestInit_ex(ctx, EVP_sm3(), nullptr);
    // hash计算
    EVP_DigestUpdate(ctx, string.toStdString().c_str(),
                     string.toStdString().length());
    unsigned char result[SM3_DIGEST_LENGTH] = {};
    EVP_DigestFinal_ex(ctx, result, &len);
    //    EVP_MD_CTX_init(&ctx);
    QString res = Tools::CharToHex(result, len);
    EVP_MD_CTX_free(ctx);
    return res;
}
 
QString EncytionSM3::EncytonFile(QString inFilePath)
{
    qInfo() << "EncytionSha256 EncytonFile" << inFilePath;
    unsigned int len = 0;
 
    EVP_MD_CTX *ctx = EVP_MD_CTX_new();
    EVP_DigestInit_ex(ctx, EVP_sm3(), nullptr);
    FILE *fp = nullptr;
    fp = fopen(inFilePath.toStdString().c_str(), "rb+");
    if (nullptr == fp) {
        return "";
    }
    char szDataBuff[SM3_DIGEST_LENGTH];
    unsigned long nLineLen;
    unsigned long a = 1;
    while (!feof(fp)) {
        memset(szDataBuff, 0x00, sizeof(szDataBuff));
        nLineLen = fread(szDataBuff, a,
                         static_cast<unsigned long>(SM3_DIGEST_LENGTH), fp);
        if (nLineLen) {
            EVP_DigestUpdate(ctx, szDataBuff, static_cast<int>(nLineLen));
        }
    }
    fclose(fp);
    unsigned char result[SM3_DIGEST_LENGTH] = {};
    EVP_DigestFinal_ex(ctx, result, &len);
    EVP_MD_CTX_free(ctx);
    QString res = Tools::CharToHex(result, len);
    return res;
}

五、源码地址

GitHub - arv000/cipher: linux操作系统,使用openssl实现加密解密功能。

Chapter3 OpenSSL之EVP(二)——EVP系列函数介绍

原文链接:https://blog.csdn.net/scuyxi/article/details/60621075

EVP系列函数
摘要函数
典型的摘要函数主要有:
1) EVP_md5
返回 md5 的 EVP_MD。
2) EVP_sha1
返回 sha1 的 EVP_MD。
3) EVP_sha256
返回 sha256 的 EVP_MD。
4) EVP_DigestInit
摘要初使化函数,需要有 EVP_MD 作为输入参数。
5) EVP_DigestUpdate 和 EVP_DigestInit_ex
摘要 Update 函数,用于进行多次摘要。
6) EVP_DigestFinal 和 EVP_DigestFinal_ex
摘要 Final 函数,用户得到最终结果。
7) EVP_Digest
对一个数据进行摘要,它依次调用了上述三个函数。

对称加解密函数
典型的加解密函数主要有:
1) EVP_CIPHER_CTX_init
初始化对称计算上下文。
2) EVP_CIPHER_CTX_cleanup
清除对称算法上下文数据, 它调用用户提供的销毁函数销清除内部密钥以及其他数据。
3) EVP_des_ede3_ecb
返回一个 EVP_CIPHER;
4) EVP_EncryptInit 和 EVP_EncryptInit_ex
加密初始化函数,本函数调用具体算法的 init 回调函数,将外送密钥 key 转换为内部密钥形式,将初始化向量 iv 拷贝到 ctx 结构中。
5) EVP_EncryptUpdate
加密函数,用于多次计算,它调用了具体算法的 do_cipher 回调函数。
6) EVP_EncryptFinal 和 EVP_EncryptFinal_ex
获取加密结果,函数可能涉及填充,它调用了具体算法的do_cipher 回调函数。
7) EVP_DecryptInit 和 EVP_DecryptInit_ex
解密初始化函数。
8) EVP_DecryptUpdate
解密函数,用于多次计算,它调用了具体算法的 do_cipher 回调函数。
9) EVP_DecryptFinal 和 EVP_DecryptFinal_ex
获取解密结果,函数可能涉及去填充,它调用了具体算法的do_cipher 回调函数。
10) EVP_BytesToKey
计算密钥函数,它根据算法类型、摘要算法、 salt 以及输入数据计算出一个对称密钥和初始化向量 iv。
11) PKCS5_PBE_keyivgen 和 PKCS5_v2_PBE_keyivgen
实现了 PKCS5 基于口令生成密钥和初始化向量的算法。
12) PKCS5_PBE_add
加载所有 openssl 实现的基于口令生成密钥的算法。
13) EVP_PBE_alg_add
添加一个 PBE 算法。

对称加密过程
对称加密过程如下:

1) EVP_EncryptInit:
设置 buf_len 为 0,表明临时缓冲区 buf 没有数据。

2) EVP_EncryptUpdate:
ctx 结构中的 buf 缓冲区用于存放上次 EVP_EncryptUpdate 遗留下来的未加密的数据, buf_len 指明其长度。如果 buf_len 为 0,加密的时候先加密输入数据的整数倍,将余下的数据拷贝到 buf 缓冲区。如果 buf_len 不为 0,先加密 buf 里面的数据和输入数据的一部分(凑足一个分组的长度),然后用上面的方法加密,输出结
果是加过密的数据。

3) EVP_ EncryptFinal
加密 ctx 的 buf 中余下的数据,如果长度不够一个分组(分组长度不为 1),则填充,然后再加密,输出结果。
总之,加密大块数据(比如一个大的文件,多出调用 EVP_EncryptUpdate)的结果等效于将所有的数据一次性读入内存进行加密的结果。加密和解密时每次计算的数据块的大
小不影响其运算结果。

非对称函数
典型的非对称函数有:
1) EVP_PKEY_encrypt
公钥加密。
2) EVP_PKEY_decrypt
私钥解密。
3) EVP_PKEY_assign
设置 EVP_PKEY 中具体的密钥结构,使它代表该密钥。
4) EVP_PKEY_assign_RSA/ EVP_PKEY_set1_RSA
设置 EVP_PKEY 中的 RSA 密钥结构,使它代表该 RSA 密钥。
5) EVP_PKEY_get1_RSA
获取 EVP_PKEY 的 RSA 密钥结构。
6) EVP_SignFinal
签名操作,输入参数必须有私钥(EVP_PKEY)。
7) EVP_VerifyFinal
验证签名,输入参数必须有公钥(EVP_PKEY)。
8) int EVP_OpenInit(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type,const unsigned
char *ek, int ekl, const unsigned char *iv,EVP_PKEY *priv)
解数字信封初始化操作,type为对称加密算法,ek为密钥密文,ekl为密
钥密文长度,iv为填充值,priv为用户私钥。
9) EVP_OpenUpdate
做解密运算。
10) EVP_OpenFinal
做解密运算,解开数字信封。
11) int EVP_SealInit(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, unsigned char **ek,int *ekl, unsigned char *iv, EVP_PKEY **pubk, int npubk)
type为对称算法,ek数组用来存放多个公钥对密钥加密的结果,ekl用于存放ek数组中每个密钥密文的长度,iv为填充值,pubk数组用来存放多个公钥,npubk为公钥个数,本函数用多个公钥分别加密密钥,并做加密初始化。
12)EVP_SealUpdate
做加密运算。
13)EVP_SealFinal
做加密运算,制作数字信封。

BASE64 编解码函数

  1. EVP_EncodeInit
    BASE64 编码初始化。
  2. EVP_EncodeUpdate
    BASE64 编码,可多次调用。
  3. EVP_EncodeFinal
    BASE64 编码,并获取最终结果。
  4. EVP_DecodeInit
    BASE64 解码初始化。
  5. EVP_DecodeUpdate
    输入数据长度不能大于 80 字节。 BASE64 解码可多次调用,注意,本函数的输入数据不能太长。
  6. EVP_DecodeFinal
    BASE64 解码,并获取最终结果。
    7) EVP_EncodeBlock
    BASE64 编码函数,本函数可单独调用。
    8) EVP_DecodeBlock
    BASE64 解码,本函数可单独调用,对输入数据长度无要求。

其他函数
1) EVP_add_cipher
将对称算法加入到全局变量,以供调用。
2) EVP_add_digest
将摘要算法加入到全局变量中,以供调用
3) EVP_CIPHER_CTX_ctrl
对称算法控制函数,它调用了用户实现的 ctrl 回调函数。
4) EVP_CIPHER_CTX_set_key_length
当对称算法密钥长度为可变长时,设置对称算法的密钥长度。
5) EVP_CIPHER_CTX_set_padding
设置对称算法的填充,对称算法有时候会涉及填充。加密分组长度大于一时,用户输入数据不是加密分组的整数倍时,会涉及到填充。填充在最后一个分组来完成, openssl 分组填充时,如果有 n 个填充,则将最后一个分组用 n 来填满。
6) EVP_CIPHER_get_asn1_iv
获取原始 iv,存放在 ASN1_TYPE 结构中。
7) EVP_CIPHER_param_to_asn1
设置对称算法参数,参数存放在 ASN1_TYPE 类型中,它调用用户实现的回调函数 set_asn1_parameters 来实现。
8) EVP_CIPHER_type
获取对称算法的类型。
9) EVP_CipherInit/EVP_CipherInit_ex
对称算法计算(加/解密)初始化函数, _ex 函数多了硬件 enginge 参数,EVP_EncryptInit 和 EVP_DecryptInit 函数也调用本函数。
10) EVP_CipherUpdate
对称计算 (加/解密)函数, 它调用了 EVP_EncryptUpdate 和 EVP_DecryptUpdate函数。
11) EVP_CipherFinal/EVP_CipherFinal_ex
对 称 计 算 ( 加 / 解 ) 函 数 , 调 用 了 EVP_EncryptFinal (_ex ) 和EVP_DecryptFinal(_ex);本函数主要用来处理最后加密分组,可能会有对称计算。
12) EVP_cleanup
清除加载的各种算法,包括对称算法、摘要算法以及 PBE 算法,并清除这些算法相关的哈希表的内容。
13) EVP_get_cipherbyname
根据字串名字来获取一种对称算法(EVP_CIPHER),本函数查询对称算法哈希表。
14) EVP_get_digestbyname
根据字串获取摘要算法(EVP_MD),本函数查询摘要算法哈希表。
15) EVP_get_pw_prompt
获取口令提示信息字符串.
16) int EVP_PBE_CipherInit(ASN1_OBJECT *pbe_obj, const char *pass, int passlen,
ASN1_TYPE *param, EVP_CIPHER_CTX *ctx, int en_de)
PBE 初始化函数。本函数用口令生成对称算法的密钥和初始化向量,并作加/解密初始化操作。本函数再加上后续的 EVP_CipherUpdate 以及 EVP_CipherFinal_ex
构成一个完整的加密过程(可参考 crypto/p12_decr.c 的 PKCS12_pbe_crypt 函数) .
17) EVP_PBE_cleanup
删除所有的 PBE 信息,释放全局堆栈中的信息.
18) EVP_PKEY *EVP_PKCS82PKEY(PKCS8_PRIV_KEY_INFO *p8)
将 PKCS8_PRIV_KEY_INFO(x509.h 中定义)中保存的私钥转换为 EVP_PKEY结构。
19) EVP_PKEY2PKCS8/EVP_PKEY2PKCS8_broken
将 EVP_PKEY 结构中的私钥转换为 PKCS8_PRIV_KEY_INFO 数据结构存储。
20) EVP_PKEY_bits
非对称密钥大小,为比特数。
21) EVP_PKEY_cmp_parameters
比较非对称密钥的密钥参数,用于 DSA 和 ECC 密钥。
22) EVP_PKEY_copy_parameters
拷贝非对称密钥的密钥参数,用于 DSA 和 ECC 密钥。
23) EVP_PKEY_free
释放非对称密钥数据结构。
24) EVP_PKEY_get1_DH/EVP_PKEY_set1_DH
获取/设置 EVP_PKEY 中的 DH 密钥。
25) EVP_PKEY_get1_DSA/EVP_PKEY_set1_DSA
获取/设置 EVP_PKEY 中的 DSA 密钥。
26) EVP_PKEY_get1_RSA/EVP_PKEY_set1_RSA
获取/设置 EVP_PKEY 中结构中的 RSA 结构密钥。
27) EVP_PKEY_missing_parameters
检查非对称密钥参数是否齐全,用于 DSA 和 ECC 密钥。
28) EVP_PKEY_new
生成一个 EVP_PKEY 结构。
29) EVP_PKEY_size
获取非对称密钥的字节大小。
30) EVP_PKEY_type
获取 EVP_PKEY 中表示的非对称密钥的类型。
31) int EVP_read_pw_string(char *buf,int length,const char *prompt,int verify)
获取用户输入的口令;buf 用来存放用户输入的口令,length 为 buf 长度,prompt为提示给用户的信息,如果为空,它采用内置的提示信息, verify 为 0 时,不要求验证用户输入的口令,否则回要求用户输入两遍。返回 0 表示成功。
32) EVP_set_pw_prompt
设置内置的提示信息,用于需要用户输入口令的场合。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/767639.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Maya崩溃闪退常见原因及解决方案

Autodesk Maya 是一款功能强大的 3D 计算机图形程序&#xff0c;被电影、游戏和建筑等各个领域的设计师广泛使用。然而&#xff0c;Maya 就像任何其他软件一样可能会发生崩溃问题。在前文中&#xff0c;小编给大家介绍了3ds Max使用V-Ray渲染时的崩溃闪退解决方案&#xff1a; …

Xilinx FPGA:vivado关于单端ROM的一个只读小实验

一、实验要求 将生成好的voe文件里的数据使用rom读取出来&#xff0c;采用串口工具发送给电脑&#xff08;当按键来临时&#xff09;。 二、程序设计 按键消抖模块&#xff1a; timescale 1ns / 1ps module key_debounce(input sys_clk ,input rst_n…

vcruntime140_1.dll下载——修复vcruntime140_1.dll方法解析

vcruntime140_1.dll 是 Microsoft Visual C Redistributable 中的一个重要动态链接库文件。它在许多应用程序的正常运行中起着关键作用。然而&#xff0c;有时用户可能会遇到 vcruntime140_1.dll 丢失或损坏的情况&#xff0c;导致相关程序无法启动。在这种情况下&#xff0c;下…

代码随想录-二叉搜索树①

目录 二叉搜索树的定义 700. 二叉搜索树中的搜索 题目描述&#xff1a; 输入输出示例&#xff1a; 思路和想法&#xff1a; 98. 验证二叉搜索树 题目描述&#xff1a; 输入输出示例&#xff1a; 思路和想法&#xff1a; 530. 二叉搜索树的最小绝对差 题目描述&#x…

Windows Server 2008近源应急OS-1

前景需要&#xff1a;小王从某安全大厂被优化掉后&#xff0c;来到了某私立小学当起了计算机老师。某一天上课的时候&#xff0c;发现鼠标在自己动弹&#xff0c;又发现除了某台电脑&#xff0c;其他电脑连不上网络。感觉肯定有学生捣乱&#xff0c;于是开启了应急。 我们需要…

第三方软件测试公司分享:软件渗透测试的测试内容和注意事项

软件渗透测试是一种通过模拟攻击的方式来评估软件系统的安全性和漏洞&#xff0c;以发现并修复系统中的安全弱点。保护用户的数据和信息不被恶意攻击者利用&#xff0c;也是软件产品开发流程中重要的环节&#xff0c;可以帮助开发团队完善产品质量&#xff0c;提高用户满意度。…

VSG虚拟同步发电机simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 VSG虚拟同步发电机simulink建模与仿真&#xff0c;虚拟同步发电机&#xff08;Virtual Synchronous Generator, VSG&#xff09;技术是电力电子领域的一项重要创新&#xff0c…

Modbus RTU协议简介即CRC算法实现

目录 1 Modbus 介绍2 Modbus RTU协议传输方式2.1 地址码2.2 功能码2.3 数据码2.4 校验码 3 CRC算法实现2.1 代码3.2 运行结果 1 Modbus 介绍 Modbus是一种串行通信协议&#xff0c;是Modicon公司&#xff08;现在的施耐德电气 Schneider Electric&#xff09;于1979年为使用可编…

2024 6G无线通信AI大赛分享交流会暨颁奖典礼圆满落幕

7月1日&#xff0c;由IMT-2030(6G)推进组、IMT-2020(5G)推进组5G与AI融合研究任务组主办&#xff0c;OPPO广东移动通信有限公司承办的2024 6G无线通信AI大赛在北京顺利举行分享交流会暨颁奖典礼。主承办方专家、10支获奖团队代表及6G无线通信相关领域专业人才齐聚北京&#xff…

企业元宇宙3D云端数字化展厅扩大客户触及面

在浩瀚无垠的元宇宙中&#xff0c;一个立体、虚拟的数字空间正在等待您的探索与创造。如何在这片无边界的数字领域中快速搭建起属于您自己的虚拟展馆&#xff0c;已成为当今企业关注的焦点。 元宇宙数字展馆搭建&#xff0c;不仅是对新技术领域的探索&#xff0c;更是品牌创新与…

股指期货看盘技巧和方法分享!

股指期货看盘技巧&#xff0c;简单来说&#xff0c;就是要找到适合自己的方法&#xff0c;同时要考虑大的经济环境。做交易时&#xff0c;要勇敢&#xff0c;不要后悔。 1. 了解自己&#xff1a;首先&#xff0c;你得清楚自己是哪种类型的投资者。你是喜欢长期投资&#xff0c;…

迅睿CMS 后端配置项没有正常加载,上传插件不能正常使用

首先&#xff0c;尝试迅睿CMS官方提供的【百度编辑器问题汇总】解决方案来解决你的问题。你可以访问这个链接&#xff1a;官方解决方案。 如果按照【百度编辑器问题汇总】解决方案操作后&#xff0c;依然遇到“后端配置项没有正常加载&#xff0c;上传插件不能正常使用”的问题…

JL-33 手持式气象站/便携式气象站 小型气象站厂家 微型气象站

产品概述 手持式气象站是一款携带方便&#xff0c;操作简单&#xff0c;集多项气象要素于一体的可移动式气象观测仪器。产品采用传感器及芯片&#xff0c;能同时对空气温度、空气湿度、风速、风向、光照、大气压力、颗粒物、噪声等要素进行准确测量、记录并存储。仪器带有机械…

未对文件 xxx.ps1 进行数字签名,无法在当前系统上运行该脚本解决

无法执行PS1脚本&#xff1a; 解决方法: 启用远程签名策略 set-ExecutionPolicy RemoteSigned 启用签名策略后&#xff0c;成功执行ps1脚本 解决方法2: 使用当前用户签名策略&#xff1a; Set-ExecutionPolicy -Scope CurrentUser RemoteSigned 成功运行ps1脚本 PowerShell I…

【计算机网络】网络层(作业)

【一】 1、某主机的 IP 地址为 166.199.99.96/19。若该主机向其所在网络发送广播 IP 数据报&#xff0c; 则目的地址可以是&#xff08;D&#xff09;。 A. 166.199.99.255B. 166.199.96.255C. 166.199.96.0D. 166.199.127.255 解析&#xff1a; 166.199.99.96/19166.199.0…

【FPGA】STA静态时序分析

文章目录 一.定义二.分类1. 静态时序分析2. 静态时序分析 三. 概念四. 时间余量1.场景2.建立时间余量3.保持时间余量 一.定义 时序分析:检查电路是否满足时序要求&#xff1b; 二.分类 1. 静态时序分析 STA,遍历所有的时序路径&#xff0c;根据时序库&#xff08;.lib文件&…

【前端】HTML+CSS复习记录【5】

文章目录 前言一、padding、margin、border&#xff08;边框边距&#xff09;二、样式优先级三、var&#xff08;使用 CSS 变量更改多个元素样式&#xff09;四、media quary&#xff08;媒体查询&#xff09;系列文章目录 前言 长时间未使用HTML编程&#xff0c;前端知识感觉…

分享 6 款用于管理Docker容器的免费开源工具

Docker 是一个开源平台&#xff0c;可自动执行应用程序的部署、扩展和管理。它使用容器化技术将应用程序及其依赖项打包到软件开发的标准化单元中。 这使得使用容器创建、部署和运行应用程序变得更加容易&#xff0c;容器允许开发人员将应用程序及其所需的所有部分&#xff08…

使用Scrapy进行网络爬取时的缓存策略与User-Agent管理

缓存策略的重要性 缓存策略在网络爬虫中扮演着至关重要的角色。合理利用缓存可以显著减少对目标网站的请求次数&#xff0c;降低服务器负担&#xff0c;同时提高数据抓取的效率。Scrapy提供了多种缓存机制&#xff0c;包括HTTP缓存和Scrapy内置的缓存系统。 HTTP缓存 HTTP缓…

Conmi的正确答案——ESP32-C3开启安全下载模式

IDF版本&#xff1a;4.4.7 注意事项&#xff1a;一旦烧录“安全下载模式”&#xff0c;模组将无法被读取或清理&#xff0c;只能通过eclipse原项目烧录程序进行重新烧录&#xff0c;无法再烧录其他固件。 20240703110201——追加解法&#xff0c;暂时无法解安全下载模式 &…