RSA 大数的处理

1.大数储存

RSA 依赖大数运算,目前主流RSA 算法都建立在512 到1024位的大数运算之上。而大多数的编译器只能支持到64位的整数运算,即我们在运算中所使用的整数必须小于等于64位,即:0xffff, ffff,ffff.ffff,也就是18446744073709551615,这远远达不到RSA 的需要,于是需要专门建立大数运算库来解决这一问题。

最简单的办法是将大数当作数组进行处理,也就是将大数用0-9这十个数字组成的数组进行表 示,然后模拟人们手工进行“竖式计算”的过程编写其加减乘除函数。但是这样做效率很低,因为二进制为1024位的大数其十进制也有三百多位,对于任何一种 运算,都需要在两个有数百个元素的数组空间上做多重循环,还需要许多额外的空间存放计算的进退位标志及中间结果。另外,对于某些特殊的运算而言,采用二进 制会使计算过程大大简化,这种大数表示方法转化成二进制显然非常麻烦,所以在某些实例中则干脆采用了二进制数组的方法来记录大数,这样效率就更低了。

一个有效的改进方法是将大数表示为一个n 进制数组,对于目前的32位系统而言n 可以取值为2 的32次方,即0x10000,0000,假如将一个二进制为1024位的大数转化成0x10000,0000进制,它就变成了32位,而每一位的取值范围就 不是二进制的0—1或十进制的0—9,而是0-0xffff,ffff,我们正好可以用一个无符号长整数来表示这一数值。所以1024位的大数就是一个有 32个元素的unsigned long数组,针对unsigned long数组进行各种运算所需的循环规模至多32次而已。而且0x10000,0000 进制与二进制,对于计算机来说,几乎是一回事,转换非常容易。

例如大数18446744073709551615,等于 ffffffff ffffffff,就相当于十进制的99:有两位,每位都是ffffffff。而18446744073709551616 等于00000001 00000000 00000000,就相当于十进制的100:有三位,第一位是1 ,其它两位是0,如此等等。在实际应用中,“数字”数组的排列顺序采用低位在前高位在后的方式,这样,大数A 就可以方便地用数学表达式来表示其值:A=Sum[i=0 to n](A[i]*0x100000000 ^ i)(其中Sum 表示求和,A[i]表示用以记录A的数组的第i个元素,^表示乘方)。

任何整数运算最终都能分解成数字与数字之间的运算,在0x100000000 进制下其“数字”最大达到0xffffffff,其数字与数字之间的运算,结果也必然超出了目前32系统的字长。在VC++中,存在一个__int64 类型可以处理64位的整数,所以不用担心这一问题,而在其它编译系统中如果不存在64位整形,就需要采用更小的进制方式来存储大数,例如WORD类型 (16位)可以用来表示0x10000 进制,但效率更高的办法还是采用32位的DWORD 类型,只不过将0x100000000 进制改成0x40000000进制,这样两个数字进行四则运算的最大结果为 0x3fffffff* 0x3fffffff,小于0xffffffff,只是不能简单地用高位低位来将运算结果拆分成两个“数字”。

2.加法

设:

A=Sum[i=0 to p](A[i]*0x100000000^i)

B=Sum[i=0 to q](B[i]*0x100000000^i),p>=q

C=Sum[i=0 to n](C[i]*0x100000000^i)=A+B

显然:

C[i]不是简单地等于A[i]+B[i],因为如果C[i]>0xffffffff就需要进位,当然计算

C[i-1]时也可能产生了进位,所以计算C[i]时还要加上上次的进位值。

如果用carry[i]记录每次的进位则有:

C[i]=A[i]+B[i]+carry[i-1]-carry[i]*0x100000000

其中carry[-1]=0

若A[i]+B[i]+carry[i-1]>0xffffffff,则carry[i]=1;反之则carry[i]=0

若carry[p]=0,则n=p;反之则n=p+1

3.减法

设:

A=Sum[i=0 to p](A[i]*0x100000000^i)

B=Sum[i=0 to q](B[i]*0x100000000^i),p>=q

C=Sum[i=0 to n](C[i]*0x100000000^i)=A-B

显然:

C[i]不是简单地等于A[i]-B[i],因为如果A[i]

C[i-1]时也可能产生了借位,所以计算C[i]时还要减去上次的借位值。

如果用carry[i]记录每次的借位则有:

C[i]=A[i]+carry[i]*0x100000000-B[i]-carry[i-1]

其中carry[-1]=0

若A[i]>B[i]则carry[i]=0;反之则carry[i]=1

若C[p]=0,则n=p-1;反之则n=p

4.乘法

设:

A=Sum[i=0 to p](A[i]*0x100000000^i)

B=Sum[i=0 to q](B[i]*0x100000000^i),p>=q

C=Sum[i=0 to n](C[i]*0x100000000^i)=A*B

显然:

C=Sum[i=0 to q](A*B[i]*0x100000000^i)

而(A*B[i]*100000000^i)=Sum[j=0 to p](A[j]*B[i]*0x100000000^(i+j))

所以C=Sum[i=0 to q](Sum[j=0 to p](A[j]*B[i]*0x100000000^(i+j)))

因此:

C[i]=Sum[j=0 to q](A[i-j]*B[j])+carry[i-1]-carry[i]*0x100000000

其中carry[-1]=0

carry[i]=(Sum[j=0 to q](A[i-j]*B[j])+carry[i-1])/0x100000000

n=p+q-1,若carry[n]>0,则n=n+1,C[n]=carry

5.除法

设:

A=Sum[i=0 to p](A[i]*0x100000000^i)

B=Sum[i=0 to q](B[i]*0x100000000^i),p>=q

C=Sum[i=0 to n](C[i]*0x100000000^i)=A/B

由于无法将B 对A “试商”,我们只能转换成B[q]对A[p]的试商来得到一个近似值,

所以我们不能够直接计算C。但是,我们可以一步一步地逼近C

显然:

(A[p]/B[q]-1)*0x100000000^(p-q)

令:

X=0

重复:

A=A-X*B,X=X+(A[p]/B[q]-1)*0x100000000^(p-q),直到A

则有:

X=C

注意:

由于大数可理解为0x100000000进制,所以对于任意大数A*0x100000000^k

都等价于将A 的数组中的各元素左移k 位,不必计算;同样,除法则等价于右移

6.取模

设:

A=Sum[i=0 to p](A[i]*0x100000000^i)

B=Sum[i=0 to q](B[i]*0x100000000^i),p>=q

C=Sum[i=0 to n](C[i]*0x100000000^i)=A%B

求模与求商的过程一致,只是由于不需要记录商而更加简单:

重复:

A=A-(A[p]/B[q]-1)*0x100000000^(p-q)*B,直到A

则有:

A=C

7.二元一次方程

在RSA 算法中,往往要在已知A、M的情况下,求 B,使得 (A*B)%M=1。即相当于求解B、N都是未知数的二元一次不定方程 A*B-M*N=1,的最小整数解。

而针对不定方程ax-by=1 的最小整数解,古今中外都进行过详尽的研究,西方有著名的欧几里德算法,即辗转相除法,中国有秦九韶的“大衍求一术”。欧几里德算法是一种递归算法,比较容易理解:

例如:11x-49y=1,求x

(a) 11 x - 49 y = 1    49%11=5 ->

(b) 11 x -  5 y = 1    11%5 =1 ->

(c)    x -  5 y = 1

令y=0 代入(c)得x=1

令x=1 代入(b)得y=2

令y=2 代入(a)得x=9

同理可使用递归算法求得任意 ax-by=1(a、b互质)的解,实际上通过分析归纳将递归算法转换成非递归算法就变成了大衍求一术。

8.幂模运算

幂模运算是RSA 核心算法,最直接地决定了RSA 算法的性能,针对快速幂模运算这一课题,许多西方现代数学家提出了大量的解决方案。通常都是先将幂模运算化简为乘模运算。

例如求D=C^15 % N,由于:

a*b % n = (a % n)*(b % n) % n

所以:

C1=C*C % N       =C^2 % N

C2=C1*C % N      =C^3 % N

C3=C2*C2 % N     =C^6 % N

C4=C3*C % N      =C^7 % N

C5=C4*C4 % N     =C^14 % N

C6=C5*C % N      =C^15 % N

即:

对于E=15的幂模运算可分解为6个乘模运算

归纳分析以上方法可以发现对于任意E,可采用以下算法计算D=C^E % N:

D=1

WHILE E>=0

IF E为奇数

D=D*C % N

D=D*D % N

E=E-1

IF E为偶数

D=D*D % N

E=E/2

RETURN D

再加以分析会发现,要知道D 何时需乘 C,不需要反复对E 进行减一或除二的操作,只需要验证E 的二进制个位是0 还是1 就可以了,而且从左至右验证和从右至左验证都行,反而从左至右验证更简单:

若E=Sum[i=0 to n](E[i]*2^i),0<=E[i]<=1(E为二进制)

D=1

FOR i=n TO 0

D=D*D % N

IF E[i]=1

D=D*C % N

RETURN D

9.乘模运算

剩下的问题就是乘模运算了,对于A*B % N,如果A、B 都是1024位的大数,先计算A*B,再% N,就会产生2048位的中间结果,如果不采用动态内存分配技术就必须将大数定义中的数组空间增加一倍,这样会造成大量的浪费,因为在绝大多数情况下不会 用到那额外的一倍空间,而采用动态内存分配技术会使大数存储失去连续性而使运算过程中的循环操作变得非常繁琐。所以计算的首要原则就是要避免计算A*B。

由于:

A*B=A*(Sum[i=0 to n](B[i]*0x100000000^i))

所以:

A*B % N = (Sum[i=0 to n]((A*B[i])*0x100000000^i)) % N

可以用一个循环求得:

C=0;

FOR i=0 to n

C=C+A*B[i]*0x100000000 % N

RETURN C

事实上,有一种蒙哥马利算法能够更快地完成多次循环的乘模运算,但是其原理涉及较多的数论知识,且实现起来比较麻烦,对速度虽有提高,经测试也不会超过一个数量级,所以暂且不予考虑。

10.素数测试

数论学家利用费马小定理( a^(p-1)%p=1,其中p是质数,a是整数 )研究出了多种素数测试方法,目前最快的算法是拉宾米勒测试算法,测试N是素数的过程如下:

(1)计算奇数M,使得N=(2^r)*M+1

(2)选择随机数A

(3)对于任意i

(4)或者,若A^M MOD N = 1,则N通过随机数A的测试

(5)让A取不同的值对N进行5次测试,若全部通过则判定N为素数

若N 通过一次测试,则N 不是素数的概率为 25%,若N 通过t 次测试,则N 不是素数的概率为1/4^t。事实上取t 为5 时,N 不是素数的概率为 1/128,N 为素数的概率已经大于99.99%。

在实际应用中,可首先用300—500个小素数对N 进行测试,以提高拉宾米勒测试通过的概率,从而提高测试速度。而在生成随机素数时,选取的随机数最好让 r=0,则可省去步骤(3) 的测试,进一步提高测试速度。

11.输入输出

大数的输入输出是通过字符串来完成的,事实上很容易实现,例如按照十进制格式进行处理,则:

输入:

X=0

FOR i=0 TO n

X=X*10

X=X+(int)(str[n]-48)

RETURN X

输出:

str=

WHILE(X>0)

str=(char)(X%10-48)+str

RETURN str

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容

  • 姓名:李浩然 学号:16030410020 转自:http://blog.csdn.net/Dreaming_My...
    洛花无阅读 2,462评论 0 1
  • 背景 一年多以前我在知乎上答了有关LeetCode的问题, 分享了一些自己做题目的经验。 张土汪:刷leetcod...
    土汪阅读 12,658评论 0 33
  • **2014真题Directions:Read the following text. Choose the be...
    又是夜半惊坐起阅读 8,563评论 0 23
  • 师父说: 各人的命运不是佛在主宰、安排, 而是由各自的业力决定的。 业,不止有今生的, 还有过去世的。 信佛、学佛...
    瑞智阅读 923评论 10 2
  • 原生相册的不足/用户需求痛点 无法加密相片分两种,能见人的和不能见人的。但原生相册并没有加密的功能。 美化相片的功...
    大锤Tracy阅读 3,034评论 1 3