Diffie-Hellman算法描述: 目前被许多商业产品交易采用。
HD算法为公开的密钥算法,发明于1976年。该算法不能用于加密或解密,而是用于密钥的传输和分配。
DH 算法的安全性体现在:在有限域上计算离散对数非常困难。
离散对数:定义素数p的原始根(primitive root)为这样一个数,它能生成1~p-1所有数的一个数。现设a为p的原始根,则
a mod p, a2 mod p,…,ap-1 mod p
两两互不相同,构成一个1~p-1的全体数的一个排列。对于任意数b及素数p的原始根a,可以找到一个唯一的指数i,满足b=ai mod p, 0<=i<=p-1
则称指数i为以a为底、模P的b的离散对数。
算法描述:
假如Alice 和 Bob在不安全的网络上进行协商共同的密码:
1.Alice和Bob先说好一个大素数p和它的原始根a
2.Alice随机产生一个数x, 计算X=ax mod p,然后把X发给Bob;
3. Bob秘密产生一个随机数y,计算Y=ay mod p,然后把Y发给Alice;
4.Alice计算k=Yx mod p;
5.Bob计算k*=Xy mod p;
因为
k=Yx mod p= (ay) x mod p=(a x)ymod p=X y mod p= k*
所以 k= k*。
不安全线路上的窃听者只能得到a、p、X、Y,除非能计算离散对数x和y,否则将无法得到密钥k。因此,k为Alice和Bob独立计算出的密钥。
今天主要分析一下D-H算法的实现问题,随机数有很多办法实现,不讨论了。仔细看看D-H算法可以发现除开随机数的生成,实际上就是4次(n^x)mod p的过程。n是随机的大数(或者X,Y),x就各自的原始随机源,而p是大素数。为何要是大素数跟他的原始根呢,数学上有安全性的证明。
要实现D-H算法主要就是
1:p大素数的生成,以及其原始根的查找。这个问题,可以通过查找大素数表,网络上也有很多经典生成办法。
2:(n^x)mod p的运算。n是大数,x也是大数,这样一个运算,空间上64位的数据是远远溢出的,时间上也是一个考验。
拿1024位的x来说,我们虽然可以使用大数库(gmp,openssl,crypt++都可以),但是n^x这样一个结果有多大呢?直接计算是很不划算的做法。
其实我们可以根据以下原理做出改进。
(n^2)mod p=((n mod p)*n) mod p。
这个式子在这里不证明了。
这样做出改进后,我们最大的中间结果最多n^2位而已,空间复杂度从n^x降到n*n,1024位的数据,也不过2000位就能存储。
此外改成这种形式,也便于代码循环处理。
补充,当年其实最大的改进是在于对x运算的使用,并不是简单的x-1次乘法运算来实现,而且用2进制表达的思路来算,简单来说比如x为10(方便举例,实际是个很大值),不是对n做9次乘法,而是算出2进制1001里8次乘法和1次乘法的结果,再做一次乘法。当x很大时,时间复杂度不是O(x),而是O(log x).
已经常年不更了,不做详细描述的,懂的同学,一提即会。
x优化的简要实现如下:
inline void old_dh(int n,int x,int p)
{
struct timeval start_tv,loop1_tv;
gettimeofday(&start_tv, NULL);
long long tmp = n;
for(int i=0;i<x-1;++i)
{
tmp = n*tmp;
tmp = tmp%p;
}
tmp = tmp%p;
gettimeofday(&loop1_tv, NULL);
int iMs = (loop1_tv.tv_sec - start_tv.tv_sec)*1000 + (loop1_tv.tv_usec - start_tv.tv_usec)/1000;
printf("old_dh,n:%lld,x:%lld,p:%lld,res:%lld,cost time %d ms\n",n,x,p,tmp, iMs);
}
inline void new_dh(int n,int x,int p)
{
printf("Begin new_dh\n");
struct timeval start_tv,loop1_tv;
gettimeofday(&start_tv, NULL);
long long sn = n%p;
long long bit_tmp = sn;
long long res_mod_bitmap[64]; //res_mod_bitmap[1] is the result of(n^2)%p,so res_mod_bitmap[4] is the result of(n^16)%p
res_mod_bitmap[0] = sn;
for(int i=1;i<64;++i)
{
bit_tmp = (bit_tmp*bit_tmp);
bit_tmp = bit_tmp%p;
res_mod_bitmap[i] = bit_tmp;
}
printf("finish res_mod_bitmap\n");
//dep x into bit pres(like x is 10,then is binary 1010)
long long res = -1;
for(int i=0;i<64;++i)
{
long long vernier = 1<<i;
if(vernier>x)
break;
if(x&vernier)
{
if(res==-1)
res = res_mod_bitmap[i];
else
res = (res*res_mod_bitmap[i])%p;
}
}
gettimeofday(&loop1_tv, NULL);
int iMs = (loop1_tv.tv_sec - start_tv.tv_sec)*1000 + (loop1_tv.tv_usec - start_tv.tv_usec)/1000;
printf("new_dh,n:%lld,x:%lld,p:%lld,res:%lld,cost time %d ms\n",n,x,p,res, iMs);
}