说明
RSA是一种非对称加密算法,通过大因数项的分解难题保证安全性,根据模运算中的乘法逆元实现加解密操作
RSA是其三位设计者的姓氏开头字母拼在一起组成的
加解密公式如下
具体原理如下,可暂时忽略
(p和q皆为大质数)
,其中 e与 φ(n) 互质
大因数项的分解难题
n = p * q
例如,已知大质数p = 26538124028047659450586055917371589682921698003775751865102346535910835998131589497193463500413434735266512874610216024479533986874253135433224331867605299614658915614896695437107589334298806047406139587620041934769208134607551632548454038450670354085540776542805823383001266596798711586326795265348145175473098858813507874452817559459255272977021821735727803639603178385577276698574005776833227201703460346441971135447933444032951422741835223700716808163615275902463672719363036341648600741941056488839121107094759961283735474561638729238692022917031368907105162593284909266293623477530786021009863450356965367333249
q = 30151505015222410727273215677649392170110554567832124182592786422975505213478878771838478982433890280824843285253365719914079774509357186005802766720587053393689490210155101343291688464049769397800081241430176994461127011931906342674759592294688474420076405026033483022343373323456452483216681454808452001121551301055145250245162392643749120111707920221826916379835491973878097559206940882389377001367445813557118805573274285169942489971084767869394632351259839399001807238975988722687935257410022401523012215241778361144452513304438310681962096792150757815224710370722250369600320928493341500817111195239260365454697
public class RSAPrimeGenerator {
public static void main(String[] args) {
SecureRandom random = new SecureRandom();
// 生成2048位的大质数p
BigInteger p = new BigInteger(2048, 100, random);
// 生成2048位的大质数q
BigInteger q;
do {
q = new BigInteger(2048, 100, random);
} while (p.equals(q));
// 计算合数n = p * q
BigInteger n = p.multiply(q);
System.out.println("生成的大质数 p: " + p);
System.out.println("生成的大质数 q: " + q);
System.out.println("计算得到的合数 n = p * q: " + n);
}
}
合数n = p * q = 621146940360983777628147764870000579788772958381583629708766431201466839446739785721730066437683097795164575833646318467098870022864866692631639159321795721538198608697032746573949278060941905502900125331976227240348729396401214575804554728607140049007185365969562736270055031236151057610108412063111601688516421142631425284202050440329696662989622343397201691935007365589487872561996489777163778066563803609051055110331459200446902346625299705557563268840845827793372662048616300237632652131386722187288681695187288922113409554564372559587230225345440408255623103982249354514527451595737480398742232277526874426789744836738755528647450959953613351754533568611292364266898428669427025014952479678323630762763034682759215245592515110801266024418984277797411646501229846483525373058522345487759115037591031483540239822681802284423148181072132701506766761084189207522661739809914192327005912013519733505582517555419923767237738694902183538117984363328642160787136564357701091302917330299543148200459888209009650561335096918259239560076116134805667644105162623741787405427618414799443667798999894408193979128407846150404705362408872214592205544971560596926116939275033900134790095597094855796118838774828078892493707283894288833453041501
在已知p和q的情况下是非常容易得到n的.
但是,在已知n的情况下分解出p * q,则几乎不可能.
目前没有高效的算法可以解决大因数项的分解难题.
2048 位的数大约有 数量级。即使是现代超级计算机,要对这样规模的数进行因数分解的暴力破解,所需的计算资源和时间也是天文数字。
模运算的乘法逆元
rsa的加密的可逆性依靠乘法逆元,这里的乘法不是四则运算的乘法 是模n的乘法(有限域的乘法)
在模运算中, 对于两个整数 a 和 m,如果存在整数x使得,则称x是a模m的乘法逆元.
即 ax = my+1(因为取模计算结果为1)
其中y是某个整数,不区分正负,也可以写成 -->
成立条件
但是并非所有的a,m都存在乘法逆元.当且仅当 a与m互质时 (,gcd表示最大公约数)
a模m的乘法逆元存在(这里我们打个问号,为什么仅当有这个情况成立)
贝祖定理证明乘法逆元
这里需要用到贝祖定理来证明,至于贝祖定理的证明,我们就不在此处考虑.(默认视为成立的,像1=1一样)
- 根据贝祖定理,对于任意两个整数a和m,存在整数x和y,使得
- 当 时, 有
- 对等式两边同时取模m运算,即
- 根据模运算的加法性质展开
- 因为my modm=0(一个数m乘以n,对自己取模,一定为0),所以 ,也就是
至此
当 a与m互为乘法逆元时(a,m互为乘法逆元时 ,a,m一定互质)
ax + my = 1
接下来为了更加丝滑的理解rsa的推导过程,我们转换参数名称
a换做 e ,m换做 φ(n), x换做 d,y换做k
则 当e与φ(n) 互质时,存在d,k使得
这个记住,后面加解密的时候要用到
欧拉函数
欧拉函数(Euler's Totient Function),记作 φ(n),是数论中一个重要的计数函数。它的核心作用是:对于正整数 n , φ ( n )表示小于或等于 n 且与 n 互质(最大公约数为 1)的正整数的个数
欧拉函数的积性性质
欧拉函数 φ(n)表示小于 n且与 n互质的正整数的个数。由于 p和 q是素数,
欧拉定理
若 a和 n互质(gcd(a,n)=1),则
安全性
依靠大因数式的分解难题保证安全性,无法被暴力破解
可还原性
RSA定义的加密解密的运算规则如下
本质是通过模n乘法逆元实现 加解密的操作操作
问: 为什么选择 n 作为模
答: 可还原性本身的计算并不复杂,安全性依赖n(难以被分解),故整个公式需要和n关联起来
问: 为什么 加解密公式 是这样的
答: 可以想象成 几个设计者 先脑补出这样的公式.可能在梦里,可能在运动的时候,突然灵光一闪,根据自己学到的数据原理想到的.想到之后再去证明是否成立.即先猜想,后验证.
问: 如何理解加解密公式
答: 明文字符串先转化为字节数组,再转化为大整数,因此需要固定编码,不同编码下会导致解密失败
问: 明文长度超过n怎么办
答: 当明文长度超过n的长度时,需要将明文分割为多个长度合规的快,对每个块单独加密,最后将密文拼接成完整的密文.解密时,再将密文快分隔并逐个机密合并并得到原始明文
数学证明
证明
想要证明公式 成立
将第一个公式代入第二个公式后则等价证明
根据模运算特性; 对于任意整数 a,k和正整数 m,模运算满足:
这是因为 a modm 结果等价 a−t⋅m 的结果(t为整数),其k次幂展开后,所有含 t⋅m的项均为 m的倍数,因此模 m后结果与 相同
即同余特性 相关证明可自行搜索 此处不再证明
则等价证明
条件
看到ed我们想到之前的乘法逆元,没错,这里我们会有一个小前提
让e和φ(n)互质,则e和 d满足,存在整数 k使得 。
则
代入则等价证明
同时不要忽略还有一个限制 m<n(明文范围限制),
场景
为了确保解密正确性的数学严谨性,并覆盖所有可能的明文场景
分为两种情况
明文 m 与 n 互质
此时可以使用 数学工具 欧拉定理
代入则
(因为其中m<n,取模后值为m)
证明成立
明文 m 与 n 不互质
因为 n =p * q (p,q皆为素数),m与n不互质情况下,则m必为p或q的倍数,否则 gcd(m,n) = 1 矛盾
同时还有一个限制 m<n(明文范围限制),
m不可能同时是 p和 q的倍数(否则 m≥p⋅q=n)。因此,m仅为 p或 q的单倍数。
根据模运算的基本性质:
(即“先模后幂”等价于“先幂后模”)。因此:
证明 等价证明
代入解密后
等价证明
情况1 m= pk 且 k < q
模p分析
m = p * k (k为整数)=> (m能被p整除)
模p加密后
模p解密后
因此 (m = p*k,k为整数)
模q分析
欧拉函数及其积性性质
由于p,q互质且m< n. 则m仅为 p或 q的单倍数。所以m和 q互质.
根据欧拉函数 若 gcd(a,n)=1,则 。对模 q,φ(q)=q−1,根据积性性质
额外条件
n=p * q ,φ(n)=(p−1)(q−1)
结合私钥 d是 e模 φ(n)的逆元,即 ed=1+kφ(n)(k为整数)。
则 ed =1+ k(p-1)(q-1)
代入
根据上述代入
则
故
情况2 m = qk 且 k<p
同上 转换 p q
中国剩余定理
综合模 p和模 q的结论得出方程组
根据中国剩余定理(CRT) :若一个数 x满足:
则 x在模 n = p * q 下有唯一解
则 $$c^d ≡m mod n = m (m
证明成立
至此完成rsa的数学证明
公钥/私钥
根据数学证明我们得到了一些前置条件
-
n = p * q (p,q皆为素数)
-
m < n
-
公钥指数e 和 φ(n)互质
-
公钥指数e与私钥指数d在φ(n)下,互为乘法逆元
RSA公钥和私钥 由 p,q,n,e,d组成
其中p,q,d保密有私钥保存
n,e公开,由公钥保存
标准的RSA私钥格式(如PKCS#1),会需要保存 p,q
但是在java中的私钥会保存p,q,但是不提供api访问获取到p,q,需要借助第三方库解析字符串
故公钥(n,e)公开
私钥(n,d)保密
e和d的确认
一般来说为了让e和φ(n)互质,e也会取一个质数 =65535,但是为什么回取这个值呢,取更大或者更小的有什么问题
主要数学性质,工程优势,历史背景方面考虑
数学性质
65537表现形式为2 ^ 2 ^ 2 ^ 2 + 1 = 2(16) +1 = 65537
费马素数(Fermat Prime)。费马素数的定义是形如 Fk=2^(2^k) +1的素数(k为非负整数)。目前已知的费马素数仅有 k=0到 k=4对应的 5 个(F0=3,F1=5,F2=17,F3=257,F4=65537),其中 65537 是最大的已知费马素数。
费马素数的特殊性质使其在模运算中表现优异
二进制形式简洁:65537 的二进制表示为 10000000000000001(仅两个 1),这使得模幂运算(如 memodn)的二进制位操作更高效。
与 φ ( n )互质的高概率: 由于 φ(n)=(p−1)(q−1),而 p和 q是大素数,φ(n)的因子通常包含大量 2 的幂次(因 p−1和 q−1多为偶数)。65537 是奇素数且不包含 2 的因子,因此 gcd(65537,φ(n))=1的概率极高(只要 φ(n)不包含 65537 作为因子,而大素数 p,q使这一概率极低)。
工程优势
选择 e=65537是工程实践中对计算效率和安全性的平衡:
模幂运算(memodn)的时间复杂度与 e的位数密切相关。65537 是一个 17 位的素数(二进制 17 位),其位数远小于现代 RSA 常用的密钥长度(如 2048 位 n对应 φ(n)约 2048 位)。较小的 e可以显著减少模幂运算的计算量,提升加密效率。
RSA 的安全性核心依赖 n=p×q的大整数分解困难性,而非 e的大小。但 e的选择需避免引入额外漏洞:
- 避免低指数攻击:若 e过小(如 e=3),攻击者可能通过收集多个密文 ci=miemodn,利用中国剩余定理或多项式插值恢复明文。65537 足够大,可有效抵御此类攻击。
- 抵抗共模攻击:若多个用户使用相同的 n但不同 e,攻击者可能通过共模攻击破解。65537 作为通用选择,降低了共模攻击的风险(因不同用户的 e相同,但 n不同)。
历史背景
早期 RSA 的标准化选择
RSA 算法由 Rivest、Shamir 和 Adleman 于 1977 年提出。在早期实现中,65537 被选为默认公钥指数,主要原因包括:
-
易于计算:65537 是小素数,且 e=65537时,d(e的模逆元)的计算相对简单(通过扩展欧几里得算法)。
-
兼容性:早期的计算机系统处理 17 位的 e比处理更大的数更高效,65537 成为硬件和软件优化的自然选择。
在已知e, φ(n),计算 d就比较简单 ed + kφ(n) = 1;
密钥对生成
import java.math.BigInteger;
import java.security.SecureRandom;
public class RSAExample {
// 生成 RSA 密钥对(公钥 (e, n),私钥 (d, n))
public static KeyPair generateKeyPair(int bitLength) {
SecureRandom random = new SecureRandom();
// 步骤 1:生成两个大素数 p 和 q(bitLength 为素数的位数,通常取 1024 或 2048)
BigInteger p = BigInteger.probablePrime(bitLength, random);
BigInteger q = BigInteger.probablePrime(bitLength, random);
// 步骤 2:计算模数 n = p * q
BigInteger n = p.multiply(q);
// 步骤 3:计算欧拉函数 φ(n) = (p-1) * (q-1)
BigInteger phiN = p.subtract(BigInteger.ONE).multiply(q.subtract(BigInteger.ONE));
// 步骤 4:选择公钥指数 e(通常取 65537,需满足 1 < e < φ(n) 且与 φ(n) 互质)
BigInteger e = BigInteger.valueOf(65537);
while (!e.gcd(phiN).equals(BigInteger.ONE)) {
e = e.add(BigInteger.ONE); // 若 e 与 φ(n) 不互质,递增 e 直到找到符合条件的值
}
// 步骤 5:计算私钥指数 d(e 的模逆元,即 e*d ≡ 1 mod φ(n))
BigInteger d = e.modInverse(phiN);
// 返回公钥 (e, n) 和私钥 (d, n)
return new KeyPair(new RSAKey(e, n), new RSAKey(d, n));
}
// RSA 加密(公钥加密)
public static BigInteger encrypt(BigInteger plaintext, RSAKey publicKey) {
// 加密公式:c = m^e mod n
return plaintext.modPow(publicKey.getExponent(), publicKey.getModulus());
}
// RSA 解密(私钥解密)
public static BigInteger decrypt(BigInteger ciphertext, RSAKey privateKey) {
// 解密公式:m = c^d mod n
return ciphertext.modPow(privateKey.getExponent(), privateKey.getModulus());
}
// 测试主函数
public static void main(String[] args) {
// 生成 1024 位的 RSA 密钥对(实际应用中建议使用 2048 位或更高)
KeyPair keyPair = generateKeyPair(1024);
RSAKey publicKey = keyPair.getPublicKey();
RSAKey privateKey = keyPair.getPrivateKey();
// 打印公钥和私钥(实际应用中私钥需严格保密)
System.out.println("公钥 (e, n):");
System.out.println("e = " + publicKey.getExponent());
System.out.println("n = " + publicKey.getModulus() + "\n");
System.out.println("私钥 (d, n):");
System.out.println("d = " + privateKey.getExponent());
System.out.println("n = " + privateKey.getModulus() + "\n");
// 明文(需小于 n,这里取一个小整数方便演示)
BigInteger plaintext = BigInteger.valueOf(123456789);
// 加密
BigInteger ciphertext = encrypt(plaintext, publicKey);
System.out.println("明文: " + plaintext);
System.out.println("密文: " + ciphertext + "\n");
// 解密
BigInteger decryptedText = decrypt(ciphertext, privateKey);
System.out.println("解密后明文: " + decryptedText);
}
// 自定义 RSA 密钥类(存储指数和模数)
static class RSAKey {
private final BigInteger exponent; // 公钥指数 e 或私钥指数 d
private final BigInteger modulus; // 模数 n
public RSAKey(BigInteger exponent, BigInteger modulus) {
this.exponent = exponent;
this.modulus = modulus;
}
public BigInteger getExponent() {
return exponent;
}
public BigInteger getModulus() {
return modulus;
}
}
// 自定义密钥对类(存储公钥和私钥)
static class KeyPair {
private final RSAKey publicKey;
private final RSAKey privateKey;
public KeyPair(RSAKey publicKey, RSAKey privateKey) {
this.publicKey = publicKey;
this.privateKey = privateKey;
}
public RSAKey getPublicKey() {
return publicKey;
}
public RSAKey getPrivateKey() {
return privateKey;
}
}
}
整体加密
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
public class RSAWholeTextExample {
// 生成 RSA 密钥对(公钥 (e, n),私钥 (d, n))
public static KeyPair generateKeyPair(int bitLength) {
SecureRandom random = new SecureRandom();
BigInteger p = BigInteger.probablePrime(bitLength, random);
BigInteger q = BigInteger.probablePrime(bitLength, random);
BigInteger n = p.multiply(q);
BigInteger phiN = p.subtract(BigInteger.ONE).multiply(q.subtract(BigInteger.ONE));
BigInteger e = BigInteger.valueOf(65537); // 常用公钥指数
while (!e.gcd(phiN).equals(BigInteger.ONE)) {
e = e.add(BigInteger.ONE);
}
BigInteger d = e.modInverse(phiN);
return new KeyPair(new RSAKey(e, n), new RSAKey(d, n));
}
// RSA 加密(整体处理明文)
public static byte[] encrypt(String plaintext, RSAKey publicKey) {
// 1. 明文转字节数组(使用 UTF-8 编码)
byte[] plaintextBytes = plaintext.getBytes(StandardCharsets.UTF_8);
// 2. 字节数组转大整数
BigInteger plaintextBigInt = new BigInteger(plaintextBytes);
// 3. 整体加密:c = m^e mod n
BigInteger ciphertextBigInt = plaintextBigInt.modPow(publicKey.getExponent(), publicKey.getModulus());
// 4. 大整数转字节数组(密文)
return ciphertextBigInt.toByteArray();
}
// RSA 解密(整体处理密文)
public static String decrypt(byte[] ciphertextBytes, RSAKey privateKey) {
// 1. 密文字节数组转大整数
BigInteger ciphertextBigInt = new BigInteger(ciphertextBytes);
// 2. 整体解密:m = c^d mod n
BigInteger plaintextBigInt = ciphertextBigInt.modPow(privateKey.getExponent(), privateKey.getModulus());
// 3. 大整数转字节数组
byte[] plaintextBytes = plaintextBigInt.toByteArray();
// 4. 字节数组转明文字符串(使用 UTF-8 编码)
return new String(plaintextBytes, StandardCharsets.UTF_8);
}
// 测试主函数
public static void main(String[] args) {
// 生成 2048 位的 RSA 密钥对(实际应用推荐 2048 位或更高)
KeyPair keyPair = generateKeyPair(2048);
RSAKey publicKey = keyPair.getPublicKey();
RSAKey privateKey = keyPair.getPrivateKey();
// 原始明文(长文本示例)
String plaintext = "Hello, RSA 整体加解密示例!这是一段需要整体处理的明文。";
System.out.println("原始明文: " + plaintext);
// 加密
byte[] ciphertextBytes = encrypt(plaintext, publicKey);
System.out.println("密文字节数组长度: " + ciphertextBytes.length);
// 解密
String decryptedText = decrypt(ciphertextBytes, privateKey);
System.out.println("解密后明文: " + decryptedText);
}
// RSA 密钥类(存储指数和模数)
static class RSAKey {
private final BigInteger exponent; // 公钥指数 e 或私钥指数 d
private final BigInteger modulus; // 模数 n
public RSAKey(BigInteger exponent, BigInteger modulus) {
this.exponent = exponent;
this.modulus = modulus;
}
public BigInteger getExponent() {
return exponent;
}
public BigInteger getModulus() {
return modulus;
}
}
// 密钥对类(存储公钥和私钥)
static class KeyPair {
private final RSAKey publicKey;
private final RSAKey privateKey;
public KeyPair(RSAKey publicKey, RSAKey privateKey) {
this.publicKey = publicKey;
this.privateKey = privateKey;
}
public RSAKey getPublicKey() {
return publicKey;
}
public RSAKey getPrivateKey() {
return privateKey;
}
}
}
分块加密
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
public class RSAChunkExample {
// 生成 RSA 密钥对(公钥 (e, n),私钥 (d, n))
public static KeyPair generateKeyPair(int bitLength) {
SecureRandom random = new SecureRandom();
BigInteger p = BigInteger.probablePrime(bitLength, random);
BigInteger q = BigInteger.probablePrime(bitLength, random);
BigInteger n = p.multiply(q);
BigInteger phiN = p.subtract(BigInteger.ONE).multiply(q.subtract(BigInteger.ONE));
BigInteger e = BigInteger.valueOf(65537); // 常用公钥指数
while (!e.gcd(phiN).equals(BigInteger.ONE)) {
e = e.add(BigInteger.ONE);
}
BigInteger d = e.modInverse(phiN);
return new KeyPair(new RSAKey(e, n), new RSAKey(d, n));
}
// RSA 分块加密(处理长明文)
public static byte[] encryptLongText(String plaintext, RSAKey publicKey, int blockSize) {
List<byte[]> ciphertextBlocks = new ArrayList<>();
byte[] plaintextBytes = plaintext.getBytes(StandardCharsets.UTF_8);
// 分块加密
for (int i = 0; i < plaintextBytes.length; i += blockSize) {
int end = Math.min(i + blockSize, plaintextBytes.length);
byte[] blockBytes = new byte[end - i];
System.arraycopy(plaintextBytes, i, blockBytes, 0, blockBytes.length);
// 转换为大整数并加密
BigInteger blockBigInt = new BigInteger(blockBytes);
BigInteger ciphertextBigInt = blockBigInt.modPow(publicKey.getExponent(), publicKey.getModulus());
// 转换为字节数组(补零到 blockSize 长度)
byte[] ciphertextBlock = ciphertextBigInt.toByteArray();
if (ciphertextBlock.length < blockSize) {
byte[] paddedBlock = new byte[blockSize];
System.arraycopy(ciphertextBlock, 0, paddedBlock, blockSize - ciphertextBlock.length, ciphertextBlock.length);
ciphertextBlocks.add(paddedBlock);
} else {
ciphertextBlocks.add(ciphertextBlock);
}
}
// 拼接所有密文块
return concatenateByteArrays(ciphertextBlocks);
}
// RSA 分块解密(处理长密文)
public static String decryptLongText(byte[] ciphertextBytes, RSAKey privateKey, int blockSize) {
List<byte[]> plaintextBlocks = new ArrayList<>();
// 分块解密
for (int i = 0; i < ciphertextBytes.length; i += blockSize) {
int end = Math.min(i + blockSize, ciphertextBytes.length);
byte[] blockBytes = new byte[end - i];
System.arraycopy(ciphertextBytes, i, blockBytes, 0, blockBytes.length);
// 转换为大整数并解密
BigInteger blockBigInt = new BigInteger(blockBytes);
BigInteger plaintextBigInt = blockBigInt.modPow(privateKey.getExponent(), privateKey.getModulus());
// 转换为字节数组(去除前导零)
byte[] plaintextBlock = plaintextBigInt.toByteArray();
if (plaintextBlock.length > blockSize) {
plaintextBlock = Arrays.copyOfRange(plaintextBlock, plaintextBlock.length - blockSize, plaintextBlock.length);
}
plaintextBlocks.add(plaintextBlock);
}
// 合并所有明文块并转换为字符串
byte[] plaintextBytes = concatenateByteArrays(plaintextBlocks);
return new String(plaintextBytes, StandardCharsets.UTF_8);
}
// 辅助方法:拼接字节数组
private static byte[] concatenateByteArrays(List<byte[]> arrays) {
int totalLength = arrays.stream().mapToInt(arr -> arr.length).sum();
byte[] result = new byte[totalLength];
int offset = 0;
for (byte[] arr : arrays) {
System.arraycopy(arr, 0, result, offset, arr.length);
offset += arr.length;
}
return result;
}
// 测试主函数
public static void main(String[] args) {
// 生成 2048 位的 RSA 密钥对
KeyPair keyPair = generateKeyPair(2048);
RSAKey publicKey = keyPair.getPublicKey();
RSAKey privateKey = keyPair.getPrivateKey();
// 原始明文(长度超过 2048 位模数允许的单块长度)
StringBuilder longText = new StringBuilder();
for (int i = 0; i < 500; i++) { // 生成约 500 字节的明文(超过 245 字节的单块限制)
longText.append("Hello, RSA 分块加密示例!这是第 ").append(i).append(" 块内容。");
}
String plaintext = longText.toString();
System.out.println("原始明文长度: " + plaintext.getBytes(StandardCharsets.UTF_8).length + " 字节");
// 单块最大长度(2048 位模数 - PKCS#1 填充 11 字节 = 245 字节)
int blockSize = 245;
// 加密
byte[] ciphertextBytes = encryptLongText(plaintext, publicKey, blockSize);
System.out.println("密文长度: " + ciphertextBytes.length + " 字节");
// 解密
String decryptedText = decryptLongText(ciphertextBytes, privateKey, blockSize);
System.out.println("解密后明文长度: " + decryptedText.getBytes(StandardCharsets.UTF_8).length + " 字节");
System.out.println("解密后明文是否与原始一致: " + plaintext.equals(decryptedText));
}
// RSA 密钥类(存储指数和模数)
static class RSAKey {
private final BigInteger exponent; // 公钥指数 e 或私钥指数 d
private final BigInteger modulus; // 模数 n
public RSAKey(BigInteger exponent, BigInteger modulus) {
this.exponent = exponent;
this.modulus = modulus;
}
public BigInteger getExponent() {
return exponent;
}
public BigInteger getModulus() {
return modulus;
}
}
// 密钥对类(存储公钥和私钥)
static class KeyPair {
private final RSAKey publicKey;
private final RSAKey privateKey;
public KeyPair(RSAKey publicKey, RSAKey privateKey) {
this.publicKey = publicKey;
this.privateKey = privateKey;
}
public RSAKey getPublicKey() {
return publicKey;
}
public RSAKey getPrivateKey() {
return privateKey;
}
}
}