云计算百科
云计算领域专业知识百科平台

银行安全传输平台(六)秘钥协商服务器和客户端实现-1

文章目录

  • 秘钥协商服务器和客户端实现
  • 一、JsonCpp
    • 1.1 json如何组织数据
    • 1.2 server.json
    • 1.3 client.json
  • 二、秘钥协商客户端业务流程实现
    • 2.1 客户端流程
    • 2.2 流程实现
      • 2.2.1 Client.h
      • 2.2.2 Client.cpp
  • 三、秘钥协商服务器业务流程实现
    • 3.1 服务器流程
    • 3.2 流程实现
      • 3.2.1 ServerOP.h
      • 3.2.2 ServerOP.cpp
  • 四、main函数实现
    • 4.1 客户端
    • 4.2 服务器

秘钥协商服务器和客户端实现

由于秘钥协商服务器和客户端从的操作内容较多,我们分两篇笔记来记录这一部分,这篇笔记主要记录Jsoncpp和一般步骤(就是业务流程)。

一、JsonCpp

Json使数据的一种描述形式,以某种格式将数据组织起来。在应用程序启动的时候, 需要加载少量数据, 初始化应用程序,比如登录的用户名, 密码;远程数据库连接;连接的远程服务器地址;这样做可以让程序更加灵活,比如我们要修改初始配置,无需重新编译,运维只要改 JSON 即可调整行为。

1.1 json如何组织数据

主要有两种方式一种是json数组(本项目没用到,所以不做记录),一种是json对象:

  • 使用{}表示
    • 分为两部分, key: value
    • 键值对之间使用都被间隔, 最后一个键值对后边不要写逗号, 否则会解析失败
    • key: 必须是字符串, 对要存储的数据的描述
    • value: 要保存的数据, 数据格式可以有多种:
      • 整形, 浮点, 字符串, 布尔,

可以嵌套使用:

{
"name":"蕾蕾",
"age":12,
"sex":"man",
"婚否":flase,
"falily":["爸爸", "妈妈", "弟弟"],
"资产":{
"car":"BMW",
"房子":"北京故宫"
}
}

1.2 server.json

配置文件

{
"serverID": "666",
"dbUser": "xyz",//数据库用户
"dbPasswd": "xxxxxx",//数据库密码
"dbSID": "192.168.164.xxx:1521/orcl",
"port": 8989,
"maxnode": 20,//共享内存中最大节点数
"shmkey": "/home/bin"
}

加载示例:

ifstream ifs("server.json");
Reader r; Value root;
r.parse(ifs, root);
m_port = root["port"].asInt();
m_serverID = root["serverID"].asString();
m_dbUser = root["dbUser"].asString();
m_dbPwd = root["dbPasswd"].asString();
m_dbConnStr = root["dbSID"].asString();
int maxNode = root["maxnode"].asInt();
string shmKey = root["shmkey"].asString();
m_shm = new SecKeyShm(shmKey, maxNode);

1.3 client.json

配置文件:

{
"clientID": "666",
"serverID": "999",
"serverIP": "127.0.0.1",
"serverPort": 8989,
"maxNode": 1,
"shmKey": "/usr/local/bin"
}

加载示例:

ifstream ifs("client.json");
Reader r; Value root;
r.parse(ifs, root);
m_info.ClientID = root["clientID"].asString();
m_info.ServerID = root["serverID"].asString();
m_info.ip = root["serverIP"].asString();
m_info.port = root["serverPort"].asInt();
int maxNode = root["maxNode"].asInt();
string shmKey = root["shmKey"].asString();
m_shm = new SecKeyShm(shmKey, maxNode);

这样运维只需维护两份 JSON,即可灵活控制服务器与客户端行为。

二、秘钥协商客户端业务流程实现

2.1 客户端流程

  • 首先用户在交互界面选择“密钥协商”,程序读取 client.json 中的 clientID、serverID、serverIP 和 serverPort,并生成一对 RSA 密钥(公钥/私钥)。
  • 将客户端id、服务器id、公钥(客户端自己用于非对称加密的公钥)和其 SHA-1 哈希签名填入 RequestMsg 结构体(cmdType=1),然后用 Protobuf 将该结构体序列化成字节流。

数据分析:

//客户端使用的结构体
struct RequestMsg
{
int cmdType;
string clientID;
string serverID;
string sign;
string data;
};

message RequestMsg
{
int32 cmdType=1;
bytes clientID=2;
bytes serverID=3;
bytes sign=4;
bytes data=5;
}

// 数据分析
cmdType: 发送给服务器的数据, 服务器需要对去进行判断:
1 -> 秘钥协商
2 -> 秘钥校验
3 -> 秘钥注销
clientID: 所有有效的客户端都会被分配给一个有效的唯一的ID
serverID: 客户端要连接的服务器对应的ID
data: 对应的业务数据, 根据cmdType不同而不同
sign: 签名, 对data签名

  • 利用封装好的 TcpSocket,按配置的 IP 和端口连接服务器,发送刚才的序列化数据,并阻塞等待服务器的响应。
  • 收到服务器返回的 RespondMsg(内含用公钥加密的对称密钥和 seckeyID)后,用 Protobuf 反序列化得到密文,再用私钥解密出真正的对称密钥,最后将该密钥及其 ID 写入共享内存,完成协商。

2.2 流程实现

2.2.1 Client.h

#pragma once
#include <string>
using namespace std;

struct ClientInfo
{
string ServerID;
string ClientID;
string ip;
unsigned short port;
};

class ClientOP
{
public:
ClientOP(string jsonFile);
~ClientOP();

// 秘钥协商
bool seckeyAgree();

// 秘钥校验
void seckeyCheck() {}

// 秘钥注销
void seckeyZhuXiao() {}

private:
ClientInfo m_info;
};

2.2.2 Client.cpp

#include "ClientOP.h"
#include <jsoncpp/json/json.h>
#include <fstream>
#include <sstream>
#include <iostream>
#include "RequestFactory.h"
#include "RequestCodec.h"
#include "RsaCrypto.h"
#include "TcpSocket.h"
#include "RespondFactory.h"
#include "RespondCodec.h"
#include "Message.pb.h"
#include "Hash.h"
using namespace std;
using namespace Json;

ClientOP::ClientOP(string jsonFile)
{
// 解析json文件, 读文件 -> Value
ifstream ifs(jsonFile);
Reader r;
Value root;
r.parse(ifs, root);
// 将root中的键值对value值取出
m_info.ServerID = root["ServerID"].asString();
m_info.ClientID = root["ClientID"].asString();
m_info.ip = root["ServerIP"].asString();
m_info.port = root["Port"].asInt();
}

ClientOP::~ClientOP()
{
}

bool ClientOP::seckeyAgree()
{
// 0. 生成密钥对, 将公钥字符串读出
RsaCrypto rsa;
// 生成密钥对
rsa.generateRsakey(1024);
// 读公钥文件
ifstream ifs("public.pem");
stringstream str;
str << ifs.rdbuf();
// 1. 初始化序列化数据
// 序列化的类对象 -> 工厂类创建
RequestInfo reqInfo;
reqInfo.clientID = m_info.ClientID;
reqInfo.serverID = m_info.ServerID;
reqInfo.cmd = 1;// 秘钥协商
reqInfo.data = str.str();// 非对称加密的公钥
// 创建哈希对象
Hash sha1(T_SHA1);
sha1.addData(str.str());
reqInfo.sign = rsa.rsaSign(sha1.result());// 公钥的的哈希值签名
cout << "签名完成…" << endl;
CodecFactory* factory = new RequestFactory(&reqInfo);
Codec* c = factory->createCodec();
// 得到序列化之后的数据, 可以将其发送给服务器端
string encstr = c->encodeMsg();
// 释放资源
delete factory;
delete c;

// 套接字通信, 当前是客户端, 连接服务器
TcpSocket* tcp = new TcpSocket;
// 连接服务器
int ret = tcp->connectToHost(m_info.ip, m_info.port);
if (ret != 0)
{
cout << "连接服务器失败…" << endl;
return false;
}
cout << "连接服务器成功…" << endl;
// 发送序列化的数据
tcp->sendMsg(encstr);
// 等待服务器回复
string msg = tcp->recvMsg();

// 解析服务器数据 -> 解码(反序列化)
// 数据还原到 RespondMsg
factory = new RespondFactory(msg);
c = factory->createCodec();
RespondMsg* resData = (RespondMsg*)c->decodeMsg();
// 判断状态
if (!resData->status())
{
cout << "秘钥协商失败" << endl;
return false;
}
// 将得到的密文解密
string key = rsa.rsaPriKeyDecrypt(resData->data());
cout << "对称加密的秘钥: " << key << endl;
// 秘钥写入共享内存中

delete factory;
delete c;
// 这是一个短连接, 通信完成, 断开连接
tcp->disConnect();
delete tcp;

return true;
}

