2019/01/19
6,843
在我之前的博客中聊到了 DH 密钥交换的原理 . 这里来讨论如何使用 OPENSSL 进行 DH 密钥交换。
一 原理验证
OPENSSL 提供了一系列API, 可以在方便地进行大数计算。在这里我们先对 DH 密钥交换的原理 中讲到的原理进行代码级别的验证。对原理不感兴趣的可以跳过这一小节。
由于在 DH 密钥交换过程中,大部分的操作过程都是一样的,可以定义一个基类来抽象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
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 P
GenKey()
即完成了密钥交换,返回交换成功后的密钥。