📋 目录
- 一、工具类功能
- 二、所需环境
- 三、方法与调用示例(按需调用)
- 四、工具类源码(可直接复制)
一、工具类功能
公钥加密私钥解密、私钥签名公钥验签,加密固定 RSA-OAEP(SHA-256,与 openssl 互通),超过单块上限的长数据走数字信封一次搞定,不用自己拼分段与会话密钥。
二、所需环境
JDK 17或以上
三、方法与调用示例(按需调用)
🔢方法序号:1
🎯方法用途:
- 一对一把 2048 位 RSA 密钥对生成好,公钥私钥都是能直接存下来的 Base64 字符串
- 省掉
KeyPairGenerator、Base64编码、X.509/PKCS#8 规范名这一串样板
🧩方法定义:public static KeyPairVo generateKeyPair()
🗣️入参解释:public static KeyPairVo generateKeyPair()
📤返回值解释:KeyPairVo,public record KeyPairVo(String publicKeyBase64, String privateKeyBase64),公钥是 X.509 编码、私钥是 PKCS#8 编码
💡边界口径:
- 固定 2048 位,等价于
generateKeyPair(2048),对外长期保密的数据用方法2 升到 3072 位 - 私钥没有任何口令保护,Base64 串拿到就能用,落地要按密钥级别保管
- 生成过程失败抛
IllegalStateException
🧪方法示例:
KeyPairVo keyPair = RsaUtils.generateKeyPair()的publicKeyBase64()长度拿到392字符,MIIBIjANBgkqhkiG打头就是 X.509 公钥- 同一把密钥对的
privateKeyBase64()长度拿到1624字符,MIIEvQIBADANBgkq打头是 PKCS#8 私钥
🔢方法序号:2
🎯方法用途:
- 按指定位数生成 RSA 密钥对,把强度选在业务真正需要的那一档
- 位数不合规当场拒绝,不会打出一把 1024 位的弱密钥还以为更安全
🧩方法定义:public static KeyPairVo generateKeyPair(int keyBits)
🗣️入参解释:public static KeyPairVo generateKeyPair(密钥位数)
📤返回值解释:KeyPairVo,Base64 形态的密钥对
💡边界口径:
- 位数下限 2048(NIST SP 800-131A)且必须是 64 的倍数,否则抛
IllegalArgumentException,消息带上当时的值 - 位数越高,单块上限、密文长度、签名长度都跟着变大,加解密也越慢
- 不要为了「看起来更安全」直接上 8192,长期保密建议 3072 位
🧪方法示例:
generateKeyPair(3072)之后maxEncryptBytes(...)拿到318字节generateKeyPair(4096)之后maxEncryptBytes(...)拿到446字节generateKeyPair(1024)抛IllegalArgumentException: RSA密钥位数不得小于2048且必须是64的倍数,当前为1024generateKeyPair(2050)抛IllegalArgumentException,2050 不是 64 的倍数generateKeyPair(0)抛IllegalArgumentException,消息同上并带上「当前为0」
🔢方法序号:3
🎯方法用途:
- 算出这把公钥单次 RSA-OAEP 最多能加密多少字节,超限前就先知道
- 不用自己记「模数字节数减 66」这个公式,换 2048 还是 3072 都能问出来
🧩方法定义:public static int maxEncryptBytes(String publicKey)
🗣️入参解释:public static int maxEncryptBytes(公钥)
📤返回值解释:int,单块最大明文字节数
💡边界口径:
- 结果是模数字节数减 66(SHA-256 的 OAEP 开销),2048 位即 190
- 超过这个长度请改走方法8 的数字信封,不要自己分段。分段密文里任何一段被替换、丢弃或换序,解密端都察觉不到
- 公钥接受裸 Base64 与 X.509 PEM 两种写法,空白或解不出来的抛
IllegalArgumentException
🧪方法示例:
maxEncryptBytes(keyPair.publicKeyBase64())拿到190,2048 位公钥的单块上限maxEncryptBytes(keyPair3072.publicKeyBase64())拿到318maxEncryptBytes(" ")抛IllegalArgumentException: publicKey不能为null或空白maxEncryptBytes("这不是密钥")抛IllegalArgumentException,消息是「密钥不是合法Base64(含字母表之外的字符 …)」
🔢方法序号:4
🎯方法用途:
- 用公钥把一段 UTF-8 文本加密成 Base64 密文,填充是 RSA-OAEP/SHA-256
- 手机号、身份证号、订单摘要这类短字段一行加密,密文能直接进 JSON
🧩方法定义:public static String encryptByPublicKey(String data, String publicKey)
🗣️入参解释:public static String encryptByPublicKey(明文文本, 公钥)
📤返回值解释:String,Base64 密文,背后字节数固定等于模数字节数
💡边界口径:
- 明文按字节算不能超过方法3 那个上限,超了抛
IllegalArgumentException,消息里给出上限、当前长度和「改用 seal」的指引 - OAEP 带随机填充,同一句明文两次加密的密文不一样,别拿密文做等值比较
- MGF1 哈希被显式固定成 SHA-256,与 openssl、BouncyCastle 打的包能互解;JDK 那个转换名默认 MGF1 用 SHA-1
- 明文或公钥为 null/空白、公钥格式非法都抛
IllegalArgumentException
🧪方法示例:
encryptByPublicKey("订单号 ORD-2026-0001,金额 128.50元", keyPair.publicKeyBase64())拿到256字节的密文(Base64 形态)- 连续两次加密同一句明文,两次密文相同拿到
false encryptByPublicKey(191 字节的文本, keyPair.publicKeyBase64())抛IllegalArgumentException,消息是「RSA-OAEP 单块最多加密 190 字节,当前 191 字节;长数据请用 seal(数字信封) 而不是分段加密」encryptByPublicKey(" ", keyPair.publicKeyBase64())抛IllegalArgumentException: 明文不能为null或空白
🔢方法序号:5
🎯方法用途:
- 用私钥把方法4 产出的 Base64 密文解回原文本
- 解不开时报错说清是密钥不对还是密文被改过,不会一句「解密失败」打发
🧩方法定义:public static String decryptByPrivateKey(String cipherTextBase64, String privateKey)
🗣️入参解释:public static String decryptByPrivateKey(Base64 密文, 私钥)
📤返回值解释:String,解密出来的明文文本
💡边界口径:
- 私钥认 PKCS#8 的 Base64 与 PEM,也认 openssl
-traditional产出的 PKCS#1 私钥(自动补 DER 头转成 PKCS#8) - 把公钥当私钥传进来抛
IllegalArgumentException,消息是「privateKey格式不合法,需要PKCS#8(或openssl的PKCS#1)私钥」 - 密钥不匹配、密文被篡改、填充方案不对,统一抛
IllegalStateException,消息是「RSA解密失败(密钥不匹配或密文被篡改): Padding error in decryption」 - 密文长度不等于模数字节数(被截断过)抛
IllegalArgumentException,不等IllegalStateException
🧪方法示例:
decryptByPrivateKey(cipherText, keyPair.privateKeyBase64())拿到订单号 ORD-2026-0001,金额 128.50元decryptByPrivateKey(cipherText, keyPair.publicKeyBase64())抛IllegalArgumentException,公私钥用反了- 密文中间改一个字节后解密抛
IllegalStateException - 换成另一把私钥解密抛
IllegalStateException
🔢方法序号:6
🎯方法用途:
- 用公钥加密任意字节(已序列化的对象、哈希摘要、二进制报文),拿到定长密文字节
- 载荷不是文本时不用先硬编码成字符串再加密
🧩方法定义:public static byte[] encryptBytes(byte[] data, String publicKey)
🗣️入参解释:public static byte[] encryptBytes(明文字节, 公钥)
📤返回值解释:byte[],密文字节,长度等于模数字节数(2048 位就是 256)
💡边界口径:
- 明文字节不能为
null也不能是空数组,超过单块上限同样抛IllegalArgumentException - 公钥可以是裸 Base64,也可以是带
-----BEGIN PUBLIC KEY-----的 PEM - 密文定长:同一个上限内,1 字节和 190 字节加密出来都是 256 字节
🧪方法示例:
encryptBytes(plainBytes, keyPair.publicKeyBase64())拿到256字节的密文- 把公钥写成 X.509 PEM 再
encryptBytes(...),拿到256字节,解回来仍是原文 encryptBytes(new byte[0], keyPair.publicKeyBase64())抛IllegalArgumentException: 明文字节不能为null或空数组
🔢方法序号:7
🎯方法用途:
- 用私钥把 RSA-OAEP 的密文字节解回原始字节
- 与方法6 成对,二进制链路全程不过 Base64,少一次编解码
🧩方法定义:public static byte[] decryptBytes(byte[] cipherData, String privateKey)
🗣️入参解释:public static byte[] decryptBytes(密文字节, 私钥)
📤返回值解释:byte[],解密出来的原始字节
💡边界口径:
- 解密前先校验长度必须等于模数字节数,截断过的密文抛
IllegalArgumentException并写明期望长度与当前长度 - 填充校验不过(密钥不对、密文被改)抛
IllegalStateException - 用 PKCS#1 密文或别的填充方案产出的密文走这里,也是
IllegalStateException
🧪方法示例:
Arrays.equals(decryptBytes(cipherBytes, keyPair.privateKeyBase64()), plainBytes)拿到true- 把密文砍掉最后一字节再解,抛
IllegalArgumentException: RSA密文长度必须等于模数字节数256,当前为255 - 方法4 的 OAEP 密文拿去走
decryptByPrivateKeyPkcs1(...)抛IllegalStateException,填充方案不匹配
🔢方法序号:8
🎯方法用途:
- 把任意长度的数据封成数字信封:随机一把 AES-256-GCM 会话密钥加密正文,再用 RSA-OAEP 只加密这把密钥
- 长正文不用分段也不用自己拼对称密钥,正文被改动会在拆封时报错
🧩方法定义:public static byte[] seal(byte[] plainText, String publicKey)
🗣️入参解释:public static byte[] seal(明文字节, 公钥)
📤返回值解释:byte[],信封字节,格式是「2字节会话密钥长度 + RSA 加密的会话密钥 + AesUtils 输出(IV 12 字节 + 密文 + GCM 标签)」
💡边界口径:
- 长度不限,32 万字节照样一次封完
- 会话密钥的加解密复用同包的
AesUtils,单独复制本文件时要一并带上它 - 正文或 GCM 标签被动过一个字节,
open会抛IllegalStateException而不是给出错数据 - 空数组也能封,信封长度是
286字节
🧪方法示例:
seal(longPayload, keyPair.publicKeyBase64())把 320000 字节正文封成320286字节的信封seal(new byte[0], keyPair.publicKeyBase64())拿到286字节的信封,拆出来是 0 字节- 把信封最后一个字节翻一下再
open(...)抛IllegalStateException,消息是「AES/GCM校验失败:密钥不匹配或密文已被篡改」 seal(null, keyPair.publicKeyBase64())抛IllegalArgumentException: 明文不能为null
🔢方法序号:9
🎯方法用途:
- 用私钥拆开方法8 的数字信封,一次拿到完整正文
- 长数据的接收端不用管会话密钥、IV、标签这些结构,只管给信封拿正文
🧩方法定义:public static byte[] open(byte[] envelope, String privateKey)
🗣️入参解释:public static byte[] open(信封字节, 私钥)
📤返回值解释:byte[],信封里的正文字节
💡边界口径:
- 信封为
null或短于 3 字节抛IllegalArgumentException,会话密钥长度对不上也抛IllegalArgumentException并写明当时的长度 - 私钥不对时在解会话密钥这一步就抛
IllegalStateException,正文被改时在 GCM 校验那一步抛IllegalStateException - 只支持本类
seal产出的格式,别的信封协议不要指望它
🧪方法示例:
open(envelope, keyPair.privateKeyBase64())解出 320000 字节,与方法8 的原文比对拿到true- 把 283 字节的信封剪掉 5 字节再解,抛
IllegalArgumentException,消息是「信封格式不合法:会话密钥长度=256,总长度=283」 open(new byte[]{1, 2}, keyPair.privateKeyBase64())抛IllegalArgumentException: 信封字节不能为null或短于长度前缀- 换一把私钥拆信封抛
IllegalStateException
🔢方法序号:10
🎯方法用途:
- 文本版数字信封,把任意长度的明文封成一段 Base64 字符串
- 密文要塞进 JSON 字段、数据库列或 URL 参数时,用它比字节数组省事
🧩方法定义:public static String sealText(String plainText, String publicKey)
🗣️入参解释:public static String sealText(明文文本, 公钥)
📤返回值解释:String,Base64 形态的信封
💡边界口径:
- 明文按 UTF-8 取字节,中文不会因为编码出问题
- 结构与
seal完全一样,只是多包了一层 Base64,长度约为字节信封的 4/3 - 明文为
null或空白抛IllegalArgumentException
🧪方法示例:
sealText("订单号 ORD-2026-0001,金额 128.50元", keyPair.publicKeyBase64())拿到440字符的 Base64 信封sealText(" ", keyPair.publicKeyBase64())抛IllegalArgumentException: 明文不能为null或空白
🔢方法序号:11
🎯方法用途:
- 用私钥解开 Base64 文本信封,直接还原文本
- 收端拿到接口里那段字符串就能解,不用自己先 Base64 解码
🧩方法定义:public static String openText(String envelopeBase64, String privateKey)
🗣️入参解释:public static String openText(Base64 信封, 私钥)
📤返回值解释:String,信封里的明文文本
💡边界口径:
- 与
sealText成对,异常口径跟着方法9 - 信封串为
null或空白抛IllegalArgumentException - 明文里的中文按 UTF-8 还原,不受系统默认编码影响
🧪方法示例:
openText(envelopeBase64, keyPair.privateKeyBase64())拿到订单号 ORD-2026-0001,金额 128.50元- 用别人的私钥解这个文本信封抛
IllegalStateException
🔢方法序号:12
🎯方法用途:
- 用私钥对文本做
SHA256withRSA签名,拿到 Base64 签名值 - 签名方案是确定性的,同一私钥同一原文逐字节相同,能直接拿去对账
🧩方法定义:public static String sign(String data, String privateKey)
🗣️入参解释:public static String sign(待签名文本, 私钥)
📤返回值解释:String,Base64 签名值,原始字节数等于模数字节数
💡边界口径:
- 2048 位密钥的签名是 256 字节、Base64 后 344 字符;3072 位是 384 字节
- 同一原文同一私钥重复签,结果一致,这是它和 OAEP 加密最大的差别
- 私钥为
null/空白、格式非法抛IllegalArgumentException;签名过程本身失败抛IllegalStateException - 它签的是原文的哈希,别拿签名值当密文用
🧪方法示例:
sign("订单号 ORD-2026-0001,金额 128.50元", keyPair.privateKeyBase64())的签名值拿到256字节- 对同一句明文签两次,两次结果相同拿到
true,Base64 长度344字符 sign("x", keyPair.publicKeyBase64())抛IllegalArgumentException,把公钥当私钥签名不被接受
🔢方法序号:13
🎯方法用途:
- 对任意字节做
SHA256withRSA签名,拿到签名字节 - 文件摘要、二进制报文这类不是文本的东西,也能走同一套签名口径
🧩方法定义:public static byte[] signBytes(byte[] data, String privateKey)
🗣️入参解释:public static byte[] signBytes(待签名字节, 私钥)
📤返回值解释:byte[],签名字节,长度等于模数字节数
💡边界口径:
- 与
sign只是编码差别:同样的内容签出来是同一个值,Base64 一下就和sign相同 - 数据为
null抛IllegalArgumentException - 签名字节直接进文件头或报文头,不必再过 Base64
🧪方法示例:
Base64.getEncoder().encodeToString(signBytes(plainBytes, keyPair.privateKeyBase64()))与sign(...)同值拿到true- 3072 位密钥下
signBytes(...)拿到384字节的签名值
🔢方法序号:14
🎯方法用途:
- 用公钥校验文本的
SHA256withRSA签名,结果只有一个boolean - 对外的验签接口拿到垃圾输入不该变成异常,这里把不匹配统一收敛成
false
🧩方法定义:public static boolean verify(String data, String publicKey, String signBase64)
🗣️入参解释:public static boolean verify(原文, 公钥, Base64 签名)
📤返回值解释:boolean,验签通过为true,签名对不上为false
💡边界口径:
- 签名值长度不对、换了公钥、原文被改过,一律
false,不抛异常 - 但签名串不是合法 Base64、或参数为
null/空白属于入参非法,仍然抛IllegalArgumentException - 公钥格式非法同样抛
IllegalArgumentException,消息是「publicKey格式不合法,需要X.509(SubjectPublicKeyInfo)的Base64或PEM」
🧪方法示例:
verify("订单号 ORD-2026-0001,金额 128.50元", keyPair.publicKeyBase64(), sign)原文没动拿到true- 原文末尾加一个字符再验签拿到
false - 换成别的公钥验同一份签名拿到
false - 签名串塞成 64 字节的无意义数据拿到
false,不是抛异常 verify("订单号 ORD-2026-0001,金额 128.50元", keyPair.publicKeyBase64(), "@@不是base64@@")抛IllegalArgumentException,消息是「签名不是合法Base64(含字母表之外的字符 @)」verify(null, keyPair.publicKeyBase64(), sign)抛IllegalArgumentException: 原始数据不能为null
🔢方法序号:15
🎯方法用途:
- 用公钥校验字节签名,字节链路的验签入口
- 与方法14 同一套判定口径,只是收的是原始字节和签名字节
🧩方法定义:public static boolean verifyBytes(byte[] data, String publicKey, byte[] signData)
🗣️入参解释:public static boolean verifyBytes(原始字节, 公钥, 签名字节)
📤返回值解释:boolean,通过为true,不通过为false
💡边界口径:
- 签名值格式错误或验签不通过都返回
false,不包装成异常 data、signData为null、公钥非法抛IllegalArgumentException- 签名字节长度不对也归进
false
🧪方法示例:
verifyBytes(plainBytes, keyPair.publicKeyBase64(), signBytes(plainBytes, keyPair.privateKeyBase64()))拿到true- 把原始字节改一个字节再验拿到
false
🔢方法序号:16
🎯方法用途:
- 历史兼容入口:用 PKCS#1 v1.5 填充加密字节,对接仍在用它的老网关
- 新系统不要选它,这个填充有 Bleichenbacher 式选择密文攻击面
🧩方法定义:public static byte[] encryptByPublicKeyPkcs1(byte[] data, String publicKey)
🗣️入参解释:public static byte[] encryptByPublicKeyPkcs1(明文字节, 公钥)
📤返回值解释:byte[],密文字节,长度等于模数字节数
💡边界口径:
- 单块上限是模数字节数减 11,2048 位即 245 字节,超了抛
IllegalArgumentException并写明当前长度 - v1.5 也是随机填充,同一明文两次密文不同
- 只在对方还在用 v1.5 时用,并确保错误信息不外泄「填充是否合法」的差异
🧪方法示例:
encryptByPublicKeyPkcs1(plainBytes, keyPair.publicKeyBase64())拿到256字节的密文- 同一句明文加密两次,两次密文相同拿到
false encryptByPublicKeyPkcs1(246 字节, keyPair.publicKeyBase64())抛IllegalArgumentException,消息是「PKCS#1 v1.5 单块最多加密 245 字节,当前 246 字节」
🔢方法序号:17
🎯方法用途:
- 历史兼容入口:用私钥解 PKCS#1 v1.5 填充的密文
- 给仍在用 v1.5 的老网关回包用,别把新写的链路挂到它上面
🧩方法定义:public static byte[] decryptByPrivateKeyPkcs1(byte[] cipherData, String privateKey)
🗣️入参解释:public static byte[] decryptByPrivateKeyPkcs1(密文字节, 私钥)
📤返回值解释:byte[],解密出来的原始字节
💡边界口径:
- 只解 v1.5 密文,方法6 的 OAEP 密文走这里抛
IllegalStateException,两者不通用 - 密文长度不等于模数字节数抛
IllegalArgumentException - 解出来的明文字节要自己按约定编码转回字符串
🧪方法示例:
decryptByPrivateKeyPkcs1(cipherData, keyPair.privateKeyBase64())解回拿到hello- OAEP 密文走这个入口抛
IllegalStateException,消息是「RSA解密失败(密钥不匹配或密文被篡改): Padding error in decryption」
四、工具类源码(可直接复制)
📦领取方式: 关注点赞收藏,私信回复「java工具类」可领取100个工具类「Java企业级工具类全家桶(1-100)」的完整源码。
💡 工具类已调试通过,复制粘贴即可使用,省时省力
💡 可保存到自己网盘,永久有效
觉得对您有帮助,麻烦点点关注啦,您的关注是我创作的最大动力~ 🎯