evp_cipher.cpp
#include <iostream>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <fstream>
#include <ctime>
// _CRT_SECURE_NO_WARNINGS
#ifdef _WIN32
#include <openssl/applink.c>
#endif
#include "xsec.h"
using namespace std;
bool EncryptFile(string passwd, string in_filename, string out_filename,bool is_enc)
{
//选择加解密算法,后面可以替换
auto cipher = EVP_des_ede3_cbc();
//输入文件大小
int in_file_size = 0;
//输出文件大小
int out_file_size = 0;
ifstream ifs(in_filename, ios::binary); //二进制打开输入文件
if (!ifs)return false;
ofstream ofs(out_filename, ios::binary);//二进制大小输出文件
if (!ofs)
{
ifs.close();
return false;
}
auto ctx = EVP_CIPHER_CTX_new(); //加解密上下文
//密钥初始化 多出的丢弃
unsigned char key[128] = { 0 };
int key_size = EVP_CIPHER_key_length(cipher);// 获取密钥长度
if (key_size > passwd.size()) //密码少了
{
key_size = passwd.size();
}
memcpy(key, passwd.data(), key_size);
unsigned char iv[128] = { 0 }; //初始化向量
int re = EVP_CipherInit(ctx, cipher, key, iv, is_enc);
if (!re)
{
ERR_print_errors_fp(stderr);
ifs.close();
ofs.close();
EVP_CIPHER_CTX_free(ctx);
return false;
}
unsigned char buf[1024] = { 0 };
unsigned char out[1024] = { 0 };
int out_len = 0;
//1 读文件=》2 加解密文件=》3写入文件
while (!ifs.eof())
{
//1 读文件
ifs.read((char*)buf, sizeof(buf));
int count = ifs.gcount();
if (count <= 0)break;
in_file_size += count; //统计读取文件大小
//2 加解密文件 机密到out
EVP_CipherUpdate(ctx, out, &out_len, buf, count);
if (out_len <= 0)break;
//3 写入文件
ofs.write((char*)out, out_len);
out_file_size += out_len;
}
//取出最后一块数据
EVP_CipherFinal(ctx, out, &out_len);
if (out_len > 0)
{
ofs.write((char*)out, out_len);
out_file_size += out_len;
}
ifs.close();
ofs.close();
EVP_CIPHER_CTX_free(ctx);
cout << "in_file_size:" << in_file_size << endl;
cout << "out_file_size:" << out_file_size << endl;
return true;
}
bool XSecEncryptFile(string passwd, string in_filename, string out_filename, bool is_enc)
{
ifstream ifs(in_filename, ios::binary); //二进制打开输入文件
if (!ifs)return false;
ofstream ofs(out_filename, ios::binary);//二进制大小输出文件
if (!ofs)
{
ifs.close();
return false;
}
XSec sec;
sec.Init(XAES128_CBC, "1234567812345678", is_enc);
unsigned char buf[1024] = { 0 };
unsigned char out[1024] = { 0 };
int out_len = 0;
//1 读文件=》2 加解密文件=》3写入文件
while (!ifs.eof())
{
//1 读文件
ifs.read((char*)buf, sizeof(buf));
int count = ifs.gcount();
if (count <= 0)break;
bool is_end = false;
if (ifs.eof()) //文件结尾
is_end = true;
out_len = sec.Encrypt(buf, count, out, is_end);
if (out_len <= 0)
break;
ofs.write((char*)out, out_len);
}
sec.Close();
ifs.close();
ofs.close();
return true;
}
//测试算法性能
class TestCipher
{
public:
void Close()
{
delete in_;
in_ = nullptr;
delete de_;
de_ = nullptr;
delete en_;
en_ = nullptr;
}
void Init(int data_size)
{
Close();
data_size_ = data_size;
in_ = new unsigned char[data_size];
en_ = new unsigned char[data_size + 128];
de_ = new unsigned char[data_size + 128];
//测试数据赋初值
unsigned int data = 1;
for (int i = 0; i < data_size; i += sizeof(data))
{
memcpy(in_ + i, &data, sizeof(data));
data++;
}
memset(en_, 0, data_size + 128);
memset(de_, 0, data_size + 128);
}
void Test(XSecType type, string type_name)
{
memset(en_, 0, data_size_ + 128);
memset(de_, 0, data_size_ + 128);
cout << " ============ "<<type_name << " ============ " << endl;
XSec sec;
//加密
sec.Init(type, passwd,true);
auto start = clock();
int en_size = sec.Encrypt(in_, data_size_, en_);
auto end = clock();
cout << "加密花费时间:" << (double)((end - start) / (double)CLOCKS_PER_SEC) << "秒" << endl;
//解密
sec.Init(type, passwd, false);
start = clock();
int de_size = sec.Encrypt(en_, en_size, de_);
end = clock();
cout << "解密花费时间:" << (double)((end - start) / (double)CLOCKS_PER_SEC) << "秒" << endl;
}
~TestCipher()
{
Close();
}
private:
//测试数字节数
int data_size_ = 0;
//测试数据
unsigned char* in_ = nullptr;
//加密后数据
unsigned char* en_ = nullptr;
//解密后数据
unsigned char* de_ = nullptr;
//密码 适应各种强度
string passwd = "12345678ABCDEFGHabcdefgh!@#$%^&*";
};
//ci.Test(XDES_ECB, "XDES_ECB");
#define TEST_CIPHER(s) ci.Test(s, #s)
int main(int argc, char* argv[])
{
TestCipher ci;
ci.Init(1024 * 1024 * 100); // 100MB
TEST_CIPHER(XDES_ECB);
TEST_CIPHER(XDES_CBC);
TEST_CIPHER(X3DES_ECB);
TEST_CIPHER(X3DES_CBC);
TEST_CIPHER(XAES128_ECB);
TEST_CIPHER(XAES128_CBC);
TEST_CIPHER(XAES192_ECB);
TEST_CIPHER(XAES192_CBC);
TEST_CIPHER(XAES256_ECB);
TEST_CIPHER(XAES256_CBC);
TEST_CIPHER(XSM4_ECB);
TEST_CIPHER(XSM4_CBC);
getchar();
/*
XDES_ECB,
XDES_CBC,
X3DES_ECB,
X3DES_CBC,
XAES128_ECB,
XAES128_CBC,
XAES192_ECB,
XAES192_CBC,
XAES256_ECB,
XAES256_CBC,
XSM4_ECB,
XSM4_CBC*/
//ci.Test(XDES_ECB, "XDES_ECB");
//加密文件
EncryptFile("1234567812345678",
"test_evp_cipher.cpp",
"test_evp_cipher.encrypt2.txt",
true);
//解密文件
EncryptFile("1234567812345678",
"test_evp_cipher.encrypt2.txt",
"test_evp_cipher.decrypt2.txt",
false);
//加密文件
EncryptFile("12345678",
"test_evp_cipher.cpp",
"test_evp_cipher.encrypt.txt",
true);
//解密文件
EncryptFile("12345678",
"test_evp_cipher.encrypt.txt",
"test_evp_cipher.decrypt.txt",
false);
const unsigned char data[128] = "12345678123456781";//输入
int data_size = strlen((char*)data);
cout << "data_size = " << data_size << endl;
unsigned char out[1024] = { 0 }; //输出
unsigned char key[128] = "12345678901234567890";//秘钥
unsigned char iv[128] = { 0 }; //初始化向量
//三重DES 3DES 算法
auto cipher = EVP_des_ede3_cbc();
//error:digital envelope routines:EVP_CipherInit_ex:initialization error
//auto cipher = EVP_des_cbc();
//获取算法的分组大小()
int block_size = EVP_CIPHER_block_size(cipher);
int key_size = EVP_CIPHER_key_length(cipher);
int iv_size = EVP_CIPHER_iv_length(cipher);
cout << "block_size = " << block_size << endl;
cout << "key_size = " << key_size << endl;
cout << "iv_size = " << iv_size << endl;
//加解密上下文
auto ctx = EVP_CIPHER_CTX_new();
//加密算法初始化
int re = EVP_CipherInit(ctx, cipher, key, iv,
1 //1 表示加密
);
if (!re)
{
ERR_print_errors_fp(stderr);
getchar();
return -1;
}
cout << "EVP_CipherInit success!" << endl;
//默认 PKCS7 补充大小 EVP_PADDING_PKCS7
//关闭自动填充
//EVP_CIPHER_CTX_set_padding(ctx, 0);
EVP_CIPHER_CTX_set_padding(ctx, EVP_PADDING_PKCS7);
int out_size = 0;
//只处理分组大小得到数据,如果取消自动填充,多余数据丢弃
// 如果自动填充,则在EVP_CipherFinal 中获取数据
EVP_CipherUpdate(ctx,
out, //输出
&out_size, //输出数据大小
data, //输入数据
data_size
);
cout << "EVP_CipherUpdate size:" << out_size << endl;
//取出最后一块数据(需要填充的),或者是padding补充的数据
int padding_size = 0;
EVP_CipherFinal(ctx, out + out_size, &padding_size);
cout << "padding_size = " << padding_size << endl;
out_size += padding_size;
cout << out_size << ":" << out << endl;
//
/// 解密数据 使用原来的ctx
re = EVP_CipherInit(ctx, cipher, key, iv,
0 //0表示解密
);
if (!re)
{
ERR_print_errors_fp(stderr);
}
//解密密文后存放的明文
unsigned char out2[1024] = { 0 };
int out2_size = 0;
//解密数据 填充数据取不到
EVP_CipherUpdate(ctx,
out2, &out2_size, //输入密文数据
out, out_size); //输出解密后明文
cout << "EVP_CipherUpdate out2_size = " << out2_size << endl;
//取出填充数据
EVP_CipherFinal(ctx, out2 + out2_size, &padding_size);
cout << "EVP_CipherFinal padding_size=" << padding_size << endl;
out2_size += padding_size;
cout << out2_size << ":" << out2 << "|" << endl;
//释放上下文
EVP_CIPHER_CTX_free(ctx);
getchar();
return 0;
}
sec.cpp
#include "xsec.h"
#include <openssl/evp.h>
#include <openssl/err.h>
#include<iostream>
using namespace std;
void XSec::Close()
{
//初始化iv_
memset(iv_, 0, sizeof(iv_));
if (ctx_)
{
EVP_CIPHER_CTX_free((EVP_CIPHER_CTX*)ctx_);
ctx_ = nullptr;
}
}
/
///初始化加密对象,清理之前的数据
///@para type 加密类型
///@para pass 秘钥,可以是二进制
///@is_en true加密 false解密
///@return 是否成功
bool XSec::Init(XSecType type, const std::string& pass, bool is_en)
{
Close();
this->type_ = type;
this->is_en_ = is_en;
//密钥补全或丢弃
unsigned char key[32] = { 0 }; //少的补充0
int key_size = pass.size();
//加解密算法
const EVP_CIPHER* cipher = 0;
switch (type)
{
case XDES_ECB:
case XDES_CBC:
block_size_ = DES_KEY_SZ;
///密码策略,超出8字节的丢弃,少的补充0
//超出8字节的丢弃,
if (key_size > block_size_) key_size = block_size_;
//少的补充0
memcpy(key, pass.data(), key_size);
DES_set_key((const_DES_cblock*)key, &ks_);
return true;
case X3DES_ECB:
cipher = EVP_des_ede3_ecb();
break;
case X3DES_CBC:
cipher = EVP_des_ede3_cbc();
break;
case XAES128_ECB:
cipher = EVP_aes_128_ecb();
break;
case XAES128_CBC:
cipher = EVP_aes_128_cbc();
break;
case XAES192_ECB:
cipher = EVP_aes_192_ecb();
break;
case XAES192_CBC:
cipher = EVP_aes_192_cbc();
break;
case XAES256_ECB:
cipher = EVP_aes_256_ecb();
break;
case XAES256_CBC:
cipher = EVP_aes_192_cbc();
break;
case XSM4_ECB:
cipher = EVP_sm4_ecb();
break;
case XSM4_CBC:
cipher = EVP_sm4_cbc();
break;
default:
break;
}
if (!cipher)return false;
//分组大小
block_size_ = EVP_CIPHER_block_size(cipher);
//密钥补充或者丢弃
if (key_size > EVP_CIPHER_key_length(cipher))
key_size = EVP_CIPHER_key_length(cipher);
memcpy(key, pass.data(), key_size);
//加解密上下文
ctx_ = EVP_CIPHER_CTX_new();
//初始化上下文
int re = EVP_CipherInit((EVP_CIPHER_CTX*)ctx_,
cipher,key,iv_,is_en_
);
if (!re)
{
ERR_print_errors_fp(stderr);
return false;
}
//cout << "EVP_CipherInit success!" << endl;
return true;
}
/
/// 加解密数据
///@para in 输入数据
///@para in_size 数据大小
///@para 输出数据
///@para 成功返回加解密后数据字节大小,失败返回0
int XSec::Encrypt(const unsigned char* in, int in_size, unsigned char* out, bool is_end)
{
if (type_ == XDES_ECB)
{
if (is_en_)
{
return EnDesECB(in,in_size,out,is_end);
}
else
{
return DeDesECB(in, in_size, out,is_end);
}
}
else if (type_ == XDES_CBC)
{
if (is_en_)
{
return EnDesCBC(in, in_size, out, is_end);
}
else
{
return DeDesCBC(in, in_size, out, is_end);
}
}
//不是最后一块数据,不填充PKCS7
if (is_end)
EVP_CIPHER_CTX_set_padding((EVP_CIPHER_CTX*)ctx_, EVP_PADDING_PKCS7);
else
EVP_CIPHER_CTX_set_padding((EVP_CIPHER_CTX*)ctx_, 0); //关闭自动填充
int out_len = 0;
EVP_CipherUpdate((EVP_CIPHER_CTX*)ctx_, out, &out_len, in, in_size);
if (out_len <= 0)
return 0;
//出去填充得到数据
int out_padding_len = 0;
EVP_CipherFinal((EVP_CIPHER_CTX*)ctx_, out + out_len, &out_padding_len);
return out_len+ out_padding_len;
}
/// DES ECB模式解密
int XSec::DeDesECB(const unsigned char* in, int in_size, unsigned char* out, bool is_end)
{
for (int i = 0; i < in_size; i += block_size_)
{
DES_ecb_encrypt(
(const_DES_cblock*)(in + i),
(DES_cblock*)(out + i),
&ks_,
DES_DECRYPT
);
}
if (is_end)
//PKCS7 最后一个字节存储的补充字节数
return in_size - out[in_size - 1];
else
return in_size;
}
/// DES ECB模式加密
int XSec::EnDesECB(const unsigned char* in, int in_size, unsigned char* out, bool is_end)
{
///数据填充 PKCS7 Padding
/*
假设数据长度需要填充n(n>0)个字节才对齐,那么填充n个字节,每个字节都是n;
如果数据本身就已经对齐了,则填充一块长度为块大小的数据,每个字节都是块大小。
*/
unsigned char pad[8] = { 0 };
int padding_size = block_size_ - (in_size % block_size_);
//填入补充的字节大小
memset(pad, padding_size, sizeof(pad));
int i = 0;
for ( ;i < in_size; i += block_size_)
{
//最后一块数据,小于block_size_ 需要填充
if (in_size - i < block_size_)
{
//填入数据
memcpy(pad, in + i, in_size - i);
break;
}
DES_ecb_encrypt((const_DES_cblock*)(in + i),
(DES_cblock*)(out + i),
&ks_,
DES_ENCRYPT
);
}
if (!is_end)return in_size;
//补充 PKCS7结尾
DES_ecb_encrypt((const_DES_cblock*)pad,
(DES_cblock*)(out + i),
&ks_,
DES_ENCRYPT
);
return in_size + padding_size;
}
/// DES CBC模式加密
int XSec::EnDesCBC(const unsigned char* in, int in_size, unsigned char* out,bool is_end)
{
//填充的数据 PKCS7 Padding
unsigned char pad[8] = { 0 };
int padding_size = block_size_ - (in_size % block_size_);
//填入补充的字节大小
memset(pad, padding_size, sizeof(pad));
//block 整数倍大小
int size1 = in_size - (in_size % block_size_);
//ncbc保留iv修改 减去需要补充的数据
DES_ncbc_encrypt(in, out,
size1,
&ks_,
(DES_cblock*)iv_,
DES_ENCRYPT
);
if (!is_end)return in_size;
//PKCS7 Padding
if (in_size % block_size_ != 0)
{
//复制剩余的数据
memcpy(pad, in + size1, (in_size % block_size_));
}
DES_ncbc_encrypt(pad, out+size1,
sizeof(pad),
&ks_,
(DES_cblock*)iv_,
DES_ENCRYPT
);
return in_size + padding_size;
}
/// DES CBC模式解密
int XSec::DeDesCBC(const unsigned char* in, int in_size, unsigned char* out,bool is_end)
{
DES_ncbc_encrypt(in, out, in_size, &ks_, (DES_cblock*)iv_, DES_DECRYPT);
if (!is_end)return in_size;
return in_size - out[in_size - 1];
}