基于RSA和AES加密算法实现的socket加密传输模型
本文中的模型思路来自于这篇博客:https://www.cnblogs.com/sheng1255blog/p/5121536.html
基本思路:采用安全的RSA加密算法来对生成AES密钥对的种子进行加密传输。通信双方得到种子Seed之后分别生成AES加密密钥和AES解密密钥,此后就可以用AES对socket传输的数据进行加密传输了。
这样做的理由:RSA加密算法安全性高,但是加密效率低,加密速度慢,一般只用来加密短小的数据,不会直接加密大量的数据。AES加密速度快,加密效率高,但是由于是对称密钥密码体制,安全性没有RSA好,详情可见我之前的博客。所以采用RSA+AES的方法,利用了两者的优点,https据说也是采用这种做法。此外AES加密数据一般是固定长度的,所以需要自己实现分块加密,我这里封装了一下,方便使用。
客户端代码:safe_client.cpp
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
#include <fcntl.h>
#include <openssl/rsa.h>
#include <openssl/aes.h>
#include "myaes.h"
#define SER_PORT 6000
#define SER_IP "127.0.0.1"
using namespace std;
int main()
{
//客户端创建流式套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1)
{
return -1;
}
//绑定服务端IP和端口
struct sockaddr_in saddr;
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(SER_PORT);
saddr.sin_addr.s_addr = inet_addr(SER_IP);
int res=connect(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
assert(res != -1);
//产生RSA密钥对
RSA* rsa = RSA_new();
BIGNUM* bne = BN_new(); //随机产生一个大数
int ret = 0;
ret = BN_set_word(bne, RSA_F4);
ret = RSA_generate_key_ex(rsa, 128, bne, NULL);
// 客户端从RSA结构中提取公钥到BUFF,以便将它传输给对方
// 128位的RSA其公钥提出出来长度是26字节,而私钥提取出来有16字节
// 保险起见,给它们预留一个128字节的空间
unsigned char PublicKey[128];
unsigned char *PKey = PublicKey; // 注意这个指针不是多余,是特意要这样做的,
int PublicKeyLen = i2d_RSAPublicKey(rsa, &PKey);
//把公钥发送给服务器
send(sockfd, PublicKey, 128, 0);
//接收服务器发来的Seed结构 这里的AES_BLOCK_SIZE宏是aes.h里定义的 大小为16
unsigned char Seed[AES_BLOCK_SIZE], EncryptedSeed[AES_BLOCK_SIZE];
memset(Seed, '\0', AES_BLOCK_SIZE);
memset(EncryptedSeed, '\0', AES_BLOCK_SIZE);
printf("recv Seed\n");
recv(sockfd, EncryptedSeed, sizeof(EncryptedSeed), 0);
//对EncryptedSeed用RSA私钥进行解密。这里得到了Seed
int rsa_len = RSA_size(rsa);
RSA_private_decrypt(AES_BLOCK_SIZE, (unsigned char*)EncryptedSeed, (unsigned char*)Seed, rsa, RSA_NO_PADDING); // 解密
// 逐个字节打印解密后的Seed信息 和服务器发送过来的Seed进行对比
/*printf("Seed after decrypt, Len=%ld\n", sizeof(Seed));
for (int i = 0; i < sizeof(Seed); i++)
{
printf("0x%d, ", *(Seed+i));
}
printf("\n");*/
//这里用封装过的AES加密算法进行加解密
BobAES a(Seed);
string str = "ssssssssssssssssssssssss";
string EncryptedData = a.aes_encrypt(str);
send(sockfd, EncryptedData.c_str(), strlen(EncryptedData.c_str()), 0);
string Data = a.aes_decrypt(EncryptedData);
cout << "date after decrypt:" << Data << endl;
//最后要释放RSA结构 AES是栈内存,系统自动管理
RSA_free(rsa);
//RSA_free(EncryptRsa); //这个是给安全通信对象释放的 从buff里面提取出来的公钥
return 0;
}服务端代码:safe_server.cpp
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/sendfile.h>
#include <openssl/rsa.h>
#include <openssl/aes.h>
#include "myaes.h"
#define SER_PORT 6000
#define SER_IP "127.0.0.1"
using namespace std;
int main()
{
int sockfd=socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1)
{
return -1;
}
struct sockaddr_in saddr;
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(SER_PORT);
saddr.sin_addr.s_addr=inet_addr(SER_IP);
int res = bind(sockfd, (struct sockaddr*)&saddr, sizeof(saddr));
if(res== -1)
{
return -1;
}
listen(sockfd, 5);
struct sockaddr_in caddr;
socklen_t len = sizeof(caddr);
int clientfd = accept(sockfd, (struct sockaddr*)&caddr, &len);
//首先接受客户端发送过来的RSA公钥信息
unsigned char buff_rsa[512];
memset(buff_rsa, '\0', sizeof(buff_rsa));
int n = recv(clientfd, buff_rsa, 512, 0);
// 逐个字节打印PublicKey信息 看看接收是否正确
printf("PublicKeyBuff, Len=%d\n", 74);
for (int i = 0; i < 74; i++)
{
printf("0x%02x, ", *(buff_rsa+i));
}
printf("\n");
//跟据上面提出的公钥信息PublicKey构造一个新RSA密钥(这个密钥结构只有公钥信息,用来对Seed进行加密)
unsigned char *PKey = buff_rsa;
//int PublicKeyLen = strlen((char*)buff_rsa);
RSA *EncryptRsa = d2i_RSAPublicKey(NULL, (const unsigned char**)&PKey, 26);
// 使用EncryptRsa加密数据,再使用ClientRsa解密数据
// 注意, RSA加密/解密的数据长度是有限制,例如128位的RSA就只能最多能加密解密16字节的数据 8:1的关系
// 如果采用RSA_NO_PADDING加密方式,128位的RSA就只能加密长度等于16的数据
// 这个长度可以使用RSA_size()来获得
unsigned char Seed[AES_BLOCK_SIZE], EncryptedSeed[AES_BLOCK_SIZE];
//这里给Seed做一下随机 取1-100之间的数
for(int i = 0; i < AES_BLOCK_SIZE; i++)
{
Seed[i] = rand() % 100 + 1;
}
memset(EncryptedSeed, '\0', AES_BLOCK_SIZE);
// 逐个字节打印Seed信息 对比发送和接收是否正确
printf("Seed, Len=%ld\n", sizeof(Seed));
for (int i = 0; i < AES_BLOCK_SIZE; i++)
{
printf("0x%d, ", *(Seed+i));
}
printf("\n");
RSA_public_encrypt(AES_BLOCK_SIZE, (unsigned char*)Seed, (unsigned char*)EncryptedSeed, EncryptRsa, RSA_NO_PADDING);
//这里再把加密后的Seed(EncryptedSeed)发送到客户端
send(clientfd, EncryptedSeed, AES_BLOCK_SIZE, 0);
//利用Seed结构生成一个AES解密密钥
//使用封装后的AES进行数据解密
BobAES a(Seed);
char EncryptedData[256];
recv(clientfd, EncryptedData, sizeof(EncryptedData), 0);
cout << "encryptedata:" << EncryptedData << endl;
std::string str(EncryptedData);
std::string Data = a.aes_decrypt(str);
//输出解密之后的数据
std::cout << "data after decrypt:" << Data << std::endl;
//最后释放RSA结构
RSA_free(EncryptRsa);
return 0;
}Makefile:
safe_client safe_server: safe_client.cpp safe_server.cpp myaes.cpp
g++ -o safe_client safe_client.cpp myaes.cpp -L/usr/local/lib -lssl -lcrypto -lpthread
g++ -o safe_server safe_server.cpp myaes.cpp -L/usr/local/lib -lssl -lcrypto -lpthread
clean:
-rm -f safe_client safe_server注意使用的openssl需要自行安装,将使用到的库 libcrypto, libssl放在对应的工作目录中即可。
对AES的分块加密封装:
myaes.h:
#ifndef bob_aes_h
#define bob_aes_h
#include <iostream>
#include <stdio.h>
#include <string>
#include <string.h>
#include <cstdlib>
#include <openssl/aes.h>
class BobAES
{
public:
BobAES(unsigned char* Seed);
~BobAES();
std::string aes_encrypt(std::string msg);
std::string aes_decrypt(std::string msg);
private:
int MSG_LEN;
unsigned char key[AES_BLOCK_SIZE];
};
#endifmyaes.cpp:
#include "myaes.h"
BobAES::BobAES(unsigned char* Seed)
: MSG_LEN(0)
{
for(int i = 0; i < AES_BLOCK_SIZE; i++)
{
key[i] = Seed[i];
}
}
BobAES::~BobAES()
{
}
std::string BobAES::aes_encrypt(std::string msg)
{
int i = msg.size() / 1024;
MSG_LEN = ( i + 1 ) * 1024;
//MSG_LEN = msg.size() + 16;
char in[MSG_LEN];
char out[MSG_LEN+16];
memset((char*)in,0,MSG_LEN);
memset((char*)out,0,MSG_LEN+16);
strncpy((char*)in,msg.c_str(),msg.size());
unsigned char iv[AES_BLOCK_SIZE]; //加密的初始化向量
for(int j = 0; j < AES_BLOCK_SIZE; ++j)
{
iv[j] = 0;
}
AES_KEY aes;
if(AES_set_encrypt_key(key, 128, &aes) < 0)
{
return NULL;
}
int len = msg.size();
AES_cbc_encrypt((unsigned char*)in,(unsigned char*)out,len,&aes,iv,AES_ENCRYPT);
std::string encrypt_msg(&out[0],&out[MSG_LEN+16]);
for(int i= 0;out[i];i++){
printf("%x",(unsigned char)out[i]);
//std::cout << dstStringTemp[i];
}
std::cout << std::endl;
return encrypt_msg;
}
std::string BobAES::aes_decrypt(std::string msg)
{
MSG_LEN = msg.size();
char in[MSG_LEN];
char out[MSG_LEN+16];
memset((char*)in,0,MSG_LEN);
memset((char*)out,0,MSG_LEN+16);
strncpy((char*)in,msg.c_str(),msg.size());
for(int i= 0;in[i];i++){
printf("%x",(unsigned char)in[i]);
//std::cout << dstStringTemp[i];
}
std::cout << std::endl;
unsigned char iv[AES_BLOCK_SIZE]; //加密的初始化向量
for(int j = 0; j < AES_BLOCK_SIZE; ++j)
{
iv[j] = 0;
}
AES_KEY aes;
if(AES_set_decrypt_key(key, 128, &aes) < 0)
{
return NULL;
}
int len = msg.size();
AES_cbc_encrypt((unsigned char*)in,(unsigned char*)out,len,&aes,iv,AES_DECRYPT);
std::string decrypt_msg = out;
return decrypt_msg;
}
查看17道真题和解析