RSA算法的实现

RSA算法是公钥密码学中的重要部分。关于RSA算法的原理部分,阮一峰讲的很清楚,具体可以阅读他的博客:

http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html
http://www.ruanyifeng.com/blog/2013/07/rsa_algorithm_part_two.html

但是,网上很多都是原理的讲解,很难找到一个具体的实现案例。我找了一个Python的源代码,分析源代码加深理解。

代码下载的地址为:

https://pypi.python.org/pypi/rsa

打开源代码,找到key.py,这个文件中实现了RSA算法的核心部分。

找到其中的函数gen_key,源代码如下:

def gen_keys(nbits, getprime_func, accurate=True, exponent=DEFAULT_EXPONENT):

在这个函数里面实现了生成公钥和私钥的方法。

具体流程如下:

(1)生成质数p和q

函数的参数nbits也就是算法原理里面n的位数,可以是1024位或者2048位。这里以1024位为例。然后调用函数find_p_q生成p和q。

 
(p, q) = find_p_q (nbits/2, getprime_func, accurate)
这个函数生成了nbits/2=512位的质数(其实p、q不是严格为512位,在本文后半部分有解释),这样可以保证p*q为1024位。其中生成p和q的函数是:
 p = getprime_func(pbits)
 q = getprime_func(qbits)

在prime.py中可以找到getprime(nbits)函数。这个函数的具体实现是:先随机生成一个数字,然后判断是不是质数。

生成随机数的方法如下:

 integer = rsa.randnum.read_random_odd_int(nbits)

那么如何判断p和q是不是质数呢,这里用到了Miller Rabin算法,这个算法的实现在prime.py文件中。

miller_rabin_primality_testing(n, k):

关于Miller Rabin算法可以阅读相关资料。

维基百科:https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test

(2)计算e和d

调用函数calculate_keys_custom_exponent(p, q, exponent=exponent)。根据p和q的值生成e和d。其中e的默认值是65537。

(e, d) = calculate_keys_custom_exponent(p, q, exponent=exponent)

当calculate_keys_custom_exponent函数不再出现异常的时候,算法将停止,并返回p,q,e,d。

现在已经生成了p,q,n,e,d,也就是获得了公钥和私钥。下面介绍如何加密和解密。

(3)加密、解密

从原理可以看出,加密的时候需要对明文计算幂,解密时候也需要求幂。加密解密的实现在pkcs1.py中。encrypt函数实现加密,decrypt函数实现解密。

def encrypt(message, pub_key):
def decrypt(crypto, priv_key):

在加密函数encrypt中,对加密算法的直接实现是

encrypted = core.encrypt_int(payload, pub_key.e, pub_key.n)

于是,打开core.py,找到encrypt_int函数,最后一行是对求幂的实现。

return pow(message, ekey, n)

这个加密实现直接使用了pow函数,但是在一般的实现中,需要计算高次幂,直接使用pow会导致计算性能问题。

求完幂还需要进行取模的计算,这一步是在下一行代码中,在int2bytes的时候做了取模的计算,代码如下所示:

 encrypted = core.encrypt_int(payload, pub_key.e, pub_key.n)
 block = transform.int2bytes(encrypted, keylength)

计算大整数的大整数次幂是非常麻烦的,上面的求幂计算和取模计算可以合并成一步,直接使用快速求指数模算法。

在RSA实现中,还需要考虑大文件的加密问题,一般是把大文件分割成小文件,分块加密,代码中bigfile.py中有具体实现,就不赘述了。

(4)总结

由源代码可以看出,算法的实际实现比原理稍微复杂一点。在实现时候注意需要考虑以下问题:

(1)大数的存储和运算问题,int型能保存31位二进制数或者9位的十进制数,但是当1024位的时候需要重新定义大数的数据结构。在java里面有一个java.math.BigInteger封装了一个实现,而且其中也实现了大数的四则运算和模反等操作,使得基于BigInteger的RSA实现异常方便。可以通过阅读Java中的BigInteger的源代码理解其实现。BigInteger源代码阅读可以参考这篇博客:

http://www.hollischuang.com/archives/176

(2)质数的生成问题。为了保证质数相乘不能超过1024位,采取的方法是先生成两个512位的质数p和q,这样就可以保证不会溢出。其实在具体实现的时候,并不需要两个质数严格的是512位 ,可以做适当的移位,但是需要保证两个数相乘为1024位。在代码find_p_q函数中,有如下代码

 shift = nbits // 16
 pbits = nbits + shift
 qbits = nbits - shift

可以看出,pbits和qbits可以允许进行适当移位,这里shift表示移位512/16=32位,也就是pbits为544位,而qbits为480位。因为默认p>q,所以p的位数多于q的位数。这样的话pbits+qbits仍然为1024位。

(3)质数的分布问题。如果大质数比较稀疏,那么RSA算法就可以通过穷举质数的方法来攻破。但是512位大质数的分布没有想象的那么稀疏,所以能够保证安全性。

在学习理论的时候,如果有不明白的地方,可以找一份源代码阅读,帮助理解。

正如Linus Torvalds所说,Read The Fucking Source Code

  • 4
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RSA算法是一种非对称加密算法,其中C语言实现一般包括以下步骤: 1. 生成公钥和私钥 在RSA算法中,公钥和私钥是成对出现的。公钥可以公开给任何人使用,而私钥只能由拥有者保管。生成公钥和私钥的方法如下: 1.1 选择两个质数p和q,计算它们的乘积n=p*q; 1.2 计算欧拉函数φ(n)=(p-1)*(q-1); 1.3 随机选择一个整数e,满足1<e<φ(n)且e与φ(n)互质,即e和φ(n)的最大公约数为1; 1.4 计算d,满足(d*e)%φ(n)=1,即d是e关于模φ(n)的逆元。 此时,公钥为(n, e),私钥为(d, p, q)。 2. 加密数据 RSA算法中,数据的加密过程如下: 2.1 将要加密的数据m转化为整数; 2.2 计算c=m^e mod n,其中^表示乘方运算,mod表示取模运算; 2.3 加密后的数据c即为密文。 3. 解密数据 RSA算法中,数据的解密过程如下: 3.1 将密文c转化为整数; 3.2 计算m=c^d mod n; 3.3 解密后的数据m即为原始数据。 以下是一个简单的C语言实现RSA算法的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <math.h> // 求最大公约数 int gcd(int a, int b) { if (b == 0) return a; else return gcd(b, a % b); } // 求逆元 int inv(int a, int b) { int t1 = 0, t2 = 1, r1 = b, r2 = a % b, q, t; while (r2 != 0) { q = r1 / r2; t = t1 - q * t2; t1 = t2; t2 = t; r1 = r2; r2 = r1 % r2; } if (r1 > 1) return -1; if (t1 < 0) t1 += b; return t1; } // 判断是否为质数 int is_prime(int n) { int i; if (n <= 1) return 0; for (i = 2; i <= sqrt(n); i++) if (n % i == 0) return 0; return 1; } // 生成公钥和私钥 void gen_key(int *n, int *e, int *d) { int p, q, phi_n; do { p = rand() % 100 + 2; } while (!is_prime(p)); do { q = rand() % 100 + 2; } while (!is_prime(q)); *n = p * q; phi_n = (p - 1) * (q - 1); do { *e = rand() % phi_n + 2; } while (gcd(*e, phi_n) != 1); *d = inv(*e, phi_n); } // 加密 int encrypt(int m, int e, int n) { return (int)pow(m, e) % n; } // 解密 int decrypt(int c, int d, int n) { return (int)pow(c, d) % n; } int main() { int n, e, d; // 公钥和私钥 int m; // 原始数据 int c; // 密文 printf("请输入要加密的数据:"); scanf("%d", &m); gen_key(&n, &e, &d); // 生成公钥和私钥 c = encrypt(m, e, n); // 加密数据 printf("公钥:(%d,%d)\n", n, e); printf("私钥:(%d,%d)\n", d, n); printf("加密后的数据:%d\n", c); printf("解密后的数据:%d\n", decrypt(c, d, n)); // 解密数据 return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值