前言
- 做的东西涉及一些指令的传输,很容易通过抓包获取指令内容,为了保证指令安全,需要对指令进行加密,决定自己实现一个基于Diffie-Hellman密钥协商算法的封装类
-
GitHub
原理
密钥交换协议
1、通信双方通过协商,共同建立会话密钥,任何一个参与者均对结果产生影响,不需要任何第三方参与
2、会话密钥由每个协议参与者分别产生的参数通过计算得到
Diffie-Hellman密钥协商过程(左为A,右为B)
1、初始化:A选取一个生成元
g
和一个大素数
p
,发送给B
2、A随机选取0<
x
<p-1,计算
X
= g
x
mod p后,将X传给B
3、B随机选取0<
y
<p-1,计算
Y
= g
y
mod p后,将Y传给A
4、A计算Y
x
mod p得到会话密钥
K1
5、B计算X
y
mod p得到会话密钥
K2
6、由下可知 K1 == K2,此时双方便可利用会话密钥进行加密通信了
实现
crypto.h
#ifndef CRYPTO_H
#define CRYPTO_H
#include <stdlib.h>
/**
* 作者:公羽向阳
* 时间:20201025
* 编译器:TDM-GCC 4.8.1 64bit
*
* 基于DH的密钥协商加密类
* 1、请先使用Crypto::init()方法初始化随机种子
* 2、客户端构造此类,服务端通过客户端生成的 [g,p] 构造
* 3、客户端通过调用get_public()方法得到一个数组 [g, p]
* 4、客户端、服务端通过get_pub_key()方法获取公钥 C、S 用于协商
* 5、客户端、服务端通过get_key(size_t pub_key)方法获取密钥用于加密
* 6、客户端、服务端可使用crypto(char *data, size_t size, const char *key)方法加解密
*/
class Crypto
{
public:
Crypto();
Crypto(size_t g, size_t p);
static void init();
size_t *get_public();
size_t get_pub_key();
char *get_key(size_t pub_key);
void crypto(char *data, size_t size, const char *key);
virtual ~Crypto();
protected:
size_t create_big_integer();
size_t pow_mod(size_t n, unsigned int times, size_t mod);
private:
size_t _g, _p;
size_t *_public;
size_t _private;
size_t _pub_key;
char *_pri_key;
};
#endif
crypto.cpp
#include "crypto.h"
#include <stdio.h>
#include <unistd.h>
#include <time.h>
Crypto::Crypto()
{
this->_g = (unsigned)rand()%9+2;
this->_p = create_big_integer();
this->_private = (size_t)rand()%this->_p; // create_big_integer()>>8%this->_p;
this->_pub_key = pow_mod(this->_g, this->_private, this->_p);
this->_pri_key = new char[sizeof(size_t)+1];
this->_public = new size_t[2];
}
Crypto::Crypto(size_t g, size_t p)
{
this->_g = g;
this->_p = p;
this->_private = (size_t)rand()%this->_p; // create_big_integer()>>8%this->_p;
this->_pub_key = pow_mod(this->_g, this->_private, this->_p);
this->_pri_key = new char[sizeof(size_t)+1];
this->_public = new size_t[2];
}
void Crypto::init()
{
srand((unsigned)time(0));
}
size_t *Crypto::get_public()
{
this->_public[0] = _g;
this->_public[1] = _p;
return this->_public;
}
size_t Crypto::get_pub_key()
{
return this->_pub_key;
}
char *Crypto::get_key(size_t pub_key)
{
size_t tmp = pow_mod(pub_key, this->_private, this->_p);
sprintf(this->_pri_key, "%08x", tmp);
return this->_pri_key;
}
void Crypto::crypto(char *data, size_t size, const char *key)
{
size_t len = strlen(key);
for(size_t i=0; i<size; ++i)
{
data[i] ^= key[i%len];
}
}
size_t Crypto::create_big_integer()
{
return ((size_t)((size_t)rand()<<16)|(size_t)rand());// | (size_t)0x80000000; // 8:1000, 28*0
}
size_t Crypto::pow_mod(size_t n, unsigned int times, size_t mod)
{
size_t tmp = 1;
for(unsigned int i=0; i<times; i++)
{
tmp = tmp*n%mod;
}
return tmp;
}
Crypto::~Crypto()
{
delete [] this->_public;
delete [] this->_pri_key;
}
main.cpp
#include <iostream>
#include <string.h>
#include "crypto.h"
using namespace std;
void print(char *data, int size)
{
for(int i=0; i<size; i++)
{
cout << data[i];
}
cout << endl;
}
int main(int argc, char** argv)
{
Crypto::init(); // 初始化随机种子
// 模拟协商
Crypto client; // A
size_t *g_p = client.get_public(); // 生成 (g, p)
size_t X = client.get_pub_key(); // 计算 X
Crypto server(g_p[0], g_p[1]); // B, 接收 (g, p)
size_t Y = server.get_pub_key(); // 计算 Y
char *K2 = server.get_key(X); // B 计算会话密钥
char *K1 = client.get_key(Y); // A 通过 B 发的 Y 计算会话密钥
cout << "K1: " << K1 << "\nK2: " << K2 << endl << endl;
if(strcmp(K1, K2))
{
cout<< "会话密钥生成错误" << endl;
return 0;
}
// 模拟会话
char data[11] = "helloworld"; // 明文
cout << "A加密: " << endl;
client.crypto(data, 10, K1); // 使用密钥加密
print(data, 10);
cout << "==========" << endl;
cout << "B解密: " << endl;
server.crypto(data, 10, K2); // 使用密钥解密
print(data, 10);
cout << "==========" << endl;
return 0;
}
效果
补充
原理中说的是生成随机大素数,在实现的时候我生成的是大整数,因为大素数生成是个麻烦的事情,我也尝试写过,但是生成速度太慢,影响体验。考虑到我们的加密方式是按位异或加解密,用这种方法就算不使用大素数依旧不会出问题,所以直接生成的是一个大整数,关于大素数的生成可以看一下
这篇文章
,讲得挺好的。