三、秘钥协商服务器业务流程实现

3.1 服务器流程

服务器端作为守护进程启动后,首先从 server.json 中读取监听端口、数据库和共享内存配置,然后调用 TcpServer::setListen(port) 开始监听,并在一个循环里通过 acceptConn() 接受来自多个客户端的连接(可用多线程或 I/O 多路复用以支持并发):

  • 收到客户端请求数据 ->解析,序列化之后的数据,然后将数据反序列化得到结构体
  • 根据cmdType判断客户端想要干什么:

switch(cmdType)
{
case 1:
秘钥协商();
break;
case 2:
秘钥校验();
break;
case 3:
秘钥注销();
break;
default:
break;
}

  • 请求秘钥协商:检查 clientID/serverID 是否在允许列表中,再读出 data(客户端公钥),用 OpenSSL 对该公钥的 SHA-1 哈希和 sign 进行验签,确保公钥未被篡改。
  • 生成aes密钥并发送,用 getRandKey(16) 随机生成一段 16 字节字符串作为 AES 对称密钥,用客户端公钥将对称密钥加密为密文
  • 初始化回复数据,然后序列化发送数据,最后通过网络通信发送到客户端。

3.2 流程实现

3.2.1 ServerOP.h

#pragma once
#include <map>
#include "TcpServer.h"
#include "Message.pb.h"
// 处理客户端请求
class ServerOP
{
public:
enum KeyLen {Len16=16, Len24=24, Len32=32};
ServerOP(string json);
// 启动服务器
void startServer();
// 线程工作函数 -> 推荐使用
static void* working(void* arg);
// 友元破坏了类的封装
friend void* workHard(void* arg);
// 秘钥协商
string seckeyAgree(RequestMsg* reqMsg);
~ServerOP();

private:
string getRandKey(KeyLen len);

private:
string m_serverID;// 当前服务器的ID
unsigned short m_port;
map<pthread_t, TcpSocket*> m_list;
TcpServer *m_server = NULL;
};

void* workHard(void* arg);

3.2.2 ServerOP.cpp

#include "ServerOP.h"
#include "TcpSocket.h"
#include "RequestFactory.h"
#include "RequestCodec.h"
#include "RespondCodec.h"
#include "RespondFactory.h"
#include "RsaCrypto.h"
#include <string>
#include <iostream>
#include <fstream>
#include <jsoncpp/json/json.h>
#include <unistd.h>
#include "Hash.h"
using namespace std;
using namespace Json;

/*
{
"Port":9898
}
*/

ServerOP::ServerOP(string json)
{
// 解析json文件, 读文件 -> Value
ifstream ifs(json);
Reader r;
Value root;
r.parse(ifs, root);
// 将root中的键值对value值取出
m_port = root["Port"].asInt();
m_serverID = root["ServerID"].asString();
}

void ServerOP::startServer()
{
m_server = new TcpServer;
m_server->setListen(m_port);
while (1)
{
cout << "等待客户端连接…" << endl;
TcpSocket* tcp = m_server->acceptConn();
if (tcp == NULL)
{
continue;
}
cout << "与客户端连接成功…" << endl;
// 通信
pthread_t tid;
// 这个回调可以是类的静态函数, 类的友元函数, 普通的函数
// 友元的类的朋友, 但是不属于这个类
// 友元函数可以访问当前类的私有成员
pthread_create(&tid, NULL, workHard, this);
m_list.insert(make_pair(tid, tcp));
}
}

void * ServerOP::working(void * arg)
{
return nullptr;
}

