Java中使用PublicKey验证RSA签名失败问题求助
RSA签名验证失败的问题分析与修复
核心问题:私钥生成逻辑错误
你的代码中generatePrivateKey方法存在致命错误:使用公钥指数e初始化RSAPrivateKeySpec,但该规范要求传入的是私钥指数d,而非公钥指数。这导致生成的私钥完全无效,用它签名的消息自然无法被正确公钥验证。
RSAPrivateKeySpec的构造参数定义为:RSAPrivateKeySpec(BigInteger modulus, BigInteger privateExponent),你错误地将公钥指数e当作私钥指数传入,直接导致签名逻辑失效。
修复方案
方案1:使用合法私钥指数d手动生成私钥
如果你已经拥有对应公钥的私钥指数d,修改generatePrivateKey方法,将参数替换为私钥指数d:
static PrivateKey generatePrivateKey(String modulus, String privateExponent) throws NoSuchAlgorithmException, InvalidKeySpecException { BigInteger n = new BigInteger(1, Base64.getUrlDecoder().decode(modulus)); BigInteger d = new BigInteger(1, Base64.getUrlDecoder().decode(privateExponent)); return KeyFactory.getInstance("RSA").generatePrivate(new RSAPrivateKeySpec(n, d)); }
同时在main方法中传入对应的d值(你原代码仅提供了n和e,缺失私钥指数d,需要补充)。
方案2:用KeyPairGenerator自动生成密钥对(推荐)
手动构造密钥对容易出错,实际开发中推荐使用Java提供的KeyPairGenerator生成合法RSA密钥对,它会自动处理所有密钥参数:
import java.nio.charset.StandardCharsets; import java.security.*; import java.security.spec.RSAPrivateKeySpec; import java.security.spec.RSAPublicKeySpec; import java.util.Base64; public class JWT2 { public static void main(String[] args) throws Exception { String plainTextMessage = "A Secret!"; // 生成2048位RSA密钥对(安全强度达标) KeyPair keyPair = generateRSAKeyPair(); // 签名 byte[] signedMessageBytes = signMessage(plainTextMessage, keyPair.getPrivate()); // 验证 boolean isMsgValid = verifySignedMessage(plainTextMessage.getBytes(StandardCharsets.UTF_8), signedMessageBytes, keyPair.getPublic()); System.out.println("isMsgValid = " + isMsgValid); // 输出true // 可选:导出密钥参数用于存储 RSAPublicKeySpec pubSpec = KeyFactory.getInstance("RSA").getKeySpec(keyPair.getPublic(), RSAPublicKeySpec.class); RSAPrivateKeySpec privSpec = KeyFactory.getInstance("RSA").getKeySpec(keyPair.getPrivate(), RSAPrivateKeySpec.class); String n = Base64.getUrlEncoder().encodeToString(pubSpec.getModulus().toByteArray()); String e = Base64.getUrlEncoder().encodeToString(pubSpec.getPublicExponent().toByteArray()); String d = Base64.getUrlEncoder().encodeToString(privSpec.getPrivateExponent().toByteArray()); System.out.println("n: " + n); System.out.println("e: " + e); System.out.println("d: " + d); } // 生成RSA密钥对 static KeyPair generateRSAKeyPair() throws NoSuchAlgorithmException { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(2048); return keyGen.generateKeyPair(); } static boolean verifySignedMessage(byte[] plainTextMessagebytes, byte[] signedMessageBytes, PublicKey publicKey) throws Exception { Signature signature = Signature.getInstance("SHA256withRSA"); signature.initVerify(publicKey); signature.update(plainTextMessagebytes); return signature.verify(signedMessageBytes); } static byte[] signMessage(String plainTextMessage, PrivateKey privateKey) throws Exception { Signature signature = Signature.getInstance("SHA256withRSA"); signature.initSign(privateKey); signature.update(plainTextMessage.getBytes(StandardCharsets.UTF_8)); return signature.sign(); } // 从导出的参数恢复公钥 static PublicKey generatePublicKey(String modulus, String publicExponent) throws NoSuchAlgorithmException, InvalidKeySpecException { BigInteger n = new BigInteger(1, Base64.getUrlDecoder().decode(modulus)); BigInteger e = new BigInteger(1, Base64.getUrlDecoder().decode(publicExponent)); return KeyFactory.getInstance("RSA").generatePublic(new RSAPublicKeySpec(n, e)); } // 从导出的参数恢复私钥 static PrivateKey generatePrivateKey(String modulus, String privateExponent) throws NoSuchAlgorithmException, InvalidKeySpecException { BigInteger n = new BigInteger(1, Base64.getUrlDecoder().decode(modulus)); BigInteger d = new BigInteger(1, Base64.getUrlDecoder().decode(privateExponent)); return KeyFactory.getInstance("RSA").generatePrivate(new RSAPrivateKeySpec(n, d)); } }
额外说明
- 你代码中使用的
SHA256withRSA签名算法是安全合规的,这部分逻辑没有问题。 - 手动构造密钥对时,必须同时拥有模数
n、公钥指数e、私钥指数d三个参数,缺一不可。
内容的提问来源于stack exchange,提问作者Kuldeep Singh
相关产品推荐
相关产品推荐

