RSA

4 阅读23分钟

说明

RSA是一种非对称加密算法,通过大因数项的分解难题保证安全性,根据模运算中的乘法逆元实现加解密操作

RSA是其三位设计者的姓氏开头字母拼在一起组成的

加解密公式如下

密文c=(明文)memodn密文c = (明文) m^e mod n

明文m=(密文)cdmodn明文m = (密文) c^d mod n

具体原理如下,可暂时忽略

n=pqn = p * q(p和q皆为大质数)

ed+kφ(n)=1ed + k φ(n) =1 ,其中 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 位的数大约有 1061610^{616} 数量级。即使是现代超级计算机,要对这样规模的数进行因数分解的暴力破解,所需的计算资源和时间也是天文数字。

模运算的乘法逆元

rsa的加密的可逆性依靠乘法逆元,这里的乘法不是四则运算的乘法 是模n的乘法(有限域的乘法)

ax1(modm)ax≡1(modm)

在模运算中, 对于两个整数 a 和 m,如果存在整数x使得ax1(modm)ax≡1(modm),则称x是a模m的乘法逆元.

即 ax = my+1(因为取模计算结果为1)

其中y是某个整数,不区分正负,也可以写成ax+(ym)=1ax+(-ym) = 1 --> ax+my=1 ax+my=1

成立条件

但是并非所有的a,m都存在乘法逆元.当且仅当 a与m互质时 (gcd(a,m)=1gcd(a,m) =1,gcd表示最大公约数)

a模m的乘法逆元存在(这里我们打个问号,为什么仅当有这个情况成立)

贝祖定理证明乘法逆元

这里需要用到贝祖定理来证明,至于贝祖定理的证明,我们就不在此处考虑.(默认视为成立的,像1=1一样)

  • 根据贝祖定理,对于任意两个整数am,存在整数xy,使得 ax+my=gcd(a,m)ax+my=gcd(a,m)
  • gcd(a,m)=1gcd(a,m)=1时, 有 ax+my=1ax+my = 1
  • 对等式两边同时取模m运算,即(ax+my)modm1modm(ax+my)modm≡1modm
  • 根据模运算的加法性质展开(a+b)modm((amodm)+(bmodm))modm(a+b)mod m≡((a modm)+(b mod m))mod m
  • 因为my modm=0(一个数m乘以n,对自己取模,一定为0),所以 (ax)modm1modm(ax) modm≡1modm,也就是 ax1(modm)ax≡1(modm)

至此

当 a与m互为乘法逆元时(a,m互为乘法逆元时 ,a,m一定互质)

ax + my = 1

接下来为了更加丝滑的理解rsa的推导过程,我们转换参数名称

a换做 e ,m换做 φ(n), x换做 d,y换做k

则 当e与φ(n) 互质时,存在d,k使得

ed+kφ(n)=1ed + k φ(n) = 1

这个记住,后面加解密的时候要用到

欧拉函数

欧拉函数(Euler's Totient Function),记作 φ(n),是数论中一个重要的计数函数。它的核心作用是:对于正整数 n φ ( n )表示小于或等于 n 且与 n 互质(最大公约数为 1)的正整数的个数

欧拉函数的积性性质

欧拉函数 φ(n)表示小于 n且与 n互质的正整数的个数。由于 pq是素数,

φ(n)=φ(p×q)=φ(p)×φ(q)=(p1)(q1)φ(n)=φ(p×q)=φ(p)×φ(q)=(p−1)(q−1)

欧拉定理

若 a和 n互质(gcd(a,n)=1),则

aφ(n)1(modn)a^{φ(n)}≡ 1(modn)

安全性

依靠大因数式的分解难题保证安全性,无法被暴力破解

可还原性

RSA定义的加密解密的运算规则如下

密文c=(明文)memodn密文c = (明文) m^e mod n

明文m=(密文)cdmodn明文m = (密文) c^d mod n

本质是通过模n乘法逆元实现 加解密的操作操作

问: 为什么选择 n 作为模

答: 可还原性本身的计算并不复杂,安全性依赖n(难以被分解),故整个公式需要和n关联起来

问: 为什么 加解密公式 是这样的

答: 可以想象成 几个设计者 先脑补出这样的公式.可能在梦里,可能在运动的时候,突然灵光一闪,根据自己学到的数据原理想到的.想到之后再去证明是否成立.即先猜想,后验证.

问: 如何理解加解密公式

答: 明文字符串先转化为字节数组,再转化为大整数,因此需要固定编码,不同编码下会导致解密失败

问: 明文长度超过n怎么办

答: 当明文长度超过n的长度时,需要将明文分割为多个长度合规的快,对每个块单独加密,最后将密文拼接成完整的密文.解密时,再将密文快分隔并逐个机密合并并得到原始明文

数学证明

证明

想要证明公式 成立

密文c=(明文)memodn密文c = (明文) m^e mod n

明文m=(密文)cdmodn明文m = (密文) c^d mod n

将第一个公式代入第二个公式后则等价证明

m=(memodn)dmodnm = (m^e mod n)^d mod n

根据模运算特性; 对于任意整数 a,k和正整数 m,模运算满足:

(amodm)kmodm=akmodm(a mod m)^k mod m = a^k mod m

这是因为 a modm 结果等价 a−t⋅m 的结果(t为整数),其k次幂展开后,所有含 t⋅m的项均为 m的倍数,因此模 m后结果与 akmodma^k modm相同

即同余特性 相关证明可自行搜索 此处不再证明

则等价证明

mmedmodnm ≡ m^{ed} mod n

条件

看到ed我们想到之前的乘法逆元,没错,这里我们会有一个小前提

让e和φ(n)互质,则e和 d满足ed1modφ(n)e⋅d≡1modφ(n),存在整数 k使得 ed=1+kφ(n)e⋅d=1+k⋅φ(n)

med=m1+kφ(n)=mmkφ(n)m ^{ed} = m^{1+k⋅φ(n)} = m * m^{k⋅φ(n)}

代入mmedmodnm ≡ m^{ed} mod n则等价证明

mmmkφ(n)modn m ≡ m * m^{k⋅φ(n)}mod n

同时不要忽略还有一个限制 m<n(明文范围限制),

场景

为了确保解密正确性的数学严谨性,并覆盖所有可能的明文场景

分为两种情况

明文 m 与 n 互质

此时可以使用 数学工具 欧拉定理 mφ(n)1modnm ^ {φ(n)} ≡ 1 mod n

代入mmmkφ(n)modn m ≡ m * m^{k⋅φ(n)}mod n

mmmφ(n)kmodnm1kmodnmmodnmm≡m * {m^{φ(n)}}^k mod n≡ m*1^k modn≡ mmodn≡m(因为其中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的单倍数。

根据模运算的基本性质

amodnbmodn=abmodn{amodn}^b modn=a^bmodn(即“先模后幂”等价于“先幂后模”)。因此:

cdmodn=(memodn)dmodn=medmodnc^dmodn=(m^emodn)^dmodn=m^{ed}modn

证明 c=memodnc = m ^ e mod n等价证明

c=memodnc = m^e mod n

m=cdmodnm = c^d modn 代入解密后 mcdmodnmedmodnm≡ c^d modn ≡ m^{ed} mod n

等价证明

cdmmodnc^d≡ m mod n

情况1 m= pk 且 k < q
模p分析

m = p * k (k为整数)=> m0modpm ≡ 0 mod p(m能被p整除)

模p加密后

c=me0e0modp(e>=1)c = m^e ≡ 0^e ≡ 0 mod p (e >=1)

模p解密后

cd=0d0modp(d>=1)c^d = 0^d ≡ 0 mod p (d >=1)

因此 cdm(modp)c^d≡ m (modp) (m = p*k,k为整数)

模q分析
欧拉函数及其积性性质

由于p,q互质且m< n. 则m仅为 p或 q的单倍数。所以m和 q互质.

根据欧拉函数 若 gcd(a,n)=1,则 aφ(n)1(modn)a^{φ(n)}≡1(modn)。对模 q,φ(q)=q−1,根据积性性质 mq11(modq)m^{q−1}≡1(modq)

额外条件

n=p * q ,φ(n)=(p−1)(q−1)

结合私钥 d是 e模 φ(n)的逆元,即 ed=1+kφ(n)(k为整数)。

则 ed =1+ k(p-1)(q-1)

代入 cdc^d

cd=(memodn)dmodnmedmodnc^d = (m^{e} modn )^dmod n≡ m^{ed} modn

med=m1+k(p1)(q1)=m(m(p1)(q1))km ^{ed} = m ^{1+k(p-1)(q-1)} = m * (m^{(p-1)(q-1)})^k

根据上述mq11(modq)m^{q−1}≡1(modq)代入

m(p1)(q1)=(m(q1))(p1)1(modq)m^{(p-1)(q-1)} = (m^{(q-1)})^{(p -1)}≡1(modq)

a1(modq)a ≡1(modq) ak1(modq)a^ k ≡1(modq)

cdm1km(modq)c^d ≡m*1^k≡m(modq)

情况2 m = qk 且 k<p

同上 转换 p q

中国剩余定理

综合模 p和模 q的结论得出方程组

cdm(modp)c^d≡ m (modp)

cdm(modq)c^d ≡m(modq)

根据中国剩余定理(CRT) :若一个数 x满足:

xa(modp)x≡a(modp)

xb(modq)x≡b(modq)

则 x在模 n = p * q 下有唯一解

则 $$c^d ≡m mod n = m (m

证明成立

至此完成rsa的数学证明

公钥/私钥

根据数学证明我们得到了一些前置条件

  1. n = p * q (p,q皆为素数)

  2. m < n

  3. 公钥指数e 和 φ(n)互质

  4. 公钥指数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),而 pq是大素数,φ(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时,de的模逆元)的计算相对简单(通过扩展欧几里得算法)。

  • 兼容性:早期的计算机系统处理 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;
        }
    }
}