string ServerOP::seckeyAgree(RequestMsg* reqMsg)
{
// 0. 对签名进行校验 -> 公钥解密 -> 得到公钥
// 将收到的公钥数据写入本地磁盘
ofstream ofs("public.pem");
ofs << reqMsg->data();
ofs.close();
// 创建非对称加密对象
RespondInfo info;
RsaCrypto rsa("public.pem", false);

// 创建哈希对象
Hash sha(T_SHA1);
sha.addData(reqMsg->data());
cout << "1111111111111111" << endl;
bool bl = rsa.rsaVerify(sha.result(), reqMsg->sign());
cout << "00000000000000000000" << endl;
if (bl == false)
{
cout << "签名校验失败…" << endl;
info.status = false;
}
else
{
cout << "签名校验成功…" << endl;
// 1. 生成随机字符串
// 对称加密的秘钥, 使用对称加密算法 aes, 秘钥长度: 16, 24, 32byte
string key = getRandKey(Len16);
cout << "生成的随机秘钥: " << key << endl;
// 2. 通过公钥加密
cout << "aaaaaaaaaaaaaaaa" << endl;
string seckey = rsa.rsaPubKeyEncrypt(key);
cout << "加密之后的秘钥: " << seckey << endl;
// 3. 初始化回复的数据
info.clientID = reqMsg->clientid();
info.data = seckey;
info.seckeyID = 12;// 需要数据库操作
info.serverID = m_serverID;
info.status = true;
}

// 4. 序列化
CodecFactory* fac = new RespondFactory(&info);
Codec* c = fac->createCodec();
string encMsg = c->encodeMsg();
// 5. 发送数据
return encMsg;

}

ServerOP::~ServerOP()
{
if (m_server)
{
delete m_server;
}
}

// 要求: 字符串中包含: a-z, A-Z, 0-9, 特殊字符
string ServerOP::getRandKey(KeyLen len)
{
// 设置随机数数种子 => 根据时间
srand(time(NULL));
int flag = 0;
string randStr = string();
char *cs = "~!@#$%^&*()_+}{|\\';[]";
for (int i = 0; i < len; ++i)
{
flag = rand() % 4;// 4中字符类型
switch (flag)
{
case 0:// a-z
randStr.append(1, 'a' + rand() % 26);
break;
case 1: // A-Z
randStr.append(1, 'A' + rand() % 26);
break;
case 2: // 0-9
randStr.append(1, '0' + rand() % 10);
break;
case 3: // 特殊字符
randStr.append(1, cs[rand() % strlen(cs)]);
break;
default:
break;
}
}
return randStr;
}

void* workHard(void * arg)
{
sleep(1);
string data = string();
// 通过参数将传递的this对象转换
ServerOP* op = (ServerOP*)arg;
// 从op中将通信的套接字对象取出
TcpSocket* tcp = op->m_list[pthread_self()];
// 1. 接收客户端数据 -> 编码
string msg = tcp->recvMsg();
// 2. 反序列化 -> 得到原始数据 RequestMsg 类型
CodecFactory* fac = new RequestFactory(msg);
Codec* c = fac->createCodec();
RequestMsg* req = (RequestMsg*)c->decodeMsg();
// 3. 取出数据
// 判断客户端是什么请求
switch (req->cmdtype())
{
case 1:
// 秘钥协商
data = op->seckeyAgree(req);
break;
case 2:
// 秘钥校验
break;
default:
break;
}

// 释放资源
delete fac;
delete c;
// tcp对象如何处理
tcp->sendMsg(data);
tcp->disConnect();
op->m_list.erase(pthread_self());
delete tcp;

return NULL;
}

四、main函数实现

4.1 客户端

#include <iostream>
#include <string>
#include "ClientOP.h"
using namespace std;

int usage();
int main()
{
// 创建客户端操作类对象
ClientOP op("client.json");
while (1)
{
int sel = usage();
switch (sel)
{
case 1:
// 秘钥协商
op.seckeyAgree();
break;
case 2:
op.seckeyCheck();
// 秘钥校验
break;
case 3:
// 秘钥注销
op.seckeyZhuXiao();
default:
break;

}
}
cout << "客户端退出, bye,byte…" << endl;
return 0;
}

int usage()
{
int nSel = 1;
printf("\\n /*************************************************************/");
printf("\\n /*************************************************************/");
printf("\\n /* 1.密钥协商 */");
printf("\\n /* 2.密钥校验 */");
printf("\\n /* 3.密钥注销 */");
printf("\\n /* 4.密钥查看 */");
printf("\\n /* 0.退出系统 */");
printf("\\n /*************************************************************/");
printf("\\n /*************************************************************/");
printf("\\n\\n 选择:");

scanf("%d", &nSel);
while (getchar() != '\\n');

return nSel;
}

4.2 服务器

#include <cstdio>
#include "ServerOP.h"

int main()
{
ServerOP op("server.json");
op.startServer();

return 0;
}


赞(0)
未经允许不得转载:网硕互联帮助中心 » 银行安全传输平台(六)秘钥协商服务器和客户端实现-1
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!