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

Scala使用RS256生成JWT遇签名错误及密码短语传递问题

Scala使用RS256算法生成JWT的问题解决

问题重现

Scala报错代码

val jwtPayload = s"""|{
                     |    "exp": $time,
                     |    "iss": "$orgId",
                     |    "sub": "$technicalAccountId",
                     |    "aud": "${imsExp}/c/${clientId}",
                     |    "${imsExp}/s/${metaScope}": true
                     |}""".stripMargin
println(jwtPayload)
val token = Jwts.builder()
  .setPayload(jwtPayload)
  .signWith(SignatureAlgorithm.RS256,privateKey.getBytes("UTF-8"))

报错信息

传递字节数组时的错误:

Key bytes may only be specified for HMAC signatures.  If using RSA or Elliptic Curve, use the signWith(SignatureAlgorithm, Key) method instead.
    at io.jsonwebtoken.lang.Assert.isTrue(Assert.java:38)

移除.getBytes("UTF-8")后的错误:

Exception in thread "main" java.lang.IllegalArgumentException: Base64-encoded key bytes may only be specified for HMAC signatures.  If using RSA or Elliptic Curve, use the signWith(SignatureAlgorithm, Key) method instead.

可正常运行的JavaScript代码

const jwtPayload = {
        exp: Math.round(300 + Date.now() / 1000),
        iss: secrets.org,
        sub: secrets.id,
        aud: `${secrets.imsEndpoint}/c/${secrets.technicalAccount.clientId}`,
        [`${secrets.imsEndpoint}/s/${secrets.metascopes}`]: true
    };

let token;
try {
    token = jwt.sign(
        jwtPayload,
        { key: secrets.privateKey},
        { algorithm: 'RS256' }
    );
    console.log(token);
} catch (tokenError) {
    return Promise.reject(tokenError);
}

疑问解答

1. 解决签名错误

报错核心原因:JJWT库对RSA/ECC这类非对称加密算法,要求传入**java.security.Key类型对象**,而非字符串或字节数组。需将PEM格式的私钥字符串解析为PrivateKey实例。

解析PKCS#8格式私钥(无密码)

如果私钥是PKCS#8格式(头尾为-----BEGIN PRIVATE KEY-----/-----END PRIVATE KEY-----):

import java.security.spec.PKCS8EncodedKeySpec
import java.security.{KeyFactory, PrivateKey}
import java.util.Base64

// 清理PEM格式的冗余内容
val cleanedPrivateKey = privateKey
  .replace("-----BEGIN PRIVATE KEY-----", "")
  .replace("-----END PRIVATE KEY-----", "")
  .replaceAll("\\s+", "")

// Base64解码并生成PrivateKey
val keyBytes = Base64.getDecoder.decode(cleanedPrivateKey)
val keySpec = new PKCS8EncodedKeySpec(keyBytes)
val keyFactory = KeyFactory.getInstance("RSA")
val rsaPrivateKey: PrivateKey = keyFactory.generatePrivate(keySpec)

// 正确生成JWT
val token = Jwts.builder()
  .setPayload(jwtPayload)
  .signWith(SignatureAlgorithm.RS256, rsaPrivateKey)
  .compact() // 必须调用compact()生成最终token字符串

解析PKCS#1格式私钥(无密码)

如果私钥是PKCS#1格式(头尾为-----BEGIN RSA PRIVATE KEY-----/-----END RSA PRIVATE KEY-----),需借助BouncyCastle库解析:

import org.bouncycastle.asn1.pkcs.PrivateKeyInfo
import org.bouncycastle.openssl.PEMParser
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter
import java.io.StringReader

val pemParser = new PEMParser(new StringReader(privateKey))
val keyInfo = pemParser.readObject().asInstanceOf[PrivateKeyInfo]
val converter = new JcaPEMKeyConverter()
val rsaPrivateKey: PrivateKey = converter.getPrivateKey(keyInfo)

2. 传递密码短语(加密私钥)

如果私钥带密码保护(头尾为-----BEGIN ENCRYPTED PRIVATE KEY-----),解析时需传入密码:

import org.bouncycastle.openssl.PEMDecryptorProvider
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter
import java.io.StringReader

val password = "你的私钥密码".toCharArray()
val pemParser = new PEMParser(new StringReader(privateKey))
val encryptedKeyPair = pemParser.readObject()
val decryptorProvider = new JcePEMDecryptorProviderBuilder().build(password)
val converter = new JcaPEMKeyConverter()
val rsaPrivateKey: PrivateKey = converter.getPrivateKey(
  encryptedKeyPair.asInstanceOf[org.bouncycastle.openssl.PEMEncryptedKeyPair]
    .decryptKeyPair(decryptorProvider)
)

依赖说明:使用BouncyCastle需添加依赖,以sbt为例:

libraryDependencies += "org.bouncycastle" % "bcprov-jdk15on" % "1.70"

内容的提问来源于stack exchange,提问作者Shivam Sahil

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 12:30:46