RSA加密算法原理与Java示例

1 为什么需要对数据加密

军事战争、支付交易等传输的数据都是非常隐私的,一旦这些信息被截获会,会造成巨大的损失,但是如果截获的信息是加密的,而又没有解密的秘钥,对于截获者来说就是一堆乱码,这就是加密最重要的意义。

2 RSA用到的数学原理

欧拉函数

设n为正整数,以 φ(n)表示不超过n且与n互质的正整数个数,称为n的欧拉函数。

欧拉函数是积性函数:如果p,q互质,则有φ(pq) = φ(p)φ(q)

如果p为质数,那么φ(p)=p-1,所以如果p,q都为大于1的质数的话,就有φ(pq)=φ(p)φ(q)=(p-1)(q-1),比如φ(10)=φ(2)φ(5)=(2-1)(5-1)=4。

欧拉定理

如果n,a为正整数,且n,a互质,则有

a^φ(n) % n = 1

例如3和10互质,则有3^φ(10) %10 = 1,即3^4%10=81%10=1。

费小马定理

如果a是不能被质数p整除的正整数,则有 a^(p-1) % p = 1 ,因为根据欧拉函数有φ(p) = p - 1。

比如4^(5-1)%5=256%5=1

模反元素

如果两个正整数a和n互质,那么一定可以找到整数b,使得 ab-1 被n整除,或者说ab被n除的余数是1,即ab % n = 1。这时,b就叫做a的“模反元素”。

比如a=4,n=5,ab%n=4*b%5=1,计算得b=4。

3 秘钥生成原理

1)选取两个不相等的质数P和Q。

P = 3

Q = 11

2)计算P和Q的乘积

N = P*Q = 3*11=33

3)计算N的欧拉函数

φ(N) = φ(PQ) = φ(P)φ(Q) = (P-1)(N-1) = (3-1)(11-1) = 20

4)选取公钥

公钥E需要满足两个条件

a)1 < E < φ(N)

b)E与φ(N)互质

为了计算方便我们取E=3

公钥就是(33,3)

5)计算私钥

套用公式

E * D % φ(N) = 1

3 * D % 20 = 1

计算得到D = 7

私钥就是(33,7)

到此,公钥和私钥就都生成了,那么如果利用这一对秘钥进行加解密呢。

4 加解密原理

加密

加密需要使用公钥(N,E),如果对M进行加密,M需要满足两个条件

1)M是整数,如果不是整数可以使用对应的ASCII值或UNICODE值

2)M必须小于N

加密就是算出下式中的C

C = M^E % N

例如取M=4

C = 4^3 % 33 =31

即可得到4加密后的值为31。

解密

解密就是计算下式中的M

M = C^D % N

M = 31^7 % 33 = 4

即得到M加密之前的值4,这就是使用RSA加解密的原理。加解密流程图如下所示:

5 安全性保证

秘钥的生成一共使用了6个数字,分别是:P、Q、N、φ(N)、E、D。这6个数字中,公钥使用了两个(N,E),这两个值是公开的,其他的都是保密的。如果D被破解,私钥也就被破解了,那么如果知道了N和E如何破解D呢。

1)E * D % φ(N) = 1,所以需要知道φ(N),才能算出D;

2)φ(N) = (P-1)(Q-1),所以需要知道P和Q才能算出φ(N) ;

3)N = P*Q,所以需要对N进行因数分解才能算出P和Q。

但是大整数的因数分解,用现在的计算机水平是非常困难的,秘钥越长,越难破解,目前被破解的最长的RSA秘钥是768个二进制位,所以支付行业使用1024位的RSA秘钥基本安全,2048位的密钥及其安全。

我们可以尝试一下对一个整数做因数分解,代码如下所示:

public class Factorization {

public static void factor(int num) {

double sqr = Math.sqrt(num);

System.out.println("因数分解结果:");

for (int i = 2; i <= sqr; i++) {

if (num % i == 0) {

System.out.print(i + " ");

num /= i;

i--;

}

}

}

}

要对1024位的整数做因数分解,需要运算2^512次,一台2.3GHz的计算机每秒钟计算次数是23亿次,和2^35是同一个数量级,一年有60*60*24*365=3千万秒,和2^29次方是同一个数量级, 那么

2^512/23亿/3千万 ≈ 2^448年

也就是说要对1024位的整数做因数分解,以现在的计算水平需要2^448年才可以。所以使用RSA加密,只要秘钥足够长,是非常安全的。

6 加解密示例

jdk已经帮我们把加解密的方法封装好了,要对数据加密,需要先生成一对秘钥,生成秘钥的方法如下所示:

public class KeyPairGenUtil {

public static int KEY_LENGTH = 1024;//密钥大小

public static String ALGORITHM_TYPE = "RSA";//算法类型

public static Map genKeyPair() throws NoSuchAlgorithmException {

Map<String, String> keyMap = new HashMap<>();//存储公钥和私钥

//为RSA算法创建KeyPairGenerator对象

KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM_TYPE);

//创建RSA算法可信任的随机数源

SecureRandom secureRandom = new SecureRandom();

//使用随机数源初始化keyPairGenerator对象

keyPairGenerator.initialize(KEY_LENGTH, secureRandom);

//生成密钥对

KeyPair keyPair = keyPairGenerator.genKeyPair();

//获取私钥

PrivateKey privateKey = keyPair.getPrivate();

//获取公钥

PublicKey publicKey = keyPair.getPublic();

//使用base64将私钥和公钥转化为字符串

String privateKeyStr = Base64.getEncoder().encodeToString(privateKey.getEncoded());

String publicKeyStr = Base64.getEncoder().encodeToString(publicKey.getEncoded());

keyMap.put("privateKey", privateKeyStr);

keyMap.put("publicKey", publicKeyStr);

return keyMap;

}

}

公钥和私钥生成之后就可以对数据进行加解密了,加解密的示例如下所示:

public class RSAUtils {

public static String encrypt(String data, String publicKey) throws Exception {

//base64编码的公钥解析为二进制

byte[] publicKeyByte = Base64.getDecoder().decode(publicKey);

//得到公钥

PublicKey pubKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKeyByte));

//加密数据

Cipher cipher = Cipher.getInstance("RSA");

cipher.init(Cipher.ENCRYPT_MODE, pubKey);

//得到加密后的数据

String encryptData = Base64.getEncoder().encodeToString(cipher.doFinal(data.getBytes()));

return encryptData;

}

public static String decrypt(String data, String privateKey) throws Exception {

//base64编码的私钥解析为二进制

byte[] privateKeyByte = Base64.getDecoder().decode(privateKey);

//base64解析后的加密数据

byte[] dataByte = Base64.getDecoder().decode(data.getBytes());

//获取私钥

PrivateKey priKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKeyByte));

//RSA解密

Cipher cipher = Cipher.getInstance("RSA");

cipher.init(Cipher.DECRYPT_MODE, priKey);

//得到解密后的数据

String decryptData = new String(cipher.doFinal(dataByte));

return decryptData;

}

}

发布了32 篇原创文章 · 获赞 38 · 访问量 5488

猜你喜欢

转载自blog.csdn.net/u010482601/article/details/97805837
今日推荐