Client-ServerRSA加解密通信方案-Server端(C++)(一)
来源:互联网 发布:广州cnc编程招聘信息 编辑:程序博客网 时间:2024/05/23 17:43
0. 背景
最近,需要新做一个游戏demo,类似《部落冲突·皇室战争》的推塔玩法。客户端使用Unity,编程语言为C#,服务端使用C++。由于从零开始,需要建立基础部件,其中网络模块是最重要的模块之一。网络模块协议遵从如下图步骤时序图进行通信。
图1.通信步骤时序图
由上图可以看出网络通信主要分为两个步骤,第一步:客户端使用RSA加解密请求通信服务端,获取RC4秘钥;第二步:客户端与服务端使用RC4加解密通信。本文主要介绍第一步中在Server端(C++)的RSA加解密模块。
注:本文不叙述网络模块中的框架设计,只关注在客户端/服务端获得加密消息后,进行解密的技术环节。
1. RSA加密与解密
RSA为非对称加密方式,在C/C++中是基于使用OpenSSL库来实现。通过以下4个步骤来简单讲述。
a). 使用OpenSSL产生RSA秘钥
RSA的私钥包含了公钥的信息,所以首先通过以下命令产生长度为2048的RSA私钥。
openssl genrsa -out private.pem 2048
这样private.pem就会生成在当前目录下,使用以下命令就可以从private.pem提取出public.pem。
openssl rsa -in private.pem -outform PEM -pubout -out public.pem
如此,就产生两个秘钥,不难猜出:
public.pem是使用PEM格式的公钥,而private.pem是对应的私钥。
b). 公钥加密与私钥解密
RSA是一种非对称的加解密方式。基于它的特性(wiki),它可以以公钥加密,然后私钥解密;也可以以私钥加密,公钥解密。本小节罗列下OpenSSL对于RSA提供的接口:
int RSA_public_encrypt(int flen, unsigned char *from, unsigned char *to, RSA *rsa, int padding);int RSA_private_decrypt(int flen, unsigned char *from, unsigned char *to, RSA *rsa, int padding);
从函数名可以清晰地看出接口的功能。以RSA_public_encrypt
为例叙述参数,其他的接口可参考linux Doc:
- from: 待加密的字符串;
- flen: 待加密字符串长度;
- to: 加密完成后的字符串,其长度必须是RSA_size(rsa);
- rsa: 公钥;
- padding: 填充模式,有一下模式可供选择:
1)RSA_PKCS1_PADDING
:最常用的模式,待加密字符串长度flen < RSA_size(rsa)-11
;
2)RSA_PKCS1_OAEP_PADDING
:这种模式对于新应用也是推荐的,待加密字符串长度flen < RSA_size(rsa)-41
;
3)RSA_SSLV23_PADDING
:不常用模式;
4)RSA_NO_PADDING
:原RSA加密。这种模式只用于实现应用程序中的加密声音的填充模式。直接对用户数据RSA加密是不安全的,待加密字符串长度flen < RSA_size(rsa)
。
RSA_private_decrypt
接口真是从长度为flen的from字符串中使用私钥解密,并将解密后的字符存储在to中,而to字符串是指向一个足够存储解密字符串的buffer中,使用的填充模式padding是采取加密时的填充模式。
RSA_public_encrypt
接口返回的是加密后字符串的长度,而RSA_private_decrypt
接口返回的是解密后字符串的长度。如果加密/解密出错,返回-1。
我们可以通过以下函数封装,来使用公钥加密数据和私钥解密:
int padding = RSA_PKCS1_PADDING; //最常用的填充模式char* pub_fp = $path_to_public_pem$;char* pri_fp = $path_to_private_pem$;// 公钥加密封装int public_encrypt(unsigned char * data,int data_len,unsigned char * key, unsigned char *encrypted){ RSA * rsa = createRSAWithFilename(pub_fp,1); int result = RSA_public_encrypt(data_len,data,encrypted,rsa,padding); return result;}// 私钥解密封装int private_decrypt(unsigned char * enc_data,int data_len,unsigned char * key, unsigned char *decrypted){ RSA * rsa = createRSAWithFilename(pri_fp,0); int result = RSA_private_decrypt(data_len,enc_data,decrypted,rsa,padding); return result;}
注:公钥加密是可以使用所以的填充模式(padding mode).
其中createRSAWithFilename(char* filename, int nPublic)
接口是加载 a). 使用OpenSSL产生RSA秘钥的,filename
为公私钥的文件路径,nPublic
表示是否是公钥,具体实现如下:
RSA * createRSAWithFilename(char * filename,int nPublic){ FILE * fp = fopen(filename,"rb"); if(fp == NULL) { printf("Unable to open file %s \n",filename); return NULL; } RSA *rsa= RSA_new() ; if(nPublic) { rsa = PEM_read_RSA_PUBKEY(fp, &rsa,NULL, NULL); } else { rsa = PEM_read_RSAPrivateKey(fp, &rsa,NULL, NULL); } return rsa;}
c). 私钥加密与公钥解密
下面是OpenSSL中私钥加密与公钥解密的接口:
int RSA_public_encrypt(int flen, unsigned char *from, unsigned char *to, RSA *rsa, int padding);int RSA_private_decrypt(int flen, unsigned char *from, unsigned char *to, RSA *rsa, int padding);
接口详述参考 b).公钥加密与私钥解密,或是参考linux Doc。
d). 举个栗子
栗子代码如下:
//rsa_example.cpp#include <openssl/pem.h>#include <openssl/ssl.h>#include <openssl/rsa.h>#include <openssl/evp.h>#include <openssl/bio.h>#include <openssl/err.h>#include <stdio.h>int padding = RSA_PKCS1_PADDING;RSA* createRSAWithFilename(const char*, int);int public_encrypt(unsigned char * data,int data_len, const char* pub_fp, unsigned char *encrypted){ RSA * rsa = createRSAWithFilename(pub_fp,1); int result = RSA_public_encrypt(data_len,data,encrypted,rsa,padding); return result;}int private_decrypt(unsigned char * enc_data,int data_len, const char* pri_fp, unsigned char *decrypted){ RSA * rsa = createRSAWithFilename(pri_fp,0); int result = RSA_private_decrypt(data_len,enc_data,decrypted,rsa,padding); return result;}int private_encrypt(unsigned char * data,int data_len, const char* pri_fp, unsigned char *encrypted){ RSA * rsa = createRSAWithFilename(pri_fp,0); int result = RSA_private_encrypt(data_len,data,encrypted,rsa,padding); return result;}int public_decrypt(unsigned char * enc_data,int data_len, const char *pub_fp, unsigned char *decrypted){ RSA * rsa = createRSAWithFilename(pub_fp,1); int result = RSA_public_decrypt(data_len,enc_data,decrypted,rsa,padding); return result;}void printLastError(char *msg){ char * err = (char*)malloc(130); ERR_load_crypto_strings(); ERR_error_string(ERR_get_error(), err); printf("%s ERROR: %s\n",msg, err); free(err);}RSA * createRSAWithFilename(const char * filename,int nPublic){ FILE * fp = fopen(filename,"rb"); if(fp == NULL) { printf("Unable to open file %s \n",filename); return NULL; } RSA *rsa= RSA_new() ; if(nPublic) { rsa = PEM_read_RSA_PUBKEY(fp, &rsa,NULL, NULL); } else { rsa = PEM_read_RSAPrivateKey(fp, &rsa,NULL, NULL); } return rsa;}int main(){ unsigned char plainText[2048/8] = "Hello this is tab_space"; unsigned char encrypted[4098]={}; unsigned char decrypted[4098]={}; const char* pub_fp = "/home/wsn/crypt/res/public.pem"; const char* pri_fp = "/home/wsn/crypt/res/private.pem"; int encrypted_length= public_encrypt(plainText,strlen((const char*)plainText),pub_fp,encrypted); if(encrypted_length == -1) { printLastError((char*)"Public Encrypt failed "); exit(0); } printf("Encrypted length =%d\n",encrypted_length); int decrypted_length = private_decrypt(encrypted,encrypted_length, pri_fp, decrypted); if(decrypted_length == -1) { printLastError((char*)"Private Decrypt failed "); exit(0); } printf("Decrypted Text =%s\n",decrypted); printf("Decrypted Length =%d\n",decrypted_length); encrypted_length= private_encrypt(plainText,strlen((const char*) plainText),pri_fp,encrypted); if(encrypted_length == -1) { printLastError((char*)"Private Encrypt failed"); exit(0); } printf("Encrypted length =%d\n",encrypted_length); decrypted_length = public_decrypt(encrypted,encrypted_length,pub_fp, decrypted); if(decrypted_length == -1) { printLastError((char*)"Public Decrypt failed"); exit(0); } printf("Decrypted Text =%s\n",decrypted); printf("Decrypted Length =%d\n",decrypted_length);}
编译代码:
[root@xxx crypt]# g++ -o bin/example -I/usr/include/openssl/ -lcrypto src/example.cpp
打印的结果:
[root@xxx crypt]# bin/example Encrypted length =256Decrypted Text =Hello this is tab_spaceDecrypted Length =23Encrypted length =256Decrypted Text =Hello this is tab_spaceDecrypted Length =23
2. 总结
本文讲述了在服务端(Linux/C++)使用OpenSSL库对通信消息进行RSA非对称加解密的技术过程,下一篇会讲述客户端(Unity/C#)使用OpenSSL库对通信消息进行RSA加解密的技术过程。栗子的代码上传至csdz的github中。
- Client-ServerRSA加解密通信方案-Server端(C++)(一)
- Client-ServerRSA加解密通信方案-Client端(C#)(二)
- 通信加解密
- UDP通信client端和server端
- 实现Server和client端的通信
- 网络通信(Server和Client)
- linux下socket通信,server和client简单例子(一)
- Socket编程(一)简单Client与Server的单向通信和双向通信
- 加解密杂烩程序开发(一)
- 加解密 理论基础 一
- linux c server and client 简单的通信
- C#DES加解密
- C++DES加解密
- c base64加解密
- C#-DES加解密
- 用消息队列实现Client和Server间的通信方案
- JSON RESTful Client-Server(一)
- Netty3.0+ server & client Demo(一)
- Python图表绘制:matplotlib绘图库入门
- xLog日志
- AsyncTask的使用方法和理解
- 哪本书是对程序员最有影响、每个程序员都该阅读的书?
- 边双联通分量
- Client-ServerRSA加解密通信方案-Server端(C++)(一)
- Mysql(Linux服务器)root用户密码忘记重置方法
- vitamio视频框架使用详解
- 二叉树系列---基础
- Python机器学习库scikit-learn实践
- Android HTTPDES 原理解析
- react native 遇到的问题总结
- 面向对象 (多态)+JAVA学习笔记-DAY09
- 安卓项目依赖