RSA算法中的大素数-素性测试

0 前言

RSA算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但是想要对其乘积进行因式分解却极其困难。
本文讨论关于素数的相关知识。

1 素性测试

问题1: 如何判断一个大整数是否为素数

1.1 试除法

试除法: 判定n是否是素数,写一个循环从2到sqrt(n)判断其能否整除n,若都不能则n为素数。

如果n比较小的话,采用试除法当然是非常高效快捷的。但是当n很大的时候,这个算法可就行不通了
以RSA1024为例,当公钥为

0x890e23101a542913da8a4350672c9ef8e7b34c2687ce8cd8db3fb34244a791d60c9dc0a53172a56dcc8a66f553c0ae51e9e2e2ce9486fa6b00a6c556bfed139001133cdfe5921c425eb8823b1bd0a4c00920d24bee2633256328502eadbfac1420f9a5f47139de6f14d8eb7c2b7c0cec42530c0a71dadb80c7214f5cd19a3f2f

时,两个质因数分别为

0xe5a111a219c64f841669400f51a54dd4e75184004f0f4d21c6ae182cfb528652a02d6d677a72b564c505b1ed42a0c648dbfe14eb66b04c0d60ba3872826c32e7

0x98cb760764484e29245521be08e7f38edeebfca8427149524ba7f4735e1d5f3a45d585cb3722ff4c07c19165be738311dc346a914966f5b311416fed3b425079

转换为十进制分别为

12026655772210679470465581609002525329245773732132014742758935511187863487919026457076252932048619706498126046597130520643092209728783224795661331197604583

8002511426596424351829267099531651390448054153452321185350746845306277585856673898048740413439442356860630765545600353049345324913056448174487017235828857

这是一个155位数和一个154位数,都在2的511次方左右,这是无法进行常规计算的!
当然,并不是没有办法的,数学家们总有一些歪门邪道来给我们指明道路。

1.2 费马小定理

费马小定理是数论中的一个定理:假如a是一个整数,p是一个质数,那么ap−a是p的倍数,可以表示为 ap ≡a(modp) 如果a不是p的倍数,这个定理也可以写成 ap−1≡1(modp)

需要注意的是,费马小定理是判定一个数是否为素数的必要条件,并非充分条件,因为存在着一些伪素数满足费马小定理却不是素数,如2340≡1(mod341),但是341=11×31

1.3 Fermat素性测试

我们只考虑了a=2的情况,如果我们考虑a=3的情况,一个合数可能在a=2时通过了测试,但a=3时的计算结果却排除了素数的可能。于是,人们扩展了伪素数的定义,称满足an−1modn=1的合数n叫做以a为底的伪素数(pseudoprime to base a)。前10亿个自然数中同时以2和3为底的伪素数只有1272个,这个数目不到刚才的1/4。这告诉我们如果同时验证a=2和a=3两种情况,算法出错的概率降到了0.000025。容易想到,选择用来测试的a越多,算法越准确。通常我们的做法是,随机选择若干个小于待测数的正整数作为底数a进行若干次测试,只要有一次没有通过测试就可以判定这个数为合数。这就是Fermat素性测试。

1.4 Miller-Rabin素性测试

要测试N是否为素数,首先将N−1分解为2sd。在每次测试开始时,先随机选一个介于[1,N−1]的整数a,之后如果对所有的r∈[0,s−1],若admodN≠1且a2rdmodN≠−1,则N是合数。否则,N有3/4的概率为素数。

Fermat素性测试一样,Miller-Rabin素性测试依然只能判定一个数可能是素数,但是这个方法却以它的快速、高效而广为使用。

决定型素数测试

一个确定性的判断一个大整数是否为素数的方法—AKS素数测试

AKS素数测试(又被称为Agrawal–Kayal–Saxena素数测试和Cyclotomic AKS test)是一个决定型素数测试算法,由三个来自Indian Institute of Technology Kanpur的计算机科学家,Manindra Agrawal、Neeraj Kayal和Nitin Saxena,在2002年8月6日发表于一篇题为PRIMES is in P(素数属于P)的论文。作者们因此获得了许多奖项,包含了2006年的哥德尔奖和2006年的Fulkerson Prize。这个算法可以在多项式时间之内,决定一个给定整数是素数或者合数。

2 生成大素数

最直观的方法就是随机搜索,例如要生成一个100位的大素数,我们先随机生成一个数字序列,然后用Miller-Rabin素性测试对其进行测试即可,如果不是素数的话再随机生成一个,如此循环下去。
当然我们可以采用随机搜索法(每次生成一个完全不一样的随机数),也可以采用随机递增搜索法(生成一个随机数之后,每次对其加2)
生成一个n位十进制大素数的步骤如下:

  1. 产生一个n位的随机数p,且最高位不能为0
  2. 若最低位为偶数,则将它加1,保证该数为奇数以节省时间
  3. 测试该数能否被10000以下的素数(共1228个)整除,这样可以快速排除许多合数,节省时间
  4. 2p-1这间随机生成一个数a,以a为底对p进行Miller-Rabin素性测试,若不通过说明p为合数。若通过则再选取一个a对p进行测试。选取a时应该选取尽可能小的素数,以提高运算速度。大概进行5次Miller-Rabin素性测试后,精确性就比较高了
  5. p每次测试都通过,则认为p是素数。否则p←p+2,再次对p进行测试

大素数生成过程有两个关键点,一个是素性测试,另一个是随机数的生成。前者要保证正确性和高效性,后者则一定要保证安全性,即生成一个真随机数

大部分程序和语言中的随机数(比如C和MATLAB中的),确实都只是伪随机。是由可确定的函数(比如线性同余),通过一个种子(比如时钟),产生的伪随机数。不过UNIX内核中的随机数发生器(/dev/random),它在理论上能产生真随机。即这个随机数的生成,独立于生成函数,或者说这个产生器是非确定的。所以,计算机理论上可以产生统计意义上的真随机数。

参考: 开源Javascript加密函数库

后续

棱镜计划

据NSA前通讯员斯诺登所提供的机密文件显示,NSA跟RSA达成了一份价值1000万美元的合同,前者通过在后者的加密软件中植入一个缺陷公式,为自己留了一道“后门”。据悉,RSA存有缺陷公式的软件叫做Bsafe,而该缺陷公式的名字为Dual Elliptic Curve,它由NSA开发而出。文件内容指出,RSA自2004年起就开始在自己的软件中使用了这一缺陷公式。
RSA Security是由RSA算法的发明者Ron Rivest, Adi Shamir和Len Adleman在1982年创立,随后在2006年以21亿美元的价格被EMC公司收购。其中该算法最为有名的一个缺陷就是DUAL_EC_DRBG,密码学家早在几年前就发现了这个问题。这个加密算法可以看作是随机数生成器,但是有些数字是固定的,密码学家能够将其作为万能钥匙通过一些内置的算法进行破解。

RSA算法本身没什么问题,因为只要你的密钥是真正随机产生的,猜对这个密钥就如同大海捞针一般难,现有计算机肯定无法在密码更换周期内攻破你的加密档案。但是,如果这个随机算法是假的呢?如果它仅仅是在一个很小的集合中产生的密钥呢?你的加密档案瞬间就被查看了,这就是NSA干的事情。

  • 1
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Miller-Rabin算法是一种常用的素性检验算法,它可以在多项式时间内判断一个数是否为素数。下面是使用C语言实现Miller-Rabin算法的代码: ```c #include <stdio.h> #include <stdlib.h> #include <time.h> // 定义快速幂函数 long long fast_pow(long long a, long long b, long long p) { long long ans = 1; while (b) { if (b & 1) { ans = (ans * a) % p; } a = (a * a) % p; b >>= 1; } return ans; } // 定义Miller-Rabin素性检验函数 int is_prime(long long n, int k) { if (n < 2) { return 0; } if (n == 2 || n == 3) { return 1; } if (n % 2 == 0) { return 0; } // 将n-1分解为2^r * d的形式 long long d = n - 1; int r = 0; while (d % 2 == 0) { d /= 2; r++; } // 进行k次测试 for (int i = 0; i < k; i++) { long long a = rand() % (n - 3) + 2; // 随机选择a,2 <= a <= n-2 long long x = fast_pow(a, d, n); if (x == 1 || x == n - 1) { continue; } for (int j = 0; j < r - 1; j++) { x = (x * x) % n; if (x == n - 1) { break; } } if (x != n - 1) { return 0; } } return 1; } int main() { srand(time(NULL)); // 初始化随机数种子 long long n = 123456789; // 待判断的整数 int k = 10; // 进行10次测试 if (is_prime(n, k)) { printf("%lld is prime.\n", n); } else { printf("%lld is not prime.\n", n); } return 0; } ``` 该程序,首先定义了一个快速幂函数fast_pow,用于计算a^b mod p。然后定义了一个is_prime函数,用于判断n是否为素数。该函数接受两个参数,n为待判断的整数,k为进行测试的次数。 is_prime函数首先判断n是否小于2,是否等于2或3,是否为偶数,如果是则直接返回0或1。然后将n-1分解为2^r * d的形式,并进行k次测试。每次测试随机选择一个2 <= a <= n-2的整数,计算a^d mod n的值x,如果x等于1或n-1,则继续进行下一次测试;否则,进行r-1次平方运算,并检查是否等于n-1。如果不等于n-1,则n为合数,返回0;否则,继续进行下一次测试。如果进行了k次测试都没有得到结果,则n很可能是素数,返回1。 在main函数,首先初始化随机数种子,然后定义了一个待判断的整数n和进行测试的次数k。最后调用is_prime函数进行判断,并输出结果。 注意,由于Miller-Rabin算法是概率性算法,因此在实际应用需要进行多次测试,以提高正确性的概率。通常进行10次测试即可满足要求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值