钥匙对对碰:RSA加密解密(数字版)
RSA加密的原理其实很简单,就是你有两把钥匙,一把叫公钥,一把叫私钥。这两把钥匙都有很特别的性质:用公钥加锁(加密)之后,只能用对应的私钥来解锁(解密),反过来也一样。我们来一步步看看它是怎么实现的。
1. 找两把钥匙的“材料”
要做出公钥和私钥,首先需要两个特别的数字,我们叫它们 p
和 q
。它们必须是大质数,也就是除了1和它自己,没其他能整除它的数。为啥要质数呢?因为质数特别难分解,能保证“锁”比较安全。
接着我们把 p
和 q
乘起来,得到一个新的数字,叫它 n
。这个 n
是加密和解密时的一个重要“模数”,用来限制加密结果不会太大。
然后我们用公式 φ(n) = (p - 1) * (q - 1)
算出一个特别的数,这个数用来帮助我们生成钥匙。
2. 制作公钥(锁)
公钥其实就是两个数字,e
和 n
。
e
是一个和φ(n)
互质的数,通常我们选一个常用的数,比如65537
,这样大家都能认出来。n
是刚才我们算出来的,和e
组成了公钥。
这个公钥可以用来“加锁”——也就是加密。
3. 制作私钥(解锁工具)
私钥是用来解密的。为了让它能解锁,我们得通过一个公式:(d * e) % φ(n) = 1
来算出私钥 d
。这个 d
是啥意思呢?它就是 e
的“模逆元”,意思就是用这个数和 e
一起配合,能解开锁住的东西。
4. 加密:公钥加锁
我们用公钥加密。假设你有个数字 m
(比如一条消息的数字表示),想加密它。用下面这个公式:
c = (m^e) % n
这就把 m
通过 e
和 n
转换成了密文 c
。这样别人只要拿到 c
,他们也不知道原本的 m
是啥,因为没有私钥解锁。
5. 解密:私钥解锁
如果你有私钥 d
,你就可以用下面的公式:
m = (c^d) % n
把密文 c
重新解成明文 m
。这个公式其实就是反过来用公钥加密的步骤,解出来的就是原始的消息。
代码
import java.math.BigInteger;
import java.util.Random;
public class KeyPairMatchRSA {
// n是锁和钥匙的共同基础, e是公钥(锁),d是私钥(解锁器)
private BigInteger n;
private BigInteger e; // 公钥
private BigInteger d; // 私钥
// 构造函数:生成钥匙对(公钥和私钥)
public KeyPairMatchRSA(int bitLength) {
// 1. 找到两把钥匙的材料:两个大质数 p 和 q
BigInteger p = BigInteger.probablePrime(bitLength, new Random());
BigInteger q = BigInteger.probablePrime(bitLength, new Random());
// 2. 把 p 和 q 乘起来得到 n
n = p.multiply(q);
// 3. 计算 φ(n),用来生成钥匙
BigInteger phi = (p.subtract(BigInteger.ONE)).multiply(q.subtract(BigInteger.ONE));
// 4. 制作公钥:e 和 n
e = new BigInteger("65537"); // 通常选用 65537,大家都认得
// 5. 制作私钥:d,通过公式求逆元
d = e.modInverse(phi);
}
// 加密方法,用公钥 e 加密(加锁)
public BigInteger encrypt(BigInteger message) {
// 密文 c = (m^e) % n
return message.modPow(e, n);
}
// 解密方法,用私钥 d 解密(解锁)
public BigInteger decrypt(BigInteger cipherText) {
// 明文 m = (c^d) % n
return cipherText.modPow(d, n);
}
// 主函数:模拟加密和解密过程
public static void main(String[] args) {
// 生成钥匙对,bitLength 是密钥长度,越大越安全,512是个基础长度
KeyPairMatchRSA rsa = new KeyPairMatchRSA(512);
// 需要加密的消息,比如 "123456789",我们用大数来表示这个消息
BigInteger message = new BigInteger("123456789");
System.out.println("原始消息: " + message);
// 使用公钥加密(加锁)
BigInteger cipherText = rsa.encrypt(message);
System.out.println("加密后的密文: " + cipherText);
// 使用私钥解密(解锁)
BigInteger decryptedMessage = rsa.decrypt(cipherText);
System.out.println("解密后的明文: " + decryptedMessage);
}
}
代码讲解:
n
是用两个质数p
和q
生成的,它是加密和解密的基础。e
是公钥的一部分,代表“加锁”的工具。我们这里固定用常见的 65537。d
是通过公式(d * e) % φ(n) = 1
计算出的私钥,它是用来“解锁”的工具。
步骤:
- 先生成公钥和私钥(钥匙对)。
- 然后,我们用
encrypt()
函数,利用公钥e
和n
来加密消息。 - 最后,用
decrypt()
函数,利用私钥d
把加密后的密文还原成明文。
总结:
- 公钥 是你可以随便告诉别人用来加密的“锁”,它负责把信息藏起来。
- 私钥 是你自己保管的“解锁器”,它负责把加密的信息解开。
- 别人用公钥加锁,你才能用私钥解锁。相当于你家大门,门锁给了所有人,但钥匙只有你自己有!
这样,你的通信就安全了,因为即使有人偷看了密文,没有私钥也是没法解开的!
这个 “钥匙对对碰” 的加密解密过程,核心就是通过数学公式保证只有公钥和私钥能配对工作,确保信息的安全性。
“钥匙对对碰”-RSA加密解密(字符串版)
在之前的“钥匙对对碰”里,我们是加密和解密数字的。现在,我们想对字符串(比如“linchuan”)进行加密和解密。为了让它好理解,咱们来一步一步拆开说,看看这背后的原理。
1. 字符串和数字的转换
问题来了:RSA这种数学加密方式只能处理数字,那我们想要加密的“linchuan”是个字符串怎么办?
答案是:把字符串转换成数字!
具体怎么做呢?先把字符串转换成字节数组(也就是一串数字),再把这个字节数组变成一个巨大的整数(BigInteger
)。这样我们就能用RSA的数学魔法来对这个数字进行加密了!
2. 公钥加锁:加密字符串
公钥是两部分组成:e
和 n
。公钥可以公开,让所有人加密消息,但只有你能解密。我们拿到转换成 BigInteger
的字符串(叫它 messageInt
),然后用RSA的公式来加密:
加密公式:
cipherText = (messageInt^e) % n
这个公式的意思是,拿 messageInt
乘以它自己 e
次,然后对 n
取余数,最后得到一个加密后的数字 cipherText
。为了让这个加密后的东西能看懂,我们把它转换回字符串,方便传递和存储。
3. 私钥解锁:解密字符串
加密后的密文想要变回原来的明文,必须得用私钥 d
进行解锁。解密的时候,RSA有个特别的公式:
解密公式:
decryptedInt = (cipherText^d) % n
它的意思是,把加密后的密文 cipherText
乘以它自己 d
次,然后再对 n
取余数,这样就得到了原来的数字 messageInt
。再把这个数字转回字节数组,再转换回字符串,就得到了我们想要的原始消息。
代码
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.Random;
public class StringRSA {
// n 是锁和钥匙的共同基础, e 是公钥(加锁),d 是私钥(解锁)
private BigInteger n;
private BigInteger e; // 公钥
private BigInteger d; // 私钥
// 构造函数:生成密钥对(公钥和私钥)
public StringRSA(int bitLength) {
// 1. 找到两个大质数 p 和 q,作为钥匙的材料
BigInteger p = BigInteger.probablePrime(bitLength, new Random());
BigInteger q = BigInteger.probablePrime(bitLength, new Random());
// 2. 计算 n = p * q,这个 n 是加密和解密都要用的
n = p.multiply(q);
// 3. 计算 φ(n),这个是 p-1 和 q-1 的乘积,用来帮助生成私钥
BigInteger phi = (p.subtract(BigInteger.ONE)).multiply(q.subtract(BigInteger.ONE));
// 4. 设置一个常见的公钥 e,通常我们选 65537(大家常用的数字)
e = new BigInteger("65537");
// 5. 通过公式生成私钥 d:d 是 e 的“模逆元”
d = e.modInverse(phi);
}
// 加密方法:把字符串通过公钥 e 加密成密文
public String encrypt(String message) {
// 1. 把字符串转成字节数组,再转成一个大整数
BigInteger messageInt = new BigInteger(message.getBytes(StandardCharsets.UTF_8));
// 2. 用公钥 e 和 n 进行加密运算,生成密文
BigInteger cipherText = messageInt.modPow(e, n);
// 3. 把加密后的大整数转成字符串,返回给调用者
return cipherText.toString();
}
// 解密方法:把加密后的字符串用私钥 d 解密回原始字符串
public String decrypt(String cipherText) {
// 1. 把密文字符串转回大整数
BigInteger cipherInt = new BigInteger(cipherText);
// 2. 用私钥 d 和 n 进行解密运算,得到原始的数字
BigInteger decryptedInt = cipherInt.modPow(d, n);
// 3. 把解密后的大整数转回字节数组,再转成字符串返回
return new String(decryptedInt.toByteArray(), StandardCharsets.UTF_8);
}
// 主函数:演示加密和解密过程
public static void main(String[] args) {
// 生成密钥对,位长度为512位(越大越安全)
StringRSA rsa = new StringRSA(512);
// 需要加密的消息
String message = "linchuan";
System.out.println("原始消息: " + message);
// 加密消息
String cipherText = rsa.encrypt(message);
System.out.println("加密后的密文: " + cipherText);
// 解密密文
String decryptedMessage = rsa.decrypt(cipherText);
System.out.println("解密后的明文: " + decryptedMessage);
}
}
代码解释:
-
生成密钥对:程序首先生成了一对公钥(e, n)和私钥(d, n)。公钥用来加密,私钥用来解密。
e = 65537
是个常用的数字,生成私钥d
是通过数学公式计算出来的。n
是 p 和 q 两个质数的乘积,p 和 q 是加密和解密过程中非常重要的部分。
-
加密字符串:
- 先把需要加密的字符串(比如
"linchuan"
)转换成字节数组,再转换为BigInteger
大整数。 - 用公钥
e
和n
来对这个大整数进行加密,得到密文,最后将密文转换成字符串返回。
- 先把需要加密的字符串(比如
-
解密密文:
- 将加密后的字符串密文转回
BigInteger
大整数。 - 使用私钥
d
和n
来进行解密,得到原始的大整数。 - 最后把大整数转回字节数组,再转回字符串,得出原始的明文。
- 将加密后的字符串密文转回
总结:
- 字符串 → 数字:把我们想加密的字符串“linchuan”转换成数字(
BigInteger
)。 - 加密:用公钥把这个数字“加锁”,加密得到一个加密后的数字密文。
- 解密:用私钥“解锁”,把加密后的密文再还原成原来的数字。
- 数字 → 字符串:再把解密后的数字转回原来的字符串,解密完成!
通过这个过程,RSA就能让字符串也能安全地加密和解密。
标签:BigInteger,公钥,加密,对对碰,RSA,解密,字符串,私钥,JAVA From: https://blog.csdn.net/m0_63141213/article/details/143030482