自己实现简单的RSA秘钥生成与加解密(Java )
Posted 庹俊杰
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自己实现简单的RSA秘钥生成与加解密(Java )相关的知识,希望对你有一定的参考价值。
最近在学习PKI,顺便接触了一些加密算法。对RSA着重研究了一下,自己也写了一个简单的实现RSA算法的Demo,包括公、私钥生成,加解密的实现。虽然比较简单,但是也大概囊括了RSA加解密的核心思想与流程。这里写下来与大家分享一下。
RSA概述:
RSA是目前最有影响力的公钥加密算法,它能够抵抗到目前为止已知的绝大多数密码攻击,已被ISO推荐为公钥数据加密标准。 RSA的数学基础是大整数因子分解问题,其说明如下:
-
给定两个素数p、q,计算乘积pq=n很容易
-
给定整数n,求n的素因数p、q使得n=pq十分困难
RSA的密码体制:
设n=pq,其中p和q是大素数。设P=E=Zn,且定义K={(n,p,q,a,b):ab≡1(modΦ(n)) }其中 Φ(n)=(p-1)(q-1)。对于K={(n,p,q,a,b)} 定义加密 E(x)=xb mod n; 解密D(y)=ya mod n;(x,y∈Zn),值n,b组成了公钥,p、q和a组成了私钥
- RSA生成秘钥步骤
- 随机生成两个大素数p、q,并计算他们的乘积n
- 计算k=(p-1)(q-1)
- 生成一个小于k且与k互质的数b,一般可使用65537
- 通过扩展欧几里得算法求出a关于k的反模a。
代码如下:
首先,写出三个封装类,PrivateKey.java PublicKey.java RsaKeyPair.java (JDK中的实现高度抽象,这里我们就简单的封装一下)
1 package com.khalid.pki;
2
3 import java.math.BigInteger;
4
5 public class PublicKey {
6
7 private final BigInteger n;
8
9 private final BigInteger b;
10
11 public PublicKey(BigInteger n,BigInteger b){
12 this.n=n;
13 this.b=b;
14 }
15
16 public BigInteger getN() {
17 return n;
18 }
19
20 public BigInteger getB() {
21 return b;
22 }
23 }
1 package com.khalid.pki;
2
3 import java.math.BigInteger;
4
5 public class PrivateKey {
6
7 private final BigInteger n;
8
9 private final BigInteger a;
10
11 public PrivateKey(BigInteger n,BigInteger a){
12 this.n=n;
13 this.a=a;
14 }
15
16 public BigInteger getN() {
17 return n;
18 }
19
20 public BigInteger getA() {
21 return a;
22 }
23 }
1 package com.khalid.pki;
2
3 public class RsaKeyPair {
4
5 private final PrivateKey privateKey;
6
7 private final PublicKey publicKey;
8
9 public RsaKeyPair(PublicKey publicKey,PrivateKey privateKey){
10 this.privateKey=privateKey;
11 this.publicKey=publicKey;
12 }
13
14 public PrivateKey getPrivateKey() {
15 return privateKey;
16 }
17
18 public PublicKey getPublicKey() {
19 return publicKey;
20 }
21 }
生成大素数的方法没有自己实现,借用了BigInteger类中的probablePrime(int bitlength,SecureRandom random)方法
SecureRandom random=new SecureRandom();
random.setSeed(new Date().getTime());
BigInteger bigPrimep,bigPrimeq;
while(!(bigPrimep=BigInteger.probablePrime(bitlength,random)).isProbablePrime(1)){
continue;
}//生成大素数p
while(!(bigPrimeq=BigInteger.probablePrime(bitlength,random)).isProbablePrime(1)){
continue;
}//生成大素数q
计算k、n、b的值
1 BigInteger n=bigPrimep.multiply(bigPrimeq);//生成n
2 //生成k
3 BigInteger k=bigPrimep.subtract(BigInteger.ONE).multiply(bigPrimeq.subtract(BigInteger.ONE));
4 //生成一个比k小的b,或者使用65537
5 BigInteger b=BigInteger.probablePrime(bitlength-1, random);
核心计算a的值,扩展欧几里得算法如下
注意第二个方法 cal 其实还可以传递第三个参数,模的值,但是我们这里省略了这个参数,因为在RSA中模是1
1 private static BigInteger x; //存储临时的位置变量x,y 用于递归
2
3 private static BigInteger y;
4
5
6 //欧几里得扩展算法
7 public static BigInteger ex_gcd(BigInteger a,BigInteger b){
8 if(b.intValue()==0){
9 x=new BigInteger("1");
10 y=new BigInteger("0");
11 return a;
12 }
13 BigInteger ans=ex_gcd(b,a.mod(b));
14 BigInteger temp=x;
15 x=y;
16 y=temp.subtract(a.divide(b).multiply(y));
17 return ans;
18
19 }
20
21 //求反模
22 public static BigInteger cal(BigInteger a,BigInteger k){
23 BigInteger gcd=ex_gcd(a,k);
24 if(BigInteger.ONE.mod(gcd).intValue()!=0){
25 return new BigInteger("-1");
26 }
27 //由于我们只求乘法逆元 所以这里使用BigInteger.One,实际中如果需要更灵活可以多传递一个参数,表示模的值来代替这里
28 x=x.multiply(BigInteger.ONE.divide(gcd));
29 k=k.abs();
30 BigInteger ans=x.mod(k);
31 if(ans.compareTo(BigInteger.ZERO)<0) ans=ans.add(k);
32 return ans;
33
34 }
我们在生成中只需要
1 BigInteger a=cal(b,k);
就可以在Log级别的时间内计算出a的值
将以上代码结合包装成的 RSAGeneratorKey.java
1 package com.khalid.pki;
2
3 import java.math.BigInteger;
4 import java.security.SecureRandom;
5 import java.util.Date;
6
7 public class RSAGeneratorKey {
8
9 private static BigInteger x; //存储临时的位置变量x,y 用于递归
10
11 private static BigInteger y;
12
13
14 //欧几里得扩展算法
15 public static BigInteger ex_gcd(BigInteger a,BigInteger b){
16 if(b.intValue()==0){
17 x=new BigInteger("1");
18 y=new BigInteger("0");
19 return a;
20 }
21 BigInteger ans=ex_gcd(b,a.mod(b));
22 BigInteger temp=x;
23 x=用Java实现RSA加解密及签名和验签——.pem文件格式秘钥