背景
在读wiki中的RSA加密算法 的原理的时候,其中的n,e,d,m过于抽象,没有具体的数据,让人不好理解。
我就参照上wiki上说明的操作,用代码实现具体的操作。可能会更加具体而更容易理解。
操作
公钥与私钥的产生
假设Alice想要通过一个不可靠的媒体接收Bob的一条私人消息。她可以用以下的方式来产生一个公钥和一个私钥:
- 随意选择两个大的质数p和q,p不等于q,计算N=pq。
- 根据欧拉函数,求得 r=φ(N)=φ(p)φ(q)=(p−1)(q−1)
- 选择一个小于r的整数e,使e与r互质。并求得e关于r的模反元素,命名为d(求d令 ab≡1(modn) )。(模反元素存在,当且仅当e与r互质)
- 将p和q的记录销毁。
(N,e)是公钥,(N,d)是私钥。Alice将她的公钥(N,e)传给Bob,而将她的私钥(N,d)藏起来。
下面的java代码片段就是jdk实现的生成公钥和私钥的方法。可以把e,n,d打印出来。验证下是不是符合上面的说明。试过,e总为65537。
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(512);
KeyPair keypair = kpg.genKeyPair();
RSAPrivateKey privatekey= (RSAPrivateKey)keypair.getPrivate();
RSAPublicKey publickey= (RSAPublicKey)keypair.getPublic();
BigInteger e = publickey.getPublicExponent();
BigInteger N = publickey.getModulus();
BigInteger d = privatekey.getPrivateExponent();
BigInteger N = privatekey.getModulus();
加密消息
假设Bob想给Alice送一个消息m,他知道Alice产生的N和e。他使用起先与Alice约好的格式将m转换为一个小于N的整数n,比如他可以将每一个字转换为这个字的Unicode码,然后将这些数字连在一起组成一个数字。假如他的信息非常长的话,他可以将这个信息分为几段,然后将每一段转换为n。用下面这个公式他可以将n加密为c:
ne≡c (mod N)
计算c并不复杂。Bob算出c后就可以将它传递给Alice。
下面代码片段中的m,N,e,c与上面说明一致。m怎么到n就是分段与编码的问题,这里只是简单的用utf8编码(真实中不会这样编码,数据长了之后可能m会大于N)。因为汉字的utf8转出来,n为是一个负数,所以在前面加了>
来保证它为正。
String m = ">下午5点见";
byte[] bys = m.getBytes("UTF8");
BigInteger n =new BigInteger(bys);
BigInteger e = publickey.getPublicExponent();
BigInteger N = publickey.getModulus();
BigInteger c = n.modPow(e,N);
解密消息
Alice得到Bob的消息c后就可以利用她的密钥d来解码。她可以用以下这个公式来将c转换为n:
cd≡n (mod N)
得到n后,她可以将原来的信息m重新复原。解码的原理是
cd≡ne⋅d (mod N)以及 ed=1(modp−1)和ed=1(modq−1) 。由欧拉定理可证明(因为p和q是质数)
ne⋅d≡n (mod p)和ne⋅d≡n (mod q)这说明(因为p和q是不同的质数,所以p和q互质)
ne⋅d≡n (mod pq)
下面代码片段中的c,d,n,N与上面解释的一致。
BigInteger d = privatekey.getPrivateExponent();
BigInteger N = privatekey.getModulus();
BigInteger n=c.modPow(d,N);
String m =new String(n.toByteArray(),"UTF8");
System.out.println(m);