以下代码使用PowerBuilder作为示例
1、数据加密概述
早在几千年前人类就已经有了通信保密的思想和方法。但直到1949年,信息论创始人香农发表著名文章,论证了一般经典加密方法得到的密文几乎都是可破译的。密码学才得以进入了一个新的发展时期。70年代后期,美国的数据加密标准DES和公开密钥密码体制的出现成为近代密码学发展史上的两个重要里程碑。
公开密钥密码体制的概念是由Difie与Hellman于1976年提出。所谓公开密钥密码体制就是加密密钥与解密密钥不同,是一种由已知加密密钥推导出解密密钥在计算上是不可行的密码体制。其中,基于数论中大数分解问题的RSA体制曾被ISO/TC97的数据加密技术委员会SC20推荐为公开密钥数据加密标准。
2、RSA体制的基本原理
该体制是根据寻求两个大素数比较简单,而将它们的乘积分解开则极其困难这一原理来设计的。在已提出的公开密钥算法中它是最容易理解和实现的。RSA在世界上许多地方已成事实上的标准。ISO几乎(但没有明确)已指定RSA用作数字签名标准。该算法已经经受住了多年深入的密码分析,虽然密码分析者既不能证明也不能否定RSA的安全性,但这恰恰说明了该算法有一定的可信度。它的安全性是与大数分解密切相关的。我想通过下表你将会对它的安全性有一个较好的认识,它给出了在计算机每一微妙做一次操作的假定下分解不同大小的N所需要的时间。
N的十进位数 50 75 100 200
时间 3.9小时 104天 74年 3.8X1015年
RSA加密算法具体如下:
(1)选取两个大素数,p和q。为了获得最大程序的安全性,两个素数的长度一样。并计算乘积N(N=pq)。
(2) 随后计算出N的欧拉函数ф(N)=(p-1)(q-1),ф(N)定义为不超过N并与N互素的数的个数。
(3)从[0,ф(N)- 1]中随机选取加密密钥e,使得e和ф(N)互为素数。
(4)计算出满足公式ed=1 modф(N)的d,d为解密密钥。
(5)若用整数X表示明文,整数Y表示密文(X,Y均小于N),则加解密运算为:
加密:Y = Xe mod N
解密:X = Yd mod N
注意,其中的d和N也互素。e和N是公开密钥,d是秘密密钥。两个素数p和q应舍弃,但千万不要泄密哦。
3、相关数学背景知识
(1)素数:素数是一个比1大,其因子只有1和它本身,没有其它数可以整除它的数。素数是无限的。例如,2,3,5,7……等。
(2)两个数互为素数:指的是它们除了1之外没有共同的因子。也可以说这两个数的最大公因子是1。例如,4和9,13和27等。
(3)模变换:两个数相模,如A模N运算,它给出了A的余数,余数是从0到N-1的某个整数,这种运算称为模运算。
4、算法的具体实现
从RSA的基本原理我们得知,对明文进行加密选择一个合适的e很重要,如果你选择合适的话,RSA的加密速度将快得多,并且也不会因为用户机器的限制而要做更多的变换(指在计算中为了避免数据的溢出所进行的转换,毕竟我们用的是PC机再说也用不着很高的安全性)。最常用的三个e值是3,17,65537。在这里我们取的e等于3,当然到底选取哪个e值并没有规定,这里只是为了演示方便罢了。
根据算法定义,
(1)为了方便起见我们选取素数p = 3和q = 11,则N = pq = 3 * 11 = 33。
(2)ф(N)=(p-1)(q-1)= 2 * 10 = 20。
(3)从[0,ф(N) - 1]中,即,[0, 19]之间任意选取加密密钥e = 3,且e和ф(N)互素。
(4)如何从公式ed=1 modф(N)求出解密密钥d?
由模的定理我们可以将公式ed=1 modф(N)转换成形式ed= k * ф(N)+ 1,即3d = k * 20 + 1,将0,1,2,3…依次代入k,求出d。取k = 1,得d = 7。
读者可以通过编程实现随机选取p和q来求出相应的N,e,d。
(5)进行加解密。
对明文进行加密
根据定义,我们首先要根据N的值对明文进行分组,每个分组的值应小于N。如果要加密固定的消息分组,那么可以在它的左边填充一些0(零)并确保该值比N小。例如,我们要对数据X=172035594进行加密(在我的计算机上C盘的序列号是0A41-0E0A,转换成十进制就是172035594),我们首先要将它分成小于N(N=33)的若干小组。可以分成,X1=17,X2=20,X3=3,X4=5,X5=5,X6=9,X7=4。对第一分组X1运用加密公式得到加密密文Y1=X1e mod N = 173 mod 33 = 29,依次将其余分组进行加密得到,Y2=14,Y3=27,Y4=26,Y5=26,Y6=3,Y7=31。即密文Y= 2914272626331。我们可以将密文存储在文件或注册表中,每当应用程序启动时先读取密文,并将其解密,再将解密后的结果与硬盘序列号进行比较,以此来判断软件是否合法。在实际运用中我们可以随时通过程序修改密文,比如,将密文去掉一位或将密文颠倒等,就可以实现诸如测试版软件的使用限制问题,
对密文进行解密
对密文进行解密同样要首先对密文进行分组,使每个分组都小于N。将密文Y=2914272626331分组成:
Y1=29,Y2=14,Y3=27,Y4=26,Y5=26,Y6=3,Y7=31
这时我们一定要注意,不要急于将将各分组代入解密公式X=Yd mod N,如果这样做了我们所得到的明文将是X=1202811913,并不是加密时的明文!是不是加密算法有错?绝对不是。回顾加解密的公式,我们不难发现它们做的都是先将一个数进行n次方运算然后在做模运算。问题就出在“n次方运算”上,千万不要忽略PowerBuilder中数值的取值范围,在其它的编程语言中也是如此。在本例中我给明文和密文用的都是unsigned long类型,它的32位所允许最大值是4294967295,的确很大,但我们不能保证一个数在进行了7次方后不超过该最大值。其实,这种情况在对明文加密时也是会发生的,只是33的3次方是35937,远小于最大值,我们将其忽略罢了。
好在问题并不像我们想象的那么复杂。由模的运算规律得知,模运算像普通的运算一样,它是可交换的、可结合的、可分配的。而且,简化运算每一个中间结果的模n运算,其作用与先进行全部运算,然后再简化模n运算是一样的。比如,
(A * B) mod N = ((A mod N) * (B mod N)) mod N。
因此,
X = Y7 mod N
= (Y3 * Y4)mod N
= ((Y3 mod N)*(Y4 mod N))mod N
当然,我们也可以将Y7分解成更多项的乘积。将分组后的密文Y1至Y7依次代入上式得出密文为X = 17 20 3 5 5 9 4。即为正确明文,解密成功。
在实际的运用中考虑到PB没有现成的乘方运算函数,为了便于读者理解原程序是如何实现RSA加密算法的本文所采用的方法是通过FOR…NEXT语句循环来实现乘方运算,读者可以将其做成一个函数,在使用的时候调用。RSA加解密算法的完整程序代码如下:
// 以下参数由RSA加密算法得来
integer li_e, li_d, li_n
li_e = 3 // 设置指数e,加密密钥
li_d = 7 // 设置指数d,解密密钥
li_n = 33 // 设置N:两个素数得乘积
string ls_str
ls_str = Trim(sle_1.text) // 将明文转换成字符串,以便随后进行分组
ulong lul_temp
lul_temp = 0
ulong lul_x, lul_y // lul_x: 加密明文; lul_y: 加密密文
int I
do until ls_str = “”
lul_temp = Integer(left(ls_str, 2))
if lul_temp >= li_n then // 将明文分组,且每组均小于N(N=33)
lul_temp = Integer(left(ls_str, 1))
ls_str = right(ls_str, len(ls_str)-1)
else
ls_str = right(ls_str, len(ls_str)-2)
end if
lul_y = 1
for I = 1 to li_e // 进行乘方运算
lul_y = lul_y * lul_temp
next
lul_y = mod( lul_y, 33) // 根据加密公式计算密文
sle_2.text = trim(sle_2.text) + string(lul_y) // sle_2.tex中存放的是加密后的密文
loop
ls_str = Trim(sle_2.text) // 与加密同理,将密文转换成字符串,以便随后进行分组
ulong lul_x0, lul_x1
do until ls_str = “”
lul_temp = Integer(left(ls_str, 2))
if lul_temp >= li_n then // 将密文分组,且每组均小于N(N=33)
lul_temp = Integer(left(ls_str, 1))
ls_str = right(ls_str, len(ls_str)-1)
else
ls_str = right(ls_str, len(ls_str)-2)
end if
// 由于考虑到乘方运算得结果可能会超出数值所允许得最大取值,
// 因此对解密公式进行适当转换,lul_x = lul_x0 * lul_x1
lul_x0 = 1
lul_x1 = 1
// 假如解密密钥是7,则先进行数的4次方运算取模,在进行数的3次方运算取模
for I = 1 to 4
lul_x0 = lul_x0 * lul_temp
next
lul_x0 = mod( lul_x0, 33)
for I = 1 to li_d – 4
lul_x1 = lul_x1 * lul_temp
next
lul_x1 = mod( lul_x1, 33)
lul_x = mod(lul_x0 * lul_x1, 33) // 根据解密公式计算明文
sle_3.text = trim(sle_3.text) + string(lul_x) // sle_3.tex中存放的是解密后的明文
loop