Java “意外”;InvalidJwtSignatureException:JWT因签名无效而被拒绝;
我有一个JWT,看起来像这样(我不得不隐藏一些值): 或者简单地说:Java “意外”;InvalidJwtSignatureException:JWT因签名无效而被拒绝;,java,jose4j,Java,Jose4j,我有一个JWT,看起来像这样(我不得不隐藏一些值): 或者简单地说: { "typ": "JWT", "alg": "RS256", "x5t": "8Q3reRBv6jj6FyxBo5phA1yKzYg", "kid": "8Q3reRBv6jj6FyxBo5phA1yKzYg" } 及 这是我为验证它而编写的代码(从网站中的
{
"typ": "JWT",
"alg": "RS256",
"x5t": "8Q3reRBv6jj6FyxBo5phA1yKzYg",
"kid": "8Q3reRBv6jj6FyxBo5phA1yKzYg"
}
及
这是我为验证它而编写的代码(从网站中的示例开始)
我的目标是目前只验证签名,而不验证其他密钥对值
但是,当我运行代码时,我得到:
Invalid JWT! org.jose4j.jwt.consumer.InvalidJwtSignatureException: JWT rejected due to invalid signature. Additional details: [[9] Invalid JWS Signature: JsonWebSignature{"typ":"JWT","alg":"RS256","x5t":"8Q3reRBv6jj6FyxBo5phA1yKzYg","kid":"8Q3reRBv6jj6FyxBo5phA1yKzYg"}->eyJ0eXAiOi.....]
而“->”右边的令牌确实是我的令牌
所以我怀疑我没有正确设置JWTConsumer,但是我看不出错误在哪里
工作解决方案:
@SneakyThrows
public boolean isValid(String extractedToken) {
log.info("Validating JWT");
String pem = "MIIBIj........DAQAB";
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(Base64.getMimeDecoder().decode(pem));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(pubKeySpec);
// Use JwtConsumerBuilder to construct an appropriate JwtConsumer, which will
// be used to validate and process the JWT.
// The specific validation requirements for a JWT are context dependent, however,
// it typically advisable to require a (reasonable) expiration time, a trusted issuer, and
// and audience that identifies your system as the intended recipient.
// If the JWT is encrypted too, you need only provide a decryption key or
// decryption key resolver to the builder.
JwtConsumer jwtConsumer = new JwtConsumerBuilder()
.setVerificationKey(publicKey) // verify the signature with the public key
.setJwsAlgorithmConstraints( // only allow the expected signature algorithm(s) in the given context
AlgorithmConstraints.ConstraintType.PERMIT, AlgorithmIdentifiers.RSA_USING_SHA256) // which is only RS256 here
.setSkipAllDefaultValidators()
.build(); // create the JwtConsumer instance
try
{
// Validate the JWT and process it to the Claims
JwtClaims jwtClaims = jwtConsumer.processToClaims(extractedToken);
System.out.println("JWT validation succeeded! " + jwtClaims);
log.info("JTW validated");
return true;
}
catch (InvalidJwtException e)
{
// InvalidJwtException will be thrown, if the JWT failed processing or validation in anyway.
// Hopefully with meaningful explanations(s) about what went wrong.
System.out.println("Invalid JWT! " + e);
// Programmatic access to (some) specific reasons for JWT invalidity is also possible
// should you want different error handling behavior for certain conditions.
// Whether or not the JWT has expired being one common reason for invalidity
if (e.hasExpired())
{
System.out.println("JWT expired at " + e.getJwtContext().getJwtClaims().getExpirationTime());
}
// Or maybe the audience was invalid
if (e.hasErrorCode(ErrorCodes.AUDIENCE_INVALID))
{
System.out.println("JWT had wrong audience: " + e.getJwtContext().getJwtClaims().getAudience());
}
}
log.info("JTW validated");
return false;
}提取的Token签名来自与此处生成的RSA对不同的RSA对:
RsaJsonWebKey rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
所以当你这么做的时候:
JwtConsumer jwtConsumer = new JwtConsumerBuilder()
.setRequireExpirationTime() // the JWT must have an expiration time
.setAllowedClockSkewInSeconds(30) // allow some leeway in validating time based claims to account for clock skew
.setVerificationKey(rsaJsonWebKey.getKey()) // verify the signature with the public key
.setJwsAlgorithmConstraints( // only allow the expected signature algorithm(s) in the given context
AlgorithmConstraints.ConstraintType.PERMIT, AlgorithmIdentifiers.RSA_USING_SHA256) // which is only RS256 here
.build();
然后:
JwtClaims jwtClaims = jwtConsumer.processToClaims(extractedToken);
您正在根据其他公钥(不是最初使用的公钥)验证令牌
为了使JWT令牌的签名有效,您必须保留原始RSA对,然后将公钥放入JWTConsumer,如下所示:
.setVerificationKey(originalPublicKey)
我相信密钥必须用base64编码。.RsaJwkGenerator的目的是生成一个随机的RSA密钥对,因此如果您想对JWT令牌进行反验证。您必须使用RSA私钥对此令牌进行签名(请参见此处)。密钥id和指纹仅用于使用将正确的公钥链接到令牌JWK@aran我尝试将字符串“8Q3reRBv6jj6FyxBo5phA1yKzYg”编码为base64和base64url,并将结果粘贴到代码中,但没有成功。我误解你的评论了吗?@vmargerin我正是以这段代码为例。你能不能更具体一点,告诉我我应该改变什么?嗨,维马格林,谢谢你的回答!我将公钥作为字符串tho,代码无法编译。如何正确地将其转换为密钥?这取决于字符串密钥的编码方式。这里已经提到了这个问题:嘿,谢谢你提供了非常宝贵的提示。在我将此标记为已解决之前,还有一点。现在我有两个错误:过期和错误的观众。我没有在我的代码中为这两个检查设置任何要求。如果我使用.SetSkipalDefaultValidators(),我是否也会跳过签名的验证?仅供参考:我问这个问题是为了了解我的代码是否真的在工作还是我正在跳过签名检查如果您不希望看到特定的访问者,您可以使用setSkipDefaultAudienceValidation()。但通常建议验证受众是否可行(使用setExpectedAudience(受众))。我不完全确定,但是SetSkipalDefaultValidator应该删除声明验证(而不是签名)
JwtConsumer jwtConsumer = new JwtConsumerBuilder()
.setRequireExpirationTime() // the JWT must have an expiration time
.setAllowedClockSkewInSeconds(30) // allow some leeway in validating time based claims to account for clock skew
.setVerificationKey(rsaJsonWebKey.getKey()) // verify the signature with the public key
.setJwsAlgorithmConstraints( // only allow the expected signature algorithm(s) in the given context
AlgorithmConstraints.ConstraintType.PERMIT, AlgorithmIdentifiers.RSA_USING_SHA256) // which is only RS256 here
.build();
JwtClaims jwtClaims = jwtConsumer.processToClaims(extractedToken);
.setVerificationKey(originalPublicKey)