Java实现RSA加密解密:从数学原理到工程实践 1. 项目概述为什么RSA依然是现代通信的基石在数字世界里数据安全就像给信息上了一把锁。而RSA算法无疑是这把锁中最经典、应用最广泛的锁芯之一。从你每天登录网站时传输的密码到手机支付时验证的身份信息背后都可能有着RSA默默守护的身影。它之所以历经数十年考验依然屹立不倒核心在于其基于“大数分解难题”的非对称加密思想——公钥加密私钥解密二者分离从根本上解决了密钥分发这个老大难问题。作为一个有十多年经验的开发者我见过太多因为对加密理解不透彻而引发的安全漏洞。比如直接把私钥硬编码在客户端代码里或者错误地使用RSA去加密大量数据导致性能崩溃。这个项目就是带你用Java亲手实现一遍RSA的加密与解密全过程。目的不是让你去造一个比BouncyCastle更牛的轮子而是通过“造轮子”的过程彻底吃透RSA的原理、标准流程、以及那些在真实开发中容易踩的坑。理解了这些无论是使用Hutool这样的工具库还是排查“RSA签名遭遇异常请检查私钥格式是否正确”这类错误你都能游刃有余。2. 核心原理与数学基础拆解2.1 非对称加密的核心思想从“共享钥匙”到“信箱投递”在理解RSA之前我们先聊聊对称加密比如AES。它就像你和朋友共用一把钥匙和一把锁你们俩都得有这把钥匙才能开锁和上锁。安全传递这把“钥匙”本身就成了一个先有鸡还是先有蛋的安全难题。RSA代表的非对称加密完美地解决了这个问题。它采用了一对密钥公钥和私钥。公钥可以完全公开就像你家门口的信箱投递口任何人都可以往里塞信加密数据而私钥必须严格保密就像只有你拥有的信箱钥匙只有你能打开信箱取出信件解密数据。这个“单向”特性是构建HTTPS、数字签名、SSH免密登录等现代安全协议的基石。2.2 RSA的数学引擎欧拉定理与大数分解RSA的安全性建立在一个公认的数学难题上将一个大整数分解为两个质因数的乘积是极其困难的。整个算法围绕几个核心步骤展开密钥生成选择两个大质数p和q这是所有安全性的起点。p和q必须足够大如今至少1024位推荐2048位或更长并且随机。计算模数nn p * q。n的长度就是密钥长度如2048位。n会出现在公钥和私钥中是公开的。计算欧拉函数φ(n)φ(n) (p-1) * (q-1)。这个值必须绝对保密因为它直接关系到私钥。选择公钥指数e选择一个整数e满足1 e φ(n)且e与φ(n)互质最大公约数为1。通常使用固定值65537(0x10001)因为它二进制表示中1很少计算效率高且安全性经过充分验证。计算私钥指数d计算e对于φ(n)的模反元素d。即满足(d * e) % φ(n) 1。d就是私钥的核心部分需要严格保密。至此我们得到了公钥由(n, e)组成。私钥由(n, d)组成实际上私钥通常还包含p,q,dmp1,dmq1,iqmp等用于中国剩余定理加速运算的中间值但(n, d)是最简形式。加密与解密过程加密用公钥对于明文消息m需要先转换为一个小于n的整数计算密文c m^e mod n。解密用私钥对于密文c计算明文m c^d mod n。这里的魔法在于由于d是e关于φ(n)的模反元素根据欧拉定理上述运算能保证(m^e)^d mod n m。注意上述过程描述的是教科书式RSA。在实际应用中直接对原始数据进行这种运算是不安全的必须结合PKCS#1 v1.5或OAEP等填充方案以防止多种密码学攻击。我们会在实现部分详细展开。2.3 RSA的能力边界与常见误区很多新手会误以为RSA可以加密任意长度的数据。这是一个危险的误区。RSA算法本身一次能加密的数据块大小取决于密钥长度和使用的填充方案。对于一个2048位的密钥n是2048位使用PKCS#1 v1.5填充时能加密的明文最大长度仅为256字节 - 11字节 245字节。这是因为填充本身需要占用空间。因此RSA的典型应用场景是加密对称密钥例如用RSA加密一个随机的AES会话密钥然后用这个AES密钥去加密实际的大量数据。这是HTTPS等协议的标准做法。数字签名用私钥对数据的哈希值如SHA256进行加密即签名对方用公钥解密并验证哈希值。这就是“验签”的作用用于验证数据完整性和来源真实性。加密少量关键数据如用户密码、银行卡号等短数据。如果你试图用RSA直接加密一个几MB的文件程序要么会抛出异常要么你需要自己手动分块但这通常不是最佳实践。3. Java实现核心从KeyPairGenerator到完整加解密理解了原理我们开始动手。Java标准库java.security提供了完善的支撑。我们不使用任何第三方库如BouncyCastle、Hutool仅用JDK原生API实现这能让你看清本质。3.1 密钥对的生成与保存密钥生成是第一步也是决定安全性的关键。import java.security.*; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; public class RSAKeyGenerator { /** * 生成RSA密钥对 * param keySize 密钥长度推荐2048或以上 * return 生成的密钥对 */ public static KeyPair generateKeyPair(int keySize) throws NoSuchAlgorithmException { // 1. 获取RSA密钥对生成器实例 KeyPairGenerator keyPairGen KeyPairGenerator.getInstance(RSA); // 2. 初始化生成器指定密钥长度和随机源 keyPairGen.initialize(keySize, new SecureRandom()); // 使用强随机数 // 3. 生成密钥对 return keyPairGen.generateKeyPair(); } /** * 将公钥/私钥对象转换为Base64编码的字符串便于存储和传输 */ public static String keyToString(Key key) { return Base64.getEncoder().encodeToString(key.getEncoded()); } /** * 从Base64字符串还原公钥对象 */ public static PublicKey getPublicKeyFromString(String publicKeyStr) throws Exception { byte[] keyBytes Base64.getDecoder().decode(publicKeyStr); X509EncodedKeySpec keySpec new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory KeyFactory.getInstance(RSA); return keyFactory.generatePublic(keySpec); } /** * 从Base64字符串还原私钥对象 */ public static PrivateKey getPrivateKeyFromString(String privateKeyStr) throws Exception { byte[] keyBytes Base64.getDecoder().decode(privateKeyStr); PKCS8EncodedKeySpec keySpec new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory KeyFactory.getInstance(RSA); return keyFactory.generatePrivate(keySpec); } public static void main(String[] args) throws Exception { // 生成一个2048位的密钥对 KeyPair keyPair generateKeyPair(2048); PublicKey publicKey keyPair.getPublic(); PrivateKey privateKey keyPair.getPrivate(); System.out.println( 公钥 (Base64) ); String pubKeyStr keyToString(publicKey); System.out.println(pubKeyStr); System.out.println(\n 私钥 (Base64) ); String priKeyStr keyToString(privateKey); System.out.println(priKeyStr); // 测试还原 PublicKey restoredPubKey getPublicKeyFromString(pubKeyStr); PrivateKey restoredPriKey getPrivateKeyFromString(priKeyStr); System.out.println(\n密钥还原验证成功: publicKey.equals(restoredPubKey)); } }实操要点与避坑指南密钥长度keySize参数至关重要。1024位已被认为不安全生产环境至少使用2048位对长期安全要求高的应用应考虑3072或4096位。长度翻倍安全性呈指数级增长但加解密性能也会下降。随机数源SecureRandom是密码学安全的随机数生成器。切勿使用java.util.Random它的可预测性会彻底摧毁密钥的安全性。密钥格式PublicKey.getEncoded()默认输出的是X.509格式的编码PrivateKey.getEncoded()默认输出的是PKCS#8格式的编码。我们使用Base64将其转换为字符串便于存储在配置文件、数据库或前端传递。X509EncodedKeySpec和PKCS8EncodedKeySpec就是用来解析这两种标准格式的。私钥保管私钥字符串是最高机密绝不能提交到代码仓库、记录在日志或通过不安全的通道传输。在生产环境中应考虑使用硬件安全模块HSM或云服务商的密钥管理服务KMS。3.2 实现安全的加密与解密使用OAEP填充如前所述教科书式RSANoPadding是不安全的。我们必须使用填充方案。PKCS#1 v1.5是历史悠久的填充方式但已知在某些场景下存在潜在风险。目前更推荐使用**OAEPOptimal Asymmetric Encryption Padding**填充它安全性更强。import javax.crypto.Cipher; import java.security.*; public class RSAOAEPCrypto { private static final String TRANSFORMATION RSA/ECB/OAEPWithSHA-256AndMGF1Padding; /** * 使用公钥加密数据 * param publicKey 公钥 * param data 待加密的原始数据字节数组 * return 加密后的字节数组 */ public static byte[] encrypt(PublicKey publicKey, byte[] data) throws Exception { // 1. 获取Cipher实例并指定算法/模式/填充 Cipher cipher Cipher.getInstance(TRANSFORMATION); // 2. 初始化为加密模式传入公钥 cipher.init(Cipher.ENCRYPT_MODE, publicKey); // 3. 执行加密操作 return cipher.doFinal(data); } /** * 使用私钥解密数据 * param privateKey 私钥 * param encryptedData 加密后的数据字节数组 * return 解密后的原始数据字节数组 */ public static byte[] decrypt(PrivateKey privateKey, byte[] encryptedData) throws Exception { Cipher cipher Cipher.getInstance(TRANSFORMATION); cipher.init(Cipher.DECRYPT_MODE, privateKey); return cipher.doFinal(encryptedData); } /** * 完整的加密解密流程示例 */ public static void main(String[] args) throws Exception { String originalText 这是一段需要加密的敏感信息比如一个AES密钥a7f8d3e9c1b5a2f4; System.out.println(原始文本: originalText); System.out.println(原始文本长度(字节): originalText.getBytes().length); // 生成密钥对 KeyPair keyPair RSAKeyGenerator.generateKeyPair(2048); PublicKey publicKey keyPair.getPublic(); PrivateKey privateKey keyPair.getPrivate(); // 加密 byte[] encryptedBytes encrypt(publicKey, originalText.getBytes()); String encryptedBase64 Base64.getEncoder().encodeToString(encryptedBytes); System.out.println(\n加密后(Base64): encryptedBase64); // 解密 byte[] decryptedBytes decrypt(privateKey, encryptedBytes); String decryptedText new String(decryptedBytes); System.out.println(解密后文本: decryptedText); // 验证 System.out.println(解密是否成功: originalText.equals(decryptedText)); } }关键解析与注意事项TRANSFORMATION字符串“RSA/ECB/OAEPWithSHA-256AndMGF1Padding”是核心。RSA算法。ECB加密模式。对于非对称加密的块加密算法由于每次加密的数据块很大一个密钥长度ECB模式在这里是可以接受的且是标准写法。这与对称加密如AES中不推荐使用ECB模式是两回事。OAEPWithSHA-256AndMGF1Padding指定使用OAEP填充方案其中哈希函数使用SHA-256掩码生成函数使用MGF1。你也可以使用OAEPWithSHA-1AndMGF1Padding但SHA-256更安全。数据长度限制运行上面的main方法你会看到原始文本长度是xx字节。对于一个2048位256字节的密钥使用OAEP填充能加密的最大明文长度约为256 - 2*哈希输出长度 - 2。对于SHA-25632字节大约能加密256 - 2*32 - 2 ≈ 190字节。如果你的数据超长cipher.doFinal()会直接抛出IllegalBlockSizeException。这就是为什么RSA通常只用于加密密钥或短数据。Cipher对象的线程安全Cipher对象不是线程安全的。每次加密或解密操作最好创建新的Cipher实例或者进行同步处理。但在高并发场景下创建实例的开销可以接受且能避免并发问题。3.3 处理超长数据与对称加密如AES结合这是RSA最经典的用法。流程如下发送方随机生成一个用于AES加密的密钥例如一个128位或256位的随机字节数组。发送方用接收方的RSA公钥加密这个AES密钥。发送方用这个AES密钥以合适的模式如AES/GCM/PKCS5Padding加密实际的大段明文数据。发送方将加密后的AES密钥RSA加密结果和加密后的数据AES加密结果一起发送给接收方。接收方用自己的RSA私钥解密出AES密钥。接收方用解密出的AES密钥解密出原始数据。import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.GCMParameterSpec; import java.security.SecureRandom; public class HybridEncryptionDemo { public static void main(String[] args) throws Exception { // 模拟要传输的大量数据 String bigData 这里是非常长的业务数据......可能是一份JSON、XML或者整个文件的内容。; // **1. 接收方生成RSA密钥对** KeyPair rsaKeyPair RSAKeyGenerator.generateKeyPair(2048); PublicKey rsaPublicKey rsaKeyPair.getPublic(); PrivateKey rsaPrivateKey rsaKeyPair.getPrivate(); // **2. 发送方准备阶段** // 2.1 随机生成一个AES会话密钥 KeyGenerator aesKeyGen KeyGenerator.getInstance(AES); aesKeyGen.init(256); // 使用AES-256 SecretKey aesSessionKey aesKeyGen.generateKey(); // 2.2 用接收方的RSA公钥加密AES密钥 byte[] encryptedAesKey RSAOAEPCrypto.encrypt(rsaPublicKey, aesSessionKey.getEncoded()); // 2.3 使用AES密钥加密实际数据 (以GCM模式为例提供认证加密) Cipher aesCipher Cipher.getInstance(AES/GCM/NoPadding); byte[] iv new byte[12]; // GCM推荐12字节的IV new SecureRandom().nextBytes(iv); GCMParameterSpec gcmSpec new GCMParameterSpec(128, iv); // 128位认证标签 aesCipher.init(Cipher.ENCRYPT_MODE, aesSessionKey, gcmSpec); byte[] encryptedData aesCipher.doFinal(bigData.getBytes()); byte[] authenticationTag aesCipher.getIV(); // 在GCM中认证信息已包含在输出中 System.out.println(发送方完成); System.out.println( - AES密钥长度: aesSessionKey.getEncoded().length * 8 bits); System.out.println( - 加密后的AES密钥长度: encryptedAesKey.length bytes); System.out.println( - 加密后的数据长度: encryptedData.length bytes); // **3. 接收方解密阶段** // 3.1 用自己的RSA私钥解密出AES密钥 byte[] decryptedAesKeyBytes RSAOAEPCrypto.decrypt(rsaPrivateKey, encryptedAesKey); // 将字节数组还原为SecretKey对象 (这里需要根据字节重建示例简化) // 实际中可能需要使用 SecretKeySpec // SecretKey restoredAesKey new SecretKeySpec(decryptedAesKeyBytes, AES); // 3.2 用解密出的AES密钥解密数据 // 此处为逻辑示意重建AES Cipher进行解密 Cipher aesDecryptCipher Cipher.getInstance(AES/GCM/NoPadding); GCMParameterSpec gcmSpecDec new GCMParameterSpec(128, iv); // 必须使用相同的IV // 假设restoredAesKey已正确重建 // aesDecryptCipher.init(Cipher.DECRYPT_MODE, restoredAesKey, gcmSpecDec); // byte[] decryptedBigData aesDecryptCipher.doFinal(encryptedData); System.out.println(\n接收方已收到加密的AES密钥和数据包可用私钥解密AES密钥进而解密数据。); System.out.println(** 混合加密完成 **); } }这种“RSAAES”的混合模式结合了非对称加密便于密钥分发的优点和对称加密处理大数据速度快、结果长度可控的优点是业界标准做法。4. 数字签名与验签的实现数字签名是RSA另一个核心用途用于验证数据的完整性和来源。过程与加密相反用私钥签名用公钥验签。import java.security.*; public class RSASignatureDemo { private static final String SIGN_ALGORITHM SHA256withRSA; /** * 使用私钥对数据进行签名 * param privateKey 私钥 * param data 原始数据 * return 数字签名字节数组 */ public static byte[] sign(PrivateKey privateKey, byte[] data) throws Exception { // 1. 获取Signature实例指定算法 Signature signature Signature.getInstance(SIGN_ALGORITHM); // 2. 初始化为签名模式传入私钥 signature.initSign(privateKey); // 3. 载入待签名的数据 signature.update(data); // 4. 执行签名 return signature.sign(); } /** * 使用公钥验证签名 * param publicKey 公钥 * param data 原始数据 * param sign 待验证的签名 * return 验证是否通过 */ public static boolean verify(PublicKey publicKey, byte[] data, byte[] sign) throws Exception { Signature signature Signature.getInstance(SIGN_ALGORITHM); signature.initVerify(publicKey); signature.update(data); return signature.verify(sign); } public static void main(String[] args) throws Exception { String importantMessage 订单号202310270001支付金额299.99元; System.out.println(原始消息: importantMessage); KeyPair keyPair RSAKeyGenerator.generateKeyPair(2048); PrivateKey privateKey keyPair.getPrivate(); // 签名方持有 PublicKey publicKey keyPair.getPublic(); // 验证方持有 // 发送方签名 byte[] signatureBytes sign(privateKey, importantMessage.getBytes()); String signatureBase64 Base64.getEncoder().encodeToString(signatureBytes); System.out.println(\n生成签名(Base64): signatureBase64); // 接收方验证正常情况 boolean isValid verify(publicKey, importantMessage.getBytes(), signatureBytes); System.out.println(签名验证结果(正常): isValid); // 模拟数据被篡改 String tamperedMessage 订单号202310270001支付金额999.99元; // 金额被改 boolean isValidAfterTamper verify(publicKey, tamperedMessage.getBytes(), signatureBytes); System.out.println(签名验证结果(数据被篡改后): isValidAfterTamper); // 模拟签名被破坏 signatureBytes[10] ^ 0xFF; // 随意修改一个签名字节 boolean isValidAfterSignBroken verify(publicKey, importantMessage.getBytes(), signatureBytes); System.out.println(签名验证结果(签名被破坏后): isValidAfterSignBroken); } }签名算法解析“SHA256withRSA”表示先使用SHA-256算法计算数据的哈希值摘要然后对这个固定长度的哈希值用RSA私钥进行加密得到的结果就是签名。验签时用公钥解密签名得到哈希值A再重新计算数据的哈希值B比较A和B是否一致。一致则证明数据未被篡改且来自持有对应私钥的一方。5. 实战问题排查与性能调优5.1 常见异常与解决方案速查表在实际开发中你几乎一定会遇到以下异常。理解其根源是解决问题的关键。异常信息可能原因解决方案javax.crypto.IllegalBlockSizeException: Data must not be longer than XXX bytes尝试用RSA加密的数据长度超过了当前密钥和填充方案的限制。1.检查数据长度确保明文或待加密的密钥长度符合要求。对于长数据必须采用“RSA加密AES密钥AES加密数据”的混合模式。2.确认填充方案不同填充方案占用字节数不同。使用Cipher.getInstance(“RSA/ECB/OAEPWithSHA-256AndMGF1Padding”)明确指定。java.security.spec.InvalidKeySpecException尝试从字节数组或字符串还原密钥时提供的密钥数据格式不正确或已损坏。1.检查密钥格式公钥是否是X.509格式私钥是否是PKCS#8格式使用key.getEncoded()得到的字节数组通常是对的。2.检查Base64编解码确保编码encodeToString和解码decode过程无误没有引入换行符或空格。3.检查密钥来源确保用于还原的字符串是完整的密钥没有被截断。java.security.InvalidKeyException初始化Cipher或Signature时传入的密钥对象无效。1.密钥不匹配用公钥尝试解密或签名用私钥尝试加密或验签。确认操作与密钥类型匹配。2.密钥损坏同InvalidKeySpecException检查密钥数据本身。3.密钥长度不兼容某些旧版JDK或安全策略可能对密钥长度有最低要求。确保使用2048位或以上。SignatureException: Signature length not correct验签时提供的签名字节数组长度不对。签名在传输或存储过程中可能被截断或修改。确保签名值的完整性。比较签名字节数组的长度是否与预期相符例如2048位RSA签名应为256字节。解密或验签时出现乱码或异常加密/签名与解密/验签时使用的算法、填充方案或密钥不匹配。这是最常见的问题必须保证两端使用的TRANSFORMATION字符串完全一致。例如一方用“RSA/ECB/PKCS1Padding”加密另一方必须用同样的字符串解密。同样签名和验签的算法如“SHA256withRSA”也必须一致。5.2 性能考量与最佳实践RSA的计算开销远大于AES等对称加密。以下是一些优化建议缓存密钥对和Cipher对象对于服务端频繁生成密钥对是昂贵的。通常一次生成长期使用。Cipher和Signature对象虽然线程不安全但可以通过ThreadLocal进行简单的池化避免反复初始化的开销。使用合适的密钥长度在安全性和性能间权衡。内部微服务通信使用2048位可能足够而对互联网用户的证书可能需要4096位。定期评估和升级密钥长度。绝对不要用RSA加密大数据反复强调这既是安全问题也是性能问题。始终坚持混合加密方案。考虑使用ECC椭圆曲线加密在同等安全强度下ECC的密钥长度更短例如256位ECC相当于3072位RSA计算更快带宽占用更小。对于移动端等资源受限环境ECC是更好的选择。Java从JDK 11开始对ECC的支持已非常完善。5.3 关于“hutool rsa”和第三方库像Hutool这样的工具库对JDK原生的密码学API进行了非常友好的封装。例如使用Hutool实现RSA加解密可能只需要几行代码// Hutool 示例 (需引入依赖) import cn.hutool.crypto.asymmetric.AsymmetricCrypto; import cn.hutool.crypto.asymmetric.KeyType; // 生成密钥对 RSA rsa new RSA(); // 获取Base64编码的公私钥 String publicKeyBase64 rsa.getPublicKeyBase64(); String privateKeyBase64 rsa.getPrivateKeyBase64(); // 加密解密 AsymmetricCrypto crypto new AsymmetricCrypto(AsymmetricAlgorithm.RSA, privateKeyBase64, publicKeyBase64); byte[] encrypt crypto.encrypt(data, KeyType.PublicKey); byte[] decrypt crypto.decrypt(encrypt, KeyType.PrivateKey);我的建议是在理解了本文所述的底层原理和流程之后再在项目中使用Hutool、BouncyCastle等库。它们能极大提升开发效率处理了各种边界情况和兼容性问题。但当出现类似“rsa签名遭遇异常请检查私钥格式是否正确。不正确的长度”这样的错误时底层知识能帮你快速定位是工具库的配置问题还是密钥本身的问题。6. 从原理到实战一个完整的模拟通信案例让我们把所有知识点串联起来模拟一个简化版的客户端-服务器安全通信流程。场景客户端需要向服务器安全地提交一份包含用户ID和交易金额的JSON数据。import com.fasterxml.jackson.databind.ObjectMapper; // 需要Jackson库 public class SecureCommunicationSimulation { // 模拟服务器端持有固定的RSA密钥对 static class Server { private KeyPair serverKeyPair; private ObjectMapper objectMapper new ObjectMapper(); public Server() throws NoSuchAlgorithmException { this.serverKeyPair RSAKeyGenerator.generateKeyPair(2048); System.out.println([Server] 服务器密钥对已生成。); } public PublicKey getPublicKey() { return serverKeyPair.getPublic(); } /** * 处理客户端请求 * param encryptedSessionKey 客户端用服务器公钥加密的AES密钥 * param encryptedData 用上述AES密钥加密的业务数据 * param iv AES GCM模式使用的初始化向量 */ public void processRequest(byte[] encryptedSessionKey, byte[] encryptedData, byte[] iv) throws Exception { System.out.println(\n[Server] 开始处理请求...); // 1. 用服务器私钥解密出AES会话密钥 byte[] sessionKeyBytes RSAOAEPCrypto.decrypt(serverKeyPair.getPrivate(), encryptedSessionKey); SecretKeySpec sessionKey new SecretKeySpec(sessionKeyBytes, AES); System.out.println( [Server] AES会话密钥解密成功。); // 2. 用AES会话密钥解密业务数据 Cipher aesCipher Cipher.getInstance(AES/GCM/NoPadding); GCMParameterSpec gcmSpec new GCMParameterSpec(128, iv); aesCipher.init(Cipher.DECRYPT_MODE, sessionKey, gcmSpec); byte[] decryptedDataBytes aesCipher.doFinal(encryptedData); String decryptedDataJson new String(decryptedDataBytes); System.out.println( [Server] 业务数据解密成功。); // 3. 解析并处理业务数据 TransactionData data objectMapper.readValue(decryptedDataJson, TransactionData.class); System.out.println( [Server] 解析数据: 用户ID data.getUserId() , 金额 data.getAmount()); System.out.println( [Server] 请求处理完毕。); } } // 模拟客户端 static class Client { private PublicKey serverPublicKey; public Client(PublicKey serverPublicKey) { this.serverPublicKey serverPublicKey; } public void sendSecureRequest() throws Exception { System.out.println(\n[Client] 准备发送安全请求...); // 1. 准备业务数据 TransactionData data new TransactionData(user_12345, 299.99); String dataJson new ObjectMapper().writeValueAsString(data); System.out.println( [Client] 业务数据JSON: dataJson); // 2. 生成随机的AES会话密钥和IV KeyGenerator aesKeyGen KeyGenerator.getInstance(AES); aesKeyGen.init(256); SecretKey sessionKey aesKeyGen.generateKey(); byte[] iv new byte[12]; new SecureRandom().nextBytes(iv); // 3. 用AES密钥加密业务数据 Cipher aesCipher Cipher.getInstance(AES/GCM/NoPadding); GCMParameterSpec gcmSpec new GCMParameterSpec(128, iv); aesCipher.init(Cipher.ENCRYPT_MODE, sessionKey, gcmSpec); byte[] encryptedData aesCipher.doFinal(dataJson.getBytes()); // 4. 用服务器公钥加密AES会话密钥 byte[] encryptedSessionKey RSAOAEPCrypto.encrypt(serverPublicKey, sessionKey.getEncoded()); System.out.println( [Client] AES密钥和业务数据已加密。); System.out.println( [Client] 正在发送加密数据包...); // 5. 将 encryptedSessionKey, encryptedData, iv 发送给服务器 // (此处模拟网络传输) return new Object[]{encryptedSessionKey, encryptedData, iv}; } } // 业务数据对象 static class TransactionData { private String userId; private String amount; // 构造器、getter、setter省略... } public static void main(String[] args) throws Exception { // 启动服务器 Server server new Server(); // 客户端获取服务器公钥模拟密钥分发 Client client new Client(server.getPublicKey()); // 客户端构造并发送安全请求 Object[] requestPacket client.sendSecureRequest(); // 服务器接收并处理请求 server.processRequest( (byte[]) requestPacket[0], (byte[]) requestPacket[1], (byte[]) requestPacket[2] ); } }这个案例清晰地展示了RSA在真实场景中的角色安全地传递开启数据宝箱的“临时钥匙”AES会话密钥。整个通信过程中只有服务器的公钥是预先公开的临时生成的AES密钥和业务数据都得到了保护即使通信被监听攻击者也无法破解。7. 总结与进阶思考通过从数学原理到Java代码的完整走查你应该已经对RSA不再感到陌生和畏惧。记住几个核心要点非对称加密解决密钥分发RSA用于加密密钥或签名长数据用对称加密处理密钥长度和安全随机数是生命线算法和填充模式必须两端对齐。在实际项目中你可能会遇到更复杂的情况比如处理PEM格式的密钥、与其它语言如Python、Go的加解密互操作、或者需要符合特定的行业标准如国密SM2。这时底层知识就是你解决问题的罗盘。例如PEM格式只是Base64编码的密钥数据加上头尾标识行跨语言互操作的关键在于确认双方的密钥格式、填充方案、数据编码是否一致。最后密码学是一个深奥的领域永远保持敬畏和学习的心态。不要自己发明加密算法使用经过时间检验的标准算法和库并密切关注安全社区的最新动态及时更新过时的密钥长度和算法。

相关新闻

最新新闻

江苏省工程技术研究中心认定对企业有什么好处?如何申报

江苏省工程技术研究中心认定对企业有什么好处?如何申报

一、江苏省工程技术研究中心认定好处获得该资质意味着企业打通了“政策资金税收优惠项目申报”的绿色通道:1.直接资金奖励省级奖励:根据2026年江苏省最新政策,认定为省级工程技术研究中心,省级财政给予最高100万元的直接奖励。地方…

2026/7/3 6:02:45
AI智能体技能开发实战:从原理到企业级应用

AI智能体技能开发实战:从原理到企业级应用

1. Agent Skills:AI时代的效率革命手册 作为一名在AI领域深耕多年的技术从业者,我见证了从早期规则引擎到如今智能体技术的演进历程。Agent Skills的出现,彻底改变了我们与AI协作的方式——它不再是简单的问答工具,而成为了真正能…

2026/7/3 6:02:45
如何快速解决网盘限速问题:九大网盘直链下载助手完整指南

如何快速解决网盘限速问题:九大网盘直链下载助手完整指南

如何快速解决网盘限速问题:九大网盘直链下载助手完整指南 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天…

2026/7/3 6:02:45
终极指南:3分钟学会用AutoRaise实现macOS悬停自动激活窗口

终极指南:3分钟学会用AutoRaise实现macOS悬停自动激活窗口

终极指南:3分钟学会用AutoRaise实现macOS悬停自动激活窗口 【免费下载链接】AutoRaise AutoRaise (and focus) a window when hovering over it with the mouse 项目地址: https://gitcode.com/gh_mirrors/au/AutoRaise 你是否厌倦了在macOS上频繁点击窗口来…

2026/7/3 6:02:45
采购类标书靠谱服务商

采购类标书靠谱服务商

在众多的招投标服务供应商中,湖南光德信息科技有限公司(简称“光德”)凭借其卓越的专业能力和优质的服务体系脱颖而出,成为采购类标书制作领域中的佼佼者。以下将从多个维度详细解析为何选择光德作为您的首选合作伙伴,…

2026/7/3 6:02:45
【转网安避坑指南】2026 最新网安行业深度复盘,完整还原真实职场全貌,零基础转行参考好文

【转网安避坑指南】2026 最新网安行业深度复盘,完整还原真实职场全貌,零基础转行参考好文

2026想转行黑客/网络安全?一篇带你了解真实的白帽黑客/网络安全职场! 最近你是不是也总刷到网络安全的内容?看别人做渗透测试、打 CTF 比赛,觉得又酷又高薪,心里蠢蠢欲动,也想转行试试?先别急&a…

2026/7/3 5:57:45

周新闻

月新闻