非常不幸,我的结论过于武断,在开始写代码之前,就发现了这个错误:解密出问题了!。
就象4楼和6楼两位兄弟提到的,解密时出现一对多的情况。
加密和解密是一一对应的,或者说映射的。
就这个十进制的命题来说,可理解为:输出为15位数据集,不管你输入是多少位,也只能对应15位输入数据集,而现在明文数据集是16位,完整地包含了整个与密文数据集对应的输入数据集。
一对多成为必然,这是基本常识。所以楼主的命题恐怕无法实现!
下面分享一下我失败的教训,我认为也是有意义的,可以避免以后类似愚蠢的错误。
首先意识到,不能用对称加密算法,其结果长度无法控制。
我们知道,选取适当RSA的模N可以精准地控制输出的位数。
这个思路已经隐藏着错误,只是我还没意识到,依然继续着。如果谁能在这一时刻指出我的错误,那么他对RSA就有深刻的理解。
我从15位输出的最大值15个9开始,向下作分解(factoring)。这个最大值不能当作RSA的N来用,因为它不是两个质数的乘积。也不能向上分解,输出结果位数会超过15位。
找到离最大值最近的分解结果:
[FONT="Courier"]P: 5
Q: 199999999999997
N: 999999999999985
E: 65537
D: 705506812945345[/FONT]
在P和Q出来后,随便指定一个E,再计算D。E的选择对我们的实验结果没有影响。
然后用大数计算器作简单的验证。
先测试加密,完全没有问题,所有的16位输入必定是不超过15位的输出。
再测试解密,出问题了!怎么回不去呢?!
我们来看看RSA算法的公式,非常简单,比那些复杂的对称算法更容易理解。
[FONT="Courier"]加密:
PT ^ E MOD N
解密:
CT ^ D MOD N
这里:PT - PlainText 明文;CT - CipherText 密文[/FONT]
很简单,是吧!
我忘记了最后是一个对N的取模运算!所以输出可以控制在15位之内。
下面是加密计算记录:
[FONT="Courier"]Encrypting
==========
Plaintext Ciphertext
--------- ----------
0 0
1 1
2 232688612375957
3 153875846496713
15 754708694258465 <- ***
149 451197539489329 <- ***
999999999999983 767311387624028
999999999999984 999999999999984
999999999999985 0 N*1+0
999999999999986 1 N*1+1
999999999999987 232688612375957 N*1+2
999999999999988 153875846496713
999999999999989 181304566607584
999999999999990 286328491331895
999999999999991 449231078721781
999999999999992 572836376551517
999999999999993 623394115778033
999999999999994 337601761830569
999999999999995 878876095224315
999999999999996 997814104863221
999999999999997 91685321778322
999999999999998 212292800531143
999999999999999 682231106350444
1000000000000000 754708694258465 N*1+15
1000000000000001 356296434408516
1000000000000002 348411433004192
...
1999999999999985 754708694258465 N*2+15
2000000000000119 451197539489329 N*2+149
...
2999999999999970 754708694258465 N*3+15
3999999999999955 754708694258465 N*4+15
4999999999999940 754708694258465 N*5+15
5999999999999925 754708694258465 N*6+15
6999999999999910 754708694258465 N*7+15
7999999999999895 754708694258465 N*8+15
8999999999999880 754708694258465 N*9+15
9999999999999865 754708694258465 N*10+15
...
9999999999999999 451197539489329 N*10+149[/FONT]
显然,输入在大于等于N后,输出开始重复了。对比一下数据集的关系:
[FONT="Courier"]16位明文数据集: 0 ~ 9999999999999999
子集 A: 0 ~ 999999999999984
B: 999999999999985 ~ 1999999999999969 N*1
C: 1999999999999970 ~ 2999999999999954 N*2
D: 2999999999999955 ~ 3999999999999939 N*3
E: 3999999999999940 ~ 4999999999999924 N*4
F: 4999999999999925 ~ 5999999999999909 N*5
G: 5999999999999910 ~ 6999999999999894 N*6
H: 6999999999999895 ~ 7999999999999879 N*7
I: 7999999999999880 ~ 8999999999999864 N*8
J: 8999999999999865 ~ 9999999999999849 N*9
K: 9999999999999850 ~ 9999999999999999 N*10
15位密文数据集: 0 ~ 999999999999999[/FONT]
也就是说15位内的密文可完整对应于16位输入中的A~J子集,部分对应K子集。大致是一对十的关系,凭常识也可以理解。
除非在解密时,特别指定要还原的子集A~J中的一个,否则解密不能完成。
实际上,在输入超过N后,应进行“分组加密”,即将输入分成n个N块,最后一个不足N位的块做必要的PADDING,再进行n次RSA加密;解密时就可以保证一一对应。
这种情况下十进制的密文可能将远远长于15位,大部分会是15的倍数。
十六进制时,我通常会意识到这点;十进制时怎么就糊涂了呢,惭愧惭愧。
所以,在这个命题中,无论是加密算法还是压缩算法,都会遇到一对多的问题,应该是无解。欢迎讨论、指正。
教训是,数字游戏中,没有扎实的基础知识,瞎折腾是不可取的,应设置一个“止损点”,力所能及,适可而止。
君不见,一个大数分解另无数英雄尽折腰。与诸君共勉!
PS:
php中要使用
RSA算法,将
php.ini 文件里 "
Dynamic Extensions" 节的
extension=php_gmp.dll 启用就可以了。