Java如何基于字符串形式的公私钥构建RSAKey用于JWS签名
基于PEM格式RSA密钥构建可用于JWS签名的RSAKey实例
实现思路
你当前使用的是nimbus-jose-jwt库,不需要引入额外第三方依赖,直接通过JDK原生密钥工厂解析PEM格式的公私钥字符串,再通过RSAKey自带的Builder组装实例即可,完全兼容原有签名逻辑。
核心步骤:
- 清理PEM字符串的头尾标记、所有空白字符,得到纯Base64编码的密钥字节
- 分别用
PKCS8EncodedKeySpec解析PKCS#8格式的私钥、X509EncodedKeySpec解析X.509格式的公钥,转成JDK原生的RSA公私钥对象 - 用
RSAKey.Builder组装成RSAKey实例,替换原有随机生成密钥的代码
完整实现代码
import com.nimbusds.jose.JWSHeader; import com.nimbusds.jose.JWSSigner; import com.nimbusds.jose.JWSObject; import com.nimbusds.jose.Payload; import com.nimbusds.jose.crypto.RSASSASigner; import com.nimbusds.jose.jwk.RSAKey; import com.nimbusds.jose.util.Base64; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.PublicKey; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.HashMap; import java.util.Map; public class JwsSignUtil { // 替换为你自己存储的PEM格式私钥字符串 private static final String PRIVATE_KEY_PEM = "-----BEGIN PRIVATE KEY-----\n" + "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDWvChzPXBs9aA3\n" + "VlCKSG5z/EaTgpXlSDqiBVmoUKVFTIvK+mj72QYZLIXwp8GimNJAK+atUdH5YfNN\n" + "B8x2eQg2NLYigdnv5/lADI8gAcUVf1cKSwt9kgpLv2CURBBIlMh9eg3ofN9eCRm7\n" + "rbsVULOtYjpMe2nbLNdmdBtuVcjpKfN8nC4uWcoogK0jJ4ijBNmsIqAFCcIFWMuG\n" + "z2HENSFSbx+XOavsOaxrfsQpZI5IqiZMAR3GXCFkYu2nPrpUAhAElBql0TE/xr7l\n" + "dxfm5wwTPMbcUvBvIskYUqMkhc+LeO1zC0GNYhJju2Y6Ur3MJf0ztewwj1HOqD2b\n" + "Q3pAHjO9AgMBAAECggEATus8Go5cAU+MoInSc+AG6A2xiokVufx/wAgjWV66PuvQ\n" + "/LpnVxf9y8a3OykMW0u7CeaYkt5dQ0AigQ76sBfvUqywu6HpjOg+jLGQ8Hx2CF6S\n" + "iK/n+zBvJEOjpRoWufYkcSkob2dlWFQT5wwEk+LjBjfxoSZCU1D5oSfO5RAWFMqv\n" + "UWFQjlhpgtFWzBNtZIMBetWUeHuNJNYSCA2Gu2tv4yQqHgxcwScM596OXErduZc1\n" + "OZQ9Kwxe9AE6pFryxcxyngJWOKky668JwUdc/IKDzsg4O5uodZabtR1aAsX6IMAQ\n" + "ylRZeCnV/BHSljoS0wAXvUNcC4KsuFUiROIoGNfCaQKBgQD+J5quLr+Exso1KvZZ\n" + "3X4lK+hWx+7uSOr5j0No6Y+u5X98whbGqwXPFpjLFqQlcpx0DKbl4Od0FqeKBTdv\n" + "qiL3b2CRp4w+AgHjfM2ENg5XgkQDZXwToQy8ZIQvL82QMnZMYYrVoV6PfCIs7+/t\n" + "rWL+25IrP/NB9jGRKFCCBWz1ewKBgQDYS0jcaC11BvT4AfJfmZVZ8nPQuy5IB6Uq\n" + "I++5PhtTRxOW/XS/kiblp5grVmD8ZgSyIgCBU3zcZ9AF5KyN2Xl7fM8oB1owxXmi\n" + "SZgEA9b66elR7nnMRmPsDeeA0LBfasIIlifNdzEVyuy3BrI38zIuj/h1zs2WsLbX\n" + "tQh4smoKJwKBgQCYmMTZsj3Rhd3g4GO7fy5/OQauHCsMLQHQR0FNG3bmpurNyGcO\n" + "b570QPgKcBSsW00urG4E9e1iGTwMtaccR4XpFJlhuryMen4RzVxD9oTT6+XUODmw\n" + "O3E/KAbpogUFgBbhM2u9ar8w3XJTkth21zTqGoF/sEzpHN2T7yWve3x5QwKBgQCB\n" + "ZJhT8qavCdhmvZNniZOFWbvbNP887AXsrc7tfLAQI8ceXsYHDMHkRVyNIIGovMc2\n" + "YYz30SAzIo+Z1vE7csxwCXxMMAnOf3SCl5AvZrSnKmQANa/7emiwgKjrsOyySEWH\n" + "qXxqOFHO/bSa0ZCwU/bDUDGNGIh5C4J2jMBipCk3pQKBgAfrM+zHmXxkTMUtFeZT\n" + "OuZVv/4NEQYI0Sb243K/+PVw0c0VjIzp6SuDGvVhzcXV+0K8AhFaYBoAp4BSL11M\n" + "fcmlSTMA1Zi6xOlgwuRDvsw03pRLBD8cJ7YzYau0UTqSWa+/ojXpuSdN4o5ZOsDJ\n" + "tpl2RanF4WWFPPMrGYoPZ0f4\n" + "-----END PRIVATE KEY-----"; // 替换为你自己存储的PEM格式公钥字符串 private static final String PUBLIC_KEY_PEM = "-----BEGIN PUBLIC KEY-----\n" + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1rwocz1wbPWgN1ZQikhu\n" + "c/xGk4KV5Ug6ogVZqFClRUyLyvpo+9kGGSyF8KfBopjSQCvmrVHR+WHzTQfMdnkI\n" + "NjS2IoHZ7+f5QAyPIAHFFX9XCksLfZIKS79glEQQSJTIfXoN6HzfXgkZu627FVCz\n" + "rWI6THtp2yzXZnQbblXI6SnzfJwuLlnKKICtIyeIowTZrCKgBQnCBVjLhs9hxDUh\n" + "Um8flzmr7Dmsa37EKWSOS83mTAEdxlwhZGLtpz66VAIQBJQapdExP8a+5XcX5ucM\n" + "EzzG3FLwbyLJGFKjJIXPi3jtcwtBjWISY7tmOlK9zCX9M7XsMI9Rzqg9m0N6QB4z\n" + "vQIDAQAB\n" + "-----END PUBLIC KEY-----"; private static final int RSA_KEY_SIZE_BITS = 2048; public static String generateJws() throws Exception { // 解析私钥 String privateKeyContent = PRIVATE_KEY_PEM .replace("-----BEGIN PRIVATE KEY-----", "") .replace("-----END PRIVATE KEY-----", "") .replaceAll("\\s", ""); PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(Base64.from(privateKeyContent).decode()); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); RSAPrivateKey privateKey = (RSAPrivateKey) keyFactory.generatePrivate(privateKeySpec); // 解析公钥 String publicKeyContent = PUBLIC_KEY_PEM .replace("-----BEGIN PUBLIC KEY-----", "") .replace("-----END PUBLIC KEY-----", "") .replaceAll("\\s", ""); X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(Base64.from(publicKeyContent).decode()); RSAPublicKey publicKey = (RSAPublicKey) keyFactory.generatePublic(publicKeySpec); // 构建RSAKey实例,替换原有RSAKeyGenerator生成逻辑 RSAKey rsaJWK = new RSAKey.Builder(publicKey) .privateKey(privateKey) .keySize(RSA_KEY_SIZE_BITS) .build(); // 原有签名逻辑无需修改 Map<String, Object> header = new HashMap<>(); header.put("alg", "RS256"); header.put("x5t#S256", "hash256"); Map<String, Object> payload = new HashMap<>(); payload.put("iss", "issuer"); payload.put("aud", "audience"); payload.put("exp", Long.toString(Instant.now().plus(8, ChronoUnit.HOURS).getEpochSecond())); payload.put("iat", Long.toString(Instant.now().minus(4, ChronoUnit.HOURS).getEpochSecond())); payload.put("nbf", Long.toString(Instant.now().minus(3, ChronoUnit.HOURS).getEpochSecond())); payload.put("jti", "jtiId"); JWSSigner signer = new RSASSASigner(rsaJWK); JWSObject jwsObject = new JWSObject( JWSHeader.parse(header), new Payload(payload)); jwsObject.sign(signer); return jwsObject.serialize(); } }
关键说明
- 你提供的私钥是标准PKCS#8格式(头尾标识为
-----BEGIN PRIVATE KEY-----),直接用PKCS8EncodedKeySpec解析即可,无需做格式转换 - 清理PEM内容时必须移除所有换行、空格,否则Base64解码会抛出异常
- 构建得到的RSAKey实例同时包含公私钥,完全适配
RSASSASigner的入参要求,原有签名逻辑不需要做任何修改 - 如果后续仅需做JWS验签,构建RSAKey时只传入公钥即可,不需要加载私钥
- 若项目使用JDK15及以上版本,可直接用Java文本块
"""存储PEM字符串,不需要手动拼接换行符
内容的提问来源于stack exchange,提问作者Norby
相关产品推荐
相关产品推荐

