📄 diffie-hellman.txt
字号:
在http://en.wikipedia.org/wiki/Diffie-Hellman上面给出了这个密钥交换协议的历史,原理,重要文献的链接,以及演示代码。它的数学基础就是离散对数这个数学难题。用它进行密钥交换的过程简述如下:
选取两个大数p和g并公开,其中p是一个素数,g是p的一个模p本原单位根(primitive root module p),所谓本原单位根就是指在模p乘法运算下,g的1次方,2次方……(p-1)次方这p-1个数互不相同,并且取遍1到p-1;
对于Alice(其中的一个通信者),随机产生一个整数a,a对外保密,计算Ka = g^a mod p,将Ka发送给Bob;
对于Bob(另一个通信者),随机产生一个整数b,b对外保密,计算Kb = g^b mod p,将Kb发送给Alice;
在Alice方面,收到Bob送来的Kb后,计算出密钥为:key = Kb^a mod p=(g^b)^a=g^(b*a) mod p;
对于Bob,收到Alice送来的Ka后,计算出密钥为:key = Ka ^ b mod p=(g^a)^b=g^(a*b) mod p。
攻击者知道p和g,并且截获了Ka和Kb,但是当它们都是非常大的数的时候,依靠这四个数来计算a和b非常困难,这就是离散对数数学难题。
要实现Diffie-Hellman密钥交换协议,需要能够快速计算大数模幂,在模幂算法中,仍需计算大数的乘法和模运算,所以整个过程需要三个算法:高精度乘法,高精度除法(用来同时求出一个大数除以另一个大数的商和余数),快速模幂算法。
高精度的乘法和除法可以程序模拟手算。快速模幂算法也是从手算中总结出规律来,例如:
5^8 = (5^2)^4 = (25)^4 = (25^2)^2 = (625)^2,这样,原来计算5^8需要做8次乘法,而现在则只需要三次乘法,分别是:5^2, 25^2, 625^2。这就是快速模幂算法的基础。将算法描述出来,那就是:
算法M:输入整数a,b,p,计算a^b mod p:
M1.初始化c = 1
M2.如果b为0, 则c就是所要计算的结果。返回c的值。算法结束。
M3.如果b为奇数,则令c = c *a mod p, 令b = b-1,转到M2。
M4.如果b为偶数,则令a = a * a mod p, 令b = b / 2,转到M2。
高精度试除法原理简单,但是代码实现起来需要仔细考虑一些细节。
我的演示代码如下:
高精度运算类:
class SuperNumber {
public:
SuperNumber() {
memset(data, 0, MAX_SIZE);
high = 0;
}
// 一般整型到SuperNumber的转换,该版本中不支持负数
SuperNumber(unsigned long l) {
memset(data, 0, MAX_SIZE);
high = 0;
while(l) {
data[++high] = l % 10;
l /= 10;
}
}
// str为字符串形式表示的十进制数
SuperNumber(const char* str) {
assert(str != NULL);
high = strlen(str);
for(int i = high, j = 0; i >= 1; i--, j++) {
data[i] = str[j] - '0';
}
}
SuperNumber(const SuperNumber& s) {
memcpy(data, s.data, MAX_SIZE);
high = s.high;
}
operator const char*() const {
return toString(10);
}
SuperNumber& operator=(const SuperNumber& s) {
if(this != &s) {
memcpy(data, s.data, MAX_SIZE);
high = s.high;
}
return *this;
}
// 将数据置为0
void reset() {
memset(data, 0, MAX_SIZE);
high = 0;
}
// str为字符串形式表示的十进制数
void setToStr(const char* str) {
assert(str != NULL);
high = strlen(str);
for(int i = high, j = 0; i >= 1; i--, j++) {
data[i] = str[j] - '0';
}
}
// 将数据转换成以base指定的进制的字符串形式,默认为十进制
const char* toString(int base = 10) const {
static char buf[MAX_SIZE];
const char table[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if(high == 0) return "0";
assert(base >= 2); // 指定的进制应该不小于2
// 进制转换
buf[MAX_SIZE-1] = '\0';
int begin = MAX_SIZE-1;
char temp[MAX_SIZE];
memcpy(temp, data, MAX_SIZE);
while(1) {
// 找最高位的起始位置
int h = high;
while(temp[h] == 0 && h >= 1) h--;
if(h == 0) break;
// 除基取余
int t = 0;
while(h >= 1) {
t = t * 10 + temp[h];
temp[h] = t / base;
t = t % base;
h--;
}
buf[--begin] = table[t];
}
return buf+begin;
}
// 乘法
SuperNumber operator*(const SuperNumber& s) const {
SuperNumber result; // default set to 0
int i, j;
// 相乘
for(i = 1; i <= high; i++) {
for(j = 1; j <= s.high; j++) {
int k = data[i] * s.data[j] + result.data[i+j-1];
result.data[i+j-1] = k % 10;
result.data[i+j] += k / 10;
}
}
// 进位
for(i = 1; i < MAX_SIZE - 1; i++) {
if(result.data[i] >= 10) {
result.data[i+1] += result.data[i] / 10;
result.data[i] %= 10;
}
}
// 确定最高位
for(i = MAX_SIZE-1; i >= 1 && result.data[i] == 0; i--);
result.high = i;
return result;
}
// 除法,内部调用doDivide来实现
SuperNumber operator/(const SuperNumber& s) const {
SuperNumber q, r;
doDivide(s, q, r);
return q;
}
// 模运算,内部调用doDivide来实现
SuperNumber operator%(const SuperNumber& s) const {
SuperNumber q, r;
doDivide(s, q, r);
return r;
}
// 除法运算,一次除法运算中同时得到商和余数,运算符/和%的重载
// 内部会调用这个函数,dest为除数,Q为商,R为余数,算法使用试除法
void doDivide(const SuperNumber& dest, SuperNumber& Q, SuperNumber& R) const {
int i, j, t;
Q.reset();
Q.high = high - dest.high + 1; // 商的初始位数
R = *this; // 余数初始实为被除数
t = dest.high;
// 判断除法是否结束
while(R >= dest) {
// 循环相减进行试除
while(dest >= R.sub(1, t)) {
Q.data[Q.high--] = 0;
++t;
}
while(R.sub(1, t) >= dest) {
// i为相减时被除数最低下标,j为除数最低下标
for(i=R.high-t+1,j=1; j<=dest.high; i++,j++) {
R.data[i] -= dest.data[j];
if(R.data[i] < 0) {
R.data[i] += 10;
R.data[i+1] -= 1;
}
}
while(R.data[i] < 0 && i <= R.high) {
R.data[i] += 10;
R.data[i+1] -= 1;
++i;
}
Q.data[Q.high] += 1;
}
// 一次试除结束,更新商的最高位下标
Q.high -= 1;
// 更新被除数的最高位下标
while(R.data[R.high] == 0) {
R.high--;
t--;
}
t+=1; // 下一位被除数
}
Q.high = high - dest.high + 1;
while(Q.data[Q.high] == 0) Q.high -= 1;
R.high = high;
while(R.data[R.high] == 0) R.high -= 1;
}
// 大数模幂算法,很简单的自然算法,即将指数分解为二进制,换句
// 更简单的话来说,就是不断地找平方模幂,而不是全部乘方后再
// 做一次最终的模运算
// a.power_mod(p, n)计算a^p mod n
SuperNumber power_mod(int power, SuperNumber n) const {
SuperNumber c("1"), t(*this);
while(power) {
if(power % 2) {
c = c * t % n;
power -= 1;
} else {
t = t * t % n;
power /= 2;
}
}
return c%n;
}
bool operator>=(const SuperNumber& s) const {
if(high == s.high) {
int k = high;
while(data[k] == s.data[k] && k >= 1)k--;
if(k < 1) return true; // equal
return data[k] > s.data[k];
} else if(high > s.high) return true;
return false;
}
bool operator<(const SuperNumber& s) const {
return !(*this >= s);
}
// 从十进制表示的最高位开始数起,数到第i位,从第i位开始截取连续
// 的c位数字出来组成一个新的数。例如:数据是12345678925698,则
// sub(3, 5)返回数字34567,如果数字不够取,例如在34567上运行
// sub(3, 5),因为34567从高位数起第3个数字是5,剩下的数字是567,
// 最多只有三个,不够取要求的5个,这个时候返回567,不报错。
SuperNumber sub(int i, int c) const {
SuperNumber ret;
assert(high >= i); // 保证可截取
i = high - i + 1; // 从高位数起的第i个数位的下标
if(i >= c) {
ret.high = c;
while(c >= 1) ret.data[c--] = data[i--];
} else {
ret.high = i;
while(i >= 1) {
ret.data[i] = data[i];
i--;
}
}
// 过滤前导0
while(ret.data[ret.high] == 0) ret.high--;
return ret;
}
// I/O
friend istream& operator>>(istream& in, SuperNumber& s) {
char t[256];
in >> t;
s.setToStr(t);
return in;
}
friend ostream& operator<<(ostream& out, const SuperNumber& s) {
return out << s.toString(10);
}
private:
enum {MAX_SIZE=256}; // 最大十进制位数
// 须注意,使用data[0]存储最高位所在下标是自己的一点小聪明,后来在
// 调试的时候发现这是一个极大的错误,不过对于此题目来说可以应付
char data[MAX_SIZE]; // 数据的内部表示,字符串形式的十进制
// 其中data[0]存储最高位所在下标,data[1]
// 存储数据的最低位,也就是个位
int high;
};
主函数:
int main(int argc, char **argv) {
freopen("in.txt", "r", stdin);
SuperNumberTest st;
// st.run();
// g和n都是超过2^127的素数。它们在DH算法中公开
SuperNumber g, n;
int a, b;
SuperNumber ka, kb, key;
srand(time(0));
cin >> g >> n;
cout << "g = " << g << endl
<< "n = " << n << endl;
cout << "\nThis is Alice:\n";
a = rand();
cout << "Alice get a random integer a = " << a << endl;
cout << "Alice computer g^a mod n:\n";
ka = g.power_mod(a, n);
cout << "Alice compute out ka = " << ka << endl;
cout << "\nThis is Bob:\n";
b = rand();
cout << "Bob get a random integer b = " << b << endl;
cout << "Bob compute g^b mod n:\n";
kb = g.power_mod(b, n);
cout << "Bob compute out kb = " << kb << endl;
cout << "\nAlice get kb from Bob, she compute out key is:\n";
cout << kb.power_mod(a, n) << endl;
cout << "\nBob get ka from Alice, he compute out key is:\n";
cout << ka.power_mod(b, n) << endl;
return 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -