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();elsecipher=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_ofb2: EVP_des_ede3_cbc1加密結果長度:100test ok!please select :1: EVP_des_ede3_ofb2: EVP_des_ede3_cbc2加密結果長度:104test 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 : 29encrypto name: DES-ECBencrypto bock size : 8md info :md type : 4md nid : 4md name : MD5md pkey type : 8md size : 16md block size : 64generate key value:54 0 b1 24 18 42 8d ddgenerate 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");elseprintf("驗證錯誤\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 編解碼函數
- EVP_EncodeInit
BASE64 編碼初始化。 - EVP_EncodeUpdate
BASE64 編碼,可多次調用。 - EVP_EncodeFinal
BASE64 編碼,并獲取最終結果。 - EVP_DecodeInit
BASE64 解碼初始化。 - EVP_DecodeUpdate
輸入數據長度不能大于 80 字節。 BASE64 解碼可多次調用,注意,本函數的輸入數據不能太長。 - 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
設置內置的提示信息,用于需要用戶輸入口令的場合。