2019/01/19
8,475
在我之前的博客中聊到了 DH 密钥交换的原理 . 这里来讨论如何使用 OPENSSL 进行 DH 密钥交换。
一 原理验证
OPENSSL 提供了一系列API, 可以在方便地进行大数计算。在这里我们先对 DH 密钥交换的原理 中讲到的原理进行代码级别的验证。对原理不感兴趣的可以跳过这一小节。
由于在 DH 密钥交换过程中,大部分的操作过程都是一样的,可以定义一个基类来抽象:
class DHBase {
public:
DHBase() : m_pri_key(NULL) {}
virtual ~DHBase() {
if (m_pri_key)
BN_free(m_pri_key);
};
const BIGNUM *GetSharedNum() {
if (!m_pri_key)
GenRandomData();
BN_CTX *ctx = BN_CTX_new();
const BIGNUM *g = DH_get0_g(m_dh);
const BIGNUM *p = DH_get0_p(m_dh);
BIGNUM *r = BN_new();
// r = g ^ m_pri_key % p
int rst = BN_mod_exp(r, g, m_pri_key, p, ctx);
assert(rst == 1);
BN_CTX_free(ctx);
return r;
}
BIGNUM *GenKey(const BIGNUM *shared_num) {
if (!m_pri_key)
GenRandomData();
BIGNUM *key = BN_new();
BN_CTX *ctx = BN_CTX_new();
// key = shared_num ^ m_pri_key % p
int rst = BN_mod_exp(key, shared_num, m_pri_key, DH_get0_p(m_dh), ctx);
assert(rst == 1);
BN_CTX_free(ctx);
std::cout << m_name << ": received shared num: " << GetNumString(shared_num) << std::endl;
std::cout << m_name << ": generated KEY: " << GetNumString(key) << std::endl;
return key;
}
std::string GetName() {
return m_name;
}
static std::string GetNumString(const BIGNUM *num) {
return BN_bn2hex(num);
}
private:
void GenRandomData() {
assert(m_dh);
m_pri_key = BN_new();
int rst = BN_rand_range(m_pri_key, DH_get0_p(m_dh));
assert(rst == 1);
while (BN_is_zero(m_pri_key)) {
BN_rand_range(m_pri_key, DH_get0_p(m_dh));
}
std::cout << m_name << ": generated the private num" << std::endl;
}
protected:
DH *m_dh;
std::string m_name;
private:
BIGNUM *m_pri_key;
};
m_pri_key是双方生成的私密随机数。GetSharedNum()返回双方交换的中间变量G^A mod PGenKey()即完成了密钥交换,返回交换成功后的密钥。