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
相关产品推荐
相关产品推荐

