You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 16:18:20