本文主要是介绍代码示例:OpenSSL AES CBC 加密,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
初级代码游戏的专栏介绍与文章目录-CSDN博客
我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。
这些代码大部分以Linux为目标但部分代码是纯C++的,可以在任何平台上使用。
这是一个基于OpenSSL的AES-CBC加密的例子,其中CBC是自行实现的,原因是OpenSSL的CBC始终有问题,可能是我还没做对吧。这个例程的目的是对配置数据加密,不用于对外交换,所以CBC有没写对我也不能担保——反正这个程序自己加密的自己解密是没问题的。
目录
1 介绍
2 代码概要
3 主要代码
4 辅助代码
5 openSSL的AES用法
5.1 设置KEY
5.2 加密解密AES_encrypt/AES_decrypt
1 介绍
这个例程是基于CentOS7/8的,64位,OpenSSL大概是1.1,我也在windows上做了测试,代码可能需要修改,CBC的问题同样存在(所以我就放弃解决了,自己写了一个)。
使用OpenSSL需要头文件位置和库,如果头文件安装到标准位置则不需要-I参数,库是 -lssl -lcrypt
2 代码概要
//需要的OpenSSL的两个头文件
#include <openssl/aes.h>
#include <openssl/rand.h>class CMyOpenSSL
{
private://用来输出十六进制数据static void show_buf(char const* title, unsigned char const* buf, int len){。。。。。。}//需要一个从用户密码生成密钥的函数//这个函数我没写,有标准算法从密码生成密钥,类似于对密码进行哈希
public://这就是一般所说的“盐”class IV{private:unsigned char iv[AES_BLOCK_SIZE * 5];//其实不需要*5,但我原来就是这么写的,为了调试吧public://执行异或static void XOR(unsigned char const * iv,unsigned char* data){。。。。。。}//用随机数设置void Create(){。。。。。。}。。。。。。};//由于网上的例子也一样无法解密,所以自行实现CBCstatic void my_AES_cbc_encrypt(const unsigned char* in, unsigned char* out, size_t length, const AES_KEY* key, unsigned char* ivec, const int enc){。。。。。。}//AES加密static int aes_encrypt(unsigned char const* userpasswd, int userpasswd_len, vector<unsigned char> const& in_plain, vector<unsigned char>& out_ciphertext, IV& iv){。。。。。。}//AES解密static int aes_decrypt(unsigned char const* userpasswd, int userpasswd_len, vector<unsigned char> const& in_ciphertext, vector<unsigned char>& out_plain, IV& iv){。。。。。。}//保护数据,用密码加密并做格式转换static bool protect_encode(CBuffer const& passwd, CBuffer const& input, CBuffer& output){。。。。。。}//保护数据,用密码加密并做格式转换static bool protect_decode(CBuffer const& passwd, CBuffer const& input, CBuffer& output){。。。。。。}//下面两个就是网上别人写的程序,一样得不到正确结果// a simple hex-print routine. could be modified to print 16 bytes-per-linestatic void hex_print(const void* pv, size_t len){。。。。。。}// main entrypointstatic int a(){。。。。。。}//测试代码static bool aes_test(){。。。。。。}
};
主要函数如下:
my_AES_cbc_encrypt 我自己写的AES-CBC加密和解密,调用了openSSL的AES_encrypt和AES_decrypt,想学习AES怎么用看看这个函数就可以了
aes_encrypt AES加密,调用了openSSL的AES_set_encrypt_key和我自己写的my_AES_cbc_encrypt,为了方便把明文长度作为第一个数据块
aes_decrypt AES解密,调用了openSSL的AES_set_decrypt_key和我自己写的my_AES_cbc_encrypt
protect_encode和protect_decode是我自己写的加密配置数据的方法,完全是自定义的
a 是网上找来的测试代码,证明AES-CBC确实有问题,不是我一个人的问题
3 主要代码
下面是主要代码,上面有一些注释是我临时加的,不在完整代码里面,还有一些辅助类后面列出。
#include <openssl/aes.h>
#include <openssl/rand.h>class CMyOpenSSL
{
private:static unsigned char const ver;static void show_buf(char const* title, unsigned char const* buf, int len){cout << title << " ";for (int i = 0; i < len; ++i){cout << hex << setw(2) << setfill('0') << (unsigned int)buf[i] << " ";}cout << endl;}//需要一个从用户密码生成密钥的函数
public:class IV{private:unsigned char iv[AES_BLOCK_SIZE * 5];public:IV(){memset(iv, 0, AES_BLOCK_SIZE * 5);}//执行异或static void XOR(unsigned char const * iv,unsigned char* data){//show_buf("IV ", iv, AES_BLOCK_SIZE);//show_buf("DATA", data, AES_BLOCK_SIZE);for (int i = 0; i < AES_BLOCK_SIZE; ++i){data[i] ^= iv[i];}//show_buf("DATA", data, AES_BLOCK_SIZE);}int size()const{return AES_BLOCK_SIZE;}//用随机数设置void Create(){time_t t = time(NULL);srand(t);for (int i = 0; i < AES_BLOCK_SIZE; i += sizeof(int)){int a = rand();memcpy(iv + i, &a, sizeof(int));}}void Set(unsigned char const* a){memcpy(iv, a, AES_BLOCK_SIZE);}//注意,会修改内容unsigned char* Get(){//show_buf("", iv, AES_BLOCK_SIZE * 2);return iv;}};//由于网上的例子也一样无法解密,所以自行实现CBCstatic void my_AES_cbc_encrypt(const unsigned char* in, unsigned char* out, size_t length, const AES_KEY* key, unsigned char* ivec, const int enc){for (int i = 0; i < length; i += AES_BLOCK_SIZE){if (AES_ENCRYPT == enc){unsigned char tmpin[AES_BLOCK_SIZE];memcpy(tmpin, in + i, AES_BLOCK_SIZE);IV::XOR(ivec, tmpin);AES_encrypt(tmpin, out + i, key);memcpy(ivec, out + i, AES_BLOCK_SIZE);}else{unsigned char tmpiv[AES_BLOCK_SIZE];memcpy(tmpiv, in + i, AES_BLOCK_SIZE);AES_decrypt(in + i, out + i, key);IV::XOR(ivec, out + i);memcpy(ivec, tmpiv, AES_BLOCK_SIZE);}}}static int aes_encrypt(unsigned char const* userpasswd, int userpasswd_len, vector<unsigned char> const& in_plain, vector<unsigned char>& out_ciphertext, IV& iv){out_ciphertext.clear();unsigned char userkey[32];//必须是16/24/32memset((void*)userkey, '\0', 32);memcpy(userkey, userpasswd, (userpasswd_len > 32 ? 32 : userpasswd_len));/*设置加密key及密钥长度*/AES_KEY key;if (AES_set_encrypt_key(userkey, 32 * 8, &key) < 0){return __LINE__;}int len = 0;/*循环加密,每次只能加密AES_BLOCK_SIZE长度的数据*/out_ciphertext.reserve(in_plain.size() + AES_BLOCK_SIZE);while (len < in_plain.size()){if (0 == len){//第一个块是明文长度out_ciphertext.resize(out_ciphertext.size() + AES_BLOCK_SIZE);unsigned char tmp[AES_BLOCK_SIZE];memset((void*)tmp, '\0', AES_BLOCK_SIZE);long tmp_len = in_plain.size();memcpy(tmp, &tmp_len, sizeof(long));//show_buf("明文长度加密前 ", tmp, AES_BLOCK_SIZE);my_AES_cbc_encrypt(tmp, &out_ciphertext[out_ciphertext.size() - AES_BLOCK_SIZE], AES_BLOCK_SIZE, &key, iv.Get(), AES_ENCRYPT);//show_buf("明文长度加密后", &out_ciphertext[out_ciphertext.size() - AES_BLOCK_SIZE], AES_BLOCK_SIZE);}out_ciphertext.resize(out_ciphertext.size() + AES_BLOCK_SIZE);if (in_plain.size() - len < AES_BLOCK_SIZE){unsigned char tmp[AES_BLOCK_SIZE];memset((void*)tmp, '\0', AES_BLOCK_SIZE);memcpy(tmp, &in_plain[len], in_plain.size() - len);my_AES_cbc_encrypt(tmp, &out_ciphertext[out_ciphertext.size() - AES_BLOCK_SIZE], AES_BLOCK_SIZE, &key, iv.Get(), AES_ENCRYPT);}else{my_AES_cbc_encrypt(&in_plain[len], &out_ciphertext[out_ciphertext.size() - AES_BLOCK_SIZE], AES_BLOCK_SIZE, &key, iv.Get(), AES_ENCRYPT);}len += AES_BLOCK_SIZE;}return 0;}static int aes_decrypt(unsigned char const* userpasswd, int userpasswd_len, vector<unsigned char> const& in_ciphertext, vector<unsigned char>& out_plain, IV& iv){out_plain.clear();unsigned char userkey[32];//必须是16/24/32memset((void*)userkey, '\0', 32);memcpy(userkey, userpasswd, (userpasswd_len > 32 ? 32 : userpasswd_len));/*设置解密key及密钥长度*/AES_KEY key;if (AES_set_decrypt_key(userkey, 32 * 8, &key) < 0){return __LINE__;}int len = 0;/*循环解密*/out_plain.reserve(in_ciphertext.size());long out_len = 0;//原始长度,放在第一个加密块while (len < in_ciphertext.size()){if (0 == len){//第一个块是明文长度unsigned char tmp[AES_BLOCK_SIZE];//show_buf("明文长度解密前", &in_ciphertext[len], AES_BLOCK_SIZE);my_AES_cbc_encrypt(&in_ciphertext[len], tmp, AES_BLOCK_SIZE, &key, iv.Get(), AES_DECRYPT);//show_buf("明文长度解密后", tmp, AES_BLOCK_SIZE);memcpy(&out_len, tmp, sizeof(long));//thelog << "明文长度应该是 " << out_len << endi;len += AES_BLOCK_SIZE;}out_plain.resize(out_plain.size() + AES_BLOCK_SIZE);my_AES_cbc_encrypt(&in_ciphertext[len], &out_plain[out_plain.size() - AES_BLOCK_SIZE], AES_BLOCK_SIZE, &key, iv.Get(), AES_DECRYPT);len += AES_BLOCK_SIZE;}//恢复原始长度if (out_plain.size() > out_len)out_plain.resize(out_len);return 0;}//保护数据,用密码加密并做格式转换static bool protect_encode(CBuffer const& passwd, CBuffer const& input, CBuffer& output){output.setSize(0);IV iv;iv.Create();CBuffer tmp;tmp.AddData(&ver, 1);//第一个字节是版本tmp.AddData(iv.Get(), iv.size());//然后是IV,必须在加密之前保存,加密之后会改变//加密vector<unsigned char> in_plain;in_plain.resize(input.size());memcpy(&in_plain[0], input.data(), input.size());vector<unsigned char> out_ciphertext;aes_encrypt(passwd.data(), passwd.size(), in_plain, out_ciphertext, iv);thelog << out_ciphertext.size() << endi;//添加加密后数据tmp.AddData(&out_ciphertext[0], out_ciphertext.size());thelog << tmp.size() << endi;output.reserve(tmp.size() * 4 / 3 + 4 + 1);//三字节转为4字节,编码函数在最后还会加上一个字符串结束符thelog << output.capacity() << " " << output.size() << endi;int n = CBase64::Base64Enc(output.lockBuffer(), tmp.data(), tmp.size());output.releaseBuffer();if (n > output.capacity())thelog << "长度不足" << ende;output.setSize(n);thelog << output.size() << " [" << output.data() << "]" << endi;return true;}//保护数据,用密码加密并做格式转换static bool protect_decode(CBuffer const& passwd, CBuffer const& input, CBuffer& output){output.setSize(0);CBuffer tmp;//这里导致了奇怪的内存错误,实际并不需要这么长tmp.reserve(input.size() + 100);//实际需要的是4转3,解码函数最后会加上一个字符串结束符//thelog << input.size() << " " << tmp.capacity() << " " << tmp.size() << endi;int n = CBase64::Base64Dec(tmp.lockBuffer(), input.data(), input.size());tmp.releaseBuffer();if (n<0 || n > tmp.capacity())thelog << "长度不足" << ende;tmp.setSize(n);if (tmp.data()[0] != ver){thelog << "加密版本错误" << ende;return false;}else{//thelog << "加密版本 " << (int)tmp.data()[0]<<" " << (int)ver << ende;}IV iv;iv.Set(tmp.data() + 1);vector<unsigned char> in_plain;in_plain.resize(tmp.size() - 1 - iv.size());memcpy(&in_plain[0], tmp.data() + 1 + iv.size(), tmp.size() - 1 - iv.size());//thelog << tmp.size() << " " << in_plain.size() << endi;vector<unsigned char> out_ciphertext;aes_decrypt(passwd.data(), passwd.size(), in_plain, out_ciphertext, iv);output.AddData(&out_ciphertext[0], out_ciphertext.size());return true;}// a simple hex-print routine. could be modified to print 16 bytes-per-linestatic void hex_print(const void* pv, size_t len){const unsigned char* p = (const unsigned char*)pv;if (NULL == pv) {printf("NULL");}else {size_t i = 0;for (; i < len; ++i) {printf("%02X ", *p++);}}printf("\n");}// main entrypointstatic int a(){int const keylength = 128;/* generate a key with a given length */unsigned char aes_key[keylength / 8];memset(aes_key, 0, keylength / 8);if (!RAND_bytes(aes_key, keylength / 8)) {exit(-1);}size_t const inputslength = 16;/* generate input with a given length */unsigned char aes_input[inputslength];memset(aes_input, 1, inputslength);/* init vector */unsigned char iv_enc[AES_BLOCK_SIZE], iv_dec[AES_BLOCK_SIZE];RAND_bytes(iv_enc, AES_BLOCK_SIZE);memcpy(iv_dec, iv_enc, AES_BLOCK_SIZE);// buffers for encryption and decryptionconst size_t encslength = ((inputslength + AES_BLOCK_SIZE) / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;unsigned char enc_out[encslength];unsigned char dec_out[inputslength];memset(enc_out, 0, sizeof(enc_out));memset(dec_out, 0, sizeof(dec_out));// so i can do with this aes-cbc-128 aes-cbc-192 aes-cbc-256AES_KEY enc_key, dec_key;AES_set_encrypt_key(aes_key, keylength, &enc_key);AES_cbc_encrypt(aes_input, enc_out, inputslength, &enc_key, iv_enc, AES_ENCRYPT);AES_set_decrypt_key(aes_key, keylength, &dec_key);AES_cbc_encrypt(enc_out, dec_out, encslength, &dec_key, iv_dec, AES_DECRYPT);printf("original:\t");hex_print(aes_input, sizeof(aes_input));printf("encrypt:\t");hex_print(enc_out, sizeof(enc_out));printf("decrypt:\t");hex_print(dec_out, sizeof(dec_out));return 0;}static bool aes_test(){if (true){unsigned char userkey[32];//必须是16/24/32memset((void*)userkey, '\0', 32);memcpy(userkey, "12345", 5);AES_KEY key;if (AES_set_encrypt_key(userkey, 16 * 8, &key) < 0){return __LINE__;}unsigned char iv[AES_BLOCK_SIZE];unsigned char data[AES_BLOCK_SIZE];memset(data, 1, AES_BLOCK_SIZE);unsigned char data2[AES_BLOCK_SIZE];memset(data2, 0, AES_BLOCK_SIZE);unsigned char data3[AES_BLOCK_SIZE * 3];memset(data3, 0, AES_BLOCK_SIZE * 3);memset(iv, 0, AES_BLOCK_SIZE);show_buf("简单输入 ", data, AES_BLOCK_SIZE);show_buf("iv ", iv, AES_BLOCK_SIZE);AES_cbc_encrypt(data, data2, AES_BLOCK_SIZE, &key, iv, AES_ENCRYPT);show_buf("加密后 ", data2, AES_BLOCK_SIZE);show_buf("iv ", iv, AES_BLOCK_SIZE);if (AES_set_decrypt_key(userkey, 16 * 8, &key) < 0){return __LINE__;}memset(iv, 0, AES_BLOCK_SIZE);show_buf("解密前 ", data2, AES_BLOCK_SIZE);show_buf("iv ", iv, AES_BLOCK_SIZE);AES_cbc_encrypt(data2, data3, AES_BLOCK_SIZE, &key, iv, AES_DECRYPT);show_buf("简单输出 ", data3, AES_BLOCK_SIZE * 3);show_buf("iv ", iv, AES_BLOCK_SIZE);a();}if (true){string passwd = "13579";vector<unsigned char> plaintext;for (int i = 0; i < 65; ++i){plaintext.push_back(i);}vector<unsigned char> out;IV iv;iv.Create();IV iv2;iv2.Set(iv.Get());//show_buf("初始IV", iv.Get(), iv.size());aes_encrypt((unsigned char*)passwd.c_str(), passwd.size(), plaintext, out, iv);vector<unsigned char> plaintext2;//show_buf("初始IV", iv2.Get(), iv2.size());aes_decrypt((unsigned char*)passwd.c_str(), passwd.size(), out, plaintext2, iv2);thelog << plaintext.size() << " " << out.size() << " " << plaintext2.size() << ENDI;printf("plantext2: \n");for (int i = 0; i < plaintext2.size(); i++){printf("%.2x ", plaintext2[i]);if ((i + 1) % 32 == 0){printf("\n");}}printf("\n");}if (true){try{char const* plaintext = "1234567890";CBuffer pass;CBuffer in;CBuffer out;pass.SetData("123");in.SetData(plaintext);thelog << in.data() << endi;protect_encode(pass, in, out);thelog << out.size() << " [" << out.data() << "]" << endi;CBuffer out2;if (!protect_decode(pass, out, out2))thelog << "解码失败" << ende;thelog << out2.data() << endi;if (0 == strcmp(plaintext, (char*)out2.data())){thelog << "匹配成功" << endi;}else{thelog << "匹配失败" << ende;}//out2.lockBuffer();//out2.setSize(10240);}catch (...){thelog << "异常发生" << ende;}}return true;}
};
4 辅助代码
thelog可以替换为cout,endi、ende可以替换为endl。
CBuffer就是个包装的缓冲区,代码如下:
//内部隐含保持一个结束符,不计算在容量和大小之内,确保数据可以被当作字符串使用class CBuffer{private:string name;unsigned char* p = NULL;size_t buffer_size = 0;size_t data_size = 0;bool bLockBuffer = false;void _init(){static int i = 0;char buf[64];sprintf(buf, "CBuffer%d", i++);name = buf;if (p)throw "p不为空指针";p = new unsigned char[1];if (!p)throw "内存不足";p[0] = '\0';buffer_size = 0;}public:CBuffer(){_init();}CBuffer(CBuffer const & tmp){_init();SetData(tmp.data(), tmp.data_size);}CBuffer & operator=(CBuffer const & tmp){_init();SetData(tmp.data(), tmp.data_size);return *this;}~CBuffer(){//cout << "~CBuffer " << name <<" "<<(long)p<<" "<< buffer_size<<" "<<data_size << endl;if (p)delete[]p;data_size = 0;buffer_size = 0;p = NULL;}size_t capacity()const { return buffer_size; }size_t size()const { return data_size; }unsigned char const * data()const { return p; }unsigned char* lockBuffer(){bLockBuffer = true;return p;}void releaseBuffer() { bLockBuffer = false; }void setSize(long s){reserve(s);p[s] = '\0';data_size = s;}bool reserve(size_t n){if (n > buffer_size){if (bLockBuffer){cout << "缓冲区已锁定" << endl;throw "缓冲区已锁定";}//cout << name << " "<< "reserve " << (long)p << " " << buffer_size << " " << data_size << endl;//cout << name<<"需要扩展 " << buffer_size << " -> " << n << endl;unsigned char * p2 = new unsigned char[n + 1];if (p2){if (p){memmove(p2, p, data_size);delete[]p;}buffer_size = n;p2[data_size] = '\0';p = p2;//cout << name<< "扩展成功 " << buffer_size << " -> " << n << endl;return true;}else{//cout << name<<"扩展失败 " << buffer_size << " -> " << n << endl;return false;}}return true;}bool AddData(void const * data, long len){if (!reserve(data_size + len))return false;memmove(p + data_size, data, len);setSize(data_size + len);return true;}bool SetData(char const * sz){setSize(0);return AddData((void *)sz, strlen(sz));}bool SetData(unsigned char const* sz, long len){return SetData((char const*)sz,len);}bool SetData(char const* sz, long len){setSize(0);return AddData((void *)sz, len);}bool Compare(CBuffer const & tmp)const{if (data_size != tmp.data_size)return false;if (data_size > 0)return 0 == memcmp(tmp.data(), data(), data_size);else return true;}};
CBase64是Base64编码,代码如下:
class CBase64{public:static int Base64Enc(unsigned char *buf, unsigned char const *text,int size){ static unsigned char const * base64_encoding = (unsigned char const*)"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";int buflen = 0; while(size>0){*buf++ = base64_encoding[ (text[0] >> 2 ) & 0x3f];if(size>2){*buf++ = base64_encoding[((text[0] & 3) << 4) | (text[1] >> 4)];*buf++ = base64_encoding[((text[1] & 0xF) << 2) | (text[2] >> 6)];*buf++ = base64_encoding[text[2] & 0x3F];}else{switch(size){case 1:*buf++ = base64_encoding[(text[0] & 3L) << 4L ];*buf++ = '=';*buf++ = '=';break;case 2: *buf++ = base64_encoding[((text[0] & 3) << 4) | (text[1] >> 4)]; *buf++ = base64_encoding[((text[1] & 0x0F) << 2) | (text[2] >> 6)]; *buf++ = '='; break; } } text +=3; size -=3; buflen +=4;} *buf = 0;return buflen; } //base64解码的实现static unsigned char GetBase64Value(unsigned char ch){if ((ch >= 'A') && (ch <= 'Z')) return ch - 'A'; if ((ch >= 'a') && (ch <= 'z')) return ch - 'a' + 26; if ((ch >= '0') && (ch <= '9')) return ch - '0' + 52; switch (ch) { case '+': return 62; case '/': return 63; case '=': /* base64 padding */ return 0; default: return 0; } }//进行base64解码输入应该是4的倍数(根据mime标准)//如果不是4倍数返回错误//注意 如果是最后一个字符 那么长度不准备 可能会多1 //返回buf长度static int Base64Dec(unsigned char *buf, unsigned char const *text,int size){if(size%4)return -1;unsigned char chunk[4];int parsenum=0;while(size>0){chunk[0] = GetBase64Value(text[0]); chunk[1] = GetBase64Value(text[1]); chunk[2] = GetBase64Value(text[2]); chunk[3] = GetBase64Value(text[3]); *buf++ = (chunk[0] << 2) | (chunk[1] >> 4); *buf++ = (chunk[1] << 4) | (chunk[2] >> 2); *buf++ = (chunk[2] << 6) | (chunk[3]);text+=4;size-=4;parsenum+=3;}buf[parsenum]='\0';return parsenum;} };
5 openSSL的AES用法
5.1 设置KEY
加密解密的密钥存储为AES_KEY,需要通过AES_set_encrypt_key或AES_set_decrypt_key来设置:
static int aes_encrypt(unsigned char const* userpasswd, int userpasswd_len, vector<unsigned char> const& in_plain, vector<unsigned char>& out_ciphertext, IV& iv){out_ciphertext.clear();unsigned char userkey[32];//必须是16/24/32memset((void*)userkey, '\0', 32);memcpy(userkey, userpasswd, (userpasswd_len > 32 ? 32 : userpasswd_len));/*设置加密key及密钥长度*/AES_KEY key;if (AES_set_encrypt_key(userkey, 32 * 8, &key) < 0){return __LINE__;}
。。。。。。
注意加密和解密的函数是不一样的。
5.2 加密解密AES_encrypt/AES_decrypt
这两个函数非常简单,参数只有输入、输出、KEY,简单明了。
(这里是文档结束)
这篇关于代码示例:OpenSSL AES CBC 加密的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!