基于RSA和AES加密算法實現(xiàn)的socket加密傳輸模型
本文中的模型思路來自于這篇博客:https://www.cnblogs.com/sheng1255blog/p/5121536.html
基本思路:采用安全的RSA加密算法來對生成AES密鑰對的種子進行加密傳輸。通信雙方得到種子Seed之后分別生成AES加密密鑰和AES解密密鑰,此后就可以用AES對socket傳輸?shù)臄?shù)據(jù)進行加密傳輸了。
這樣做的理由:RSA加密算法安全性高,但是加密效率低,加密速度慢,一般只用來加密短小的數(shù)據(jù),不會直接加密大量的數(shù)據(jù)。AES加密速度快,加密效率高,但是由于是對稱密鑰密碼體制,安全性沒有RSA好,詳情可見我之前的博客。所以采用RSA+AES的方法,利用了兩者的優(yōu)點,https據(jù)說也是采用這種做法。此外AES加密數(shù)據(jù)一般是固定長度的,所以需要自己實現(xiàn)分塊加密,我這里封裝了一下,方便使用。
客戶端代碼: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() { //客戶端創(chuàng)建流式套接字 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); //產(chǎn)生RSA密鑰對 RSA* rsa = RSA_new(); BIGNUM* bne = BN_new(); //隨機產(chǎn)生一個大數(shù) int ret = 0; ret = BN_set_word(bne, RSA_F4); ret = RSA_generate_key_ex(rsa, 128, bne, NULL); // 客戶端從RSA結(jié)構(gòu)中提取公鑰到BUFF,以便將它傳輸給對方 // 128位的RSA其公鑰提出出來長度是26字節(jié),而私鑰提取出來有16字節(jié) // 保險起見,給它們預留一個128字節(jié)的空間 unsigned char PublicKey[128]; unsigned char *PKey = PublicKey; // 注意這個指針不是多余,是特意要這樣做的, int PublicKeyLen = i2d_RSAPublicKey(rsa, &PKey); //把公鑰發(fā)送給服務器 send(sockfd, PublicKey, 128, 0); //接收服務器發(fā)來的Seed結(jié)構(gòu) 這里的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); // 解密 // 逐個字節(jié)打印解密后的Seed信息 和服務器發(fā)送過來的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結(jié)構(gòu) AES是棧內(nèi)存,系統(tǒng)自動管理 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); //首先接受客戶端發(fā)送過來的RSA公鑰信息 unsigned char buff_rsa[512]; memset(buff_rsa, '\0', sizeof(buff_rsa)); int n = recv(clientfd, buff_rsa, 512, 0); // 逐個字節(jié)打印PublicKey信息 看看接收是否正確 printf("PublicKeyBuff, Len=%d\n", 74); for (int i = 0; i < 74; i++) { printf("0x%02x, ", *(buff_rsa+i)); } printf("\n"); //跟據(jù)上面提出的公鑰信息PublicKey構(gòu)造一個新RSA密鑰(這個密鑰結(jié)構(gòu)只有公鑰信息,用來對Seed進行加密) unsigned char *PKey = buff_rsa; //int PublicKeyLen = strlen((char*)buff_rsa); RSA *EncryptRsa = d2i_RSAPublicKey(NULL, (const unsigned char**)&PKey, 26); // 使用EncryptRsa加密數(shù)據(jù),再使用ClientRsa解密數(shù)據(jù) // 注意, RSA加密/解密的數(shù)據(jù)長度是有限制,例如128位的RSA就只能最多能加密解密16字節(jié)的數(shù)據(jù) 8:1的關系 // 如果采用RSA_NO_PADDING加密方式,128位的RSA就只能加密長度等于16的數(shù)據(jù) // 這個長度可以使用RSA_size()來獲得 unsigned char Seed[AES_BLOCK_SIZE], EncryptedSeed[AES_BLOCK_SIZE]; //這里給Seed做一下隨機 取1-100之間的數(shù) for(int i = 0; i < AES_BLOCK_SIZE; i++) { Seed[i] = rand() % 100 + 1; } memset(EncryptedSeed, '\0', AES_BLOCK_SIZE); // 逐個字節(jié)打印Seed信息 對比發(fā)送和接收是否正確 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)發(fā)送到客戶端 send(clientfd, EncryptedSeed, AES_BLOCK_SIZE, 0); //利用Seed結(jié)構(gòu)生成一個AES解密密鑰 //使用封裝后的AES進行數(shù)據(jù)解密 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); //輸出解密之后的數(shù)據(jù) std::cout << "data after decrypt:" << Data << std::endl; //最后釋放RSA結(jié)構(gòu) 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]; }; #endif
myaes.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; }