意外 "InvalidJwtSignatureException: JWT rejected due to invalid signature"
Unexpected "InvalidJwtSignatureException: JWT rejected due to invalid signature"
我有一个看起来像这样的 JWT(我不得不隐藏一些值):
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJ4eHgiLCJpc3MiOiJ4eHgiLCJpYXQiOjE2MTIzNDkwMTEsIm5iZiI6MCwiZXhwIjoxNjEyMzUyNjExLCJhdXRoX3RpbWUiOjE2MTEwNDU5MjgsIm5vbmNlIjoieHh4Iiwic3ViIjoieHh4IiwidXBuIjoieHh4IiwidW5pcXVlX25hbWUiOiJ4eHgiLCJwd2RfdXJsIjoieHh4IiwicHdkX2V4cCI6Inh4eCIsInNpZCI6Inh4eCIsImp0aSI6ImZmYWQ0NjM1LTU3MmItNGUyYi04ZGRhLTAxNmEzNDRlYzY4ZiJ9.nW5xTs6IbEkIFTZ_9PJZBpZAHXqG2HeU6y0XJwmQZiM
或者简单地说:
{
"typ": "JWT",
"alg": "RS256",
"x5t": "8Q3reRBv6jj6FyxBo5phA1yKzYg",
"kid": "8Q3reRBv6jj6FyxBo5phA1yKzYg"
}
和
{
"aud": "xxx",
"iss": "xxx",
"iat": 0,
"nbf": 0,
"exp": 1611049528,
"auth_time": 1611045928,
"nonce": "xxx",
"sub": "xxx",
"upn": "xxx",
"unique_name": "xxx",
"pwd_url": "xxx",
"pwd_exp": "xxx",
"sid": "xxx"
}
这是我为了验证而写的代码(从网站上的例子开始)。
@SneakyThrows
public boolean isValid(String extractedToken) {
log.info("Validating JWT");
// Generate an RSA key pair, which will be used for signing and verification of the JWT, wrapped in a JWK
RsaJsonWebKey rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
// Give the JWK a Key ID (kid), which is just the polite thing to do
rsaJsonWebKey.setKeyId("8Q3reRBv6jj6FyxBo5phA1yKzYg");
rsaJsonWebKey.setX509CertificateSha256Thumbprint("8Q3reRBv6jj6FyxBo5phA1yKzYg");
// 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()
.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(); // 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;
}
我的 objective 目前只验证签名而不验证其他密钥对值。
但是,当我 运行 代码时,我得到:
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.....]
而“->”右边的token确实是我的token
所以我怀疑我没有正确设置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;
}
extractedToken 签名来自与此处生成的不同的 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);
您正在根据另一个 public 密钥(不是最初使用的密钥)验证您的令牌
为了使 JWT 令牌的签名生效,您必须保留原始 RSA 对,然后像这样将 public 密钥放入 JWTConsumer 中:
.setVerificationKey(originalPublicKey)
我有一个看起来像这样的 JWT(我不得不隐藏一些值):
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJ4eHgiLCJpc3MiOiJ4eHgiLCJpYXQiOjE2MTIzNDkwMTEsIm5iZiI6MCwiZXhwIjoxNjEyMzUyNjExLCJhdXRoX3RpbWUiOjE2MTEwNDU5MjgsIm5vbmNlIjoieHh4Iiwic3ViIjoieHh4IiwidXBuIjoieHh4IiwidW5pcXVlX25hbWUiOiJ4eHgiLCJwd2RfdXJsIjoieHh4IiwicHdkX2V4cCI6Inh4eCIsInNpZCI6Inh4eCIsImp0aSI6ImZmYWQ0NjM1LTU3MmItNGUyYi04ZGRhLTAxNmEzNDRlYzY4ZiJ9.nW5xTs6IbEkIFTZ_9PJZBpZAHXqG2HeU6y0XJwmQZiM
或者简单地说:
{
"typ": "JWT",
"alg": "RS256",
"x5t": "8Q3reRBv6jj6FyxBo5phA1yKzYg",
"kid": "8Q3reRBv6jj6FyxBo5phA1yKzYg"
}
和
{
"aud": "xxx",
"iss": "xxx",
"iat": 0,
"nbf": 0,
"exp": 1611049528,
"auth_time": 1611045928,
"nonce": "xxx",
"sub": "xxx",
"upn": "xxx",
"unique_name": "xxx",
"pwd_url": "xxx",
"pwd_exp": "xxx",
"sid": "xxx"
}
这是我为了验证而写的代码(从网站上的例子开始)。
@SneakyThrows
public boolean isValid(String extractedToken) {
log.info("Validating JWT");
// Generate an RSA key pair, which will be used for signing and verification of the JWT, wrapped in a JWK
RsaJsonWebKey rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
// Give the JWK a Key ID (kid), which is just the polite thing to do
rsaJsonWebKey.setKeyId("8Q3reRBv6jj6FyxBo5phA1yKzYg");
rsaJsonWebKey.setX509CertificateSha256Thumbprint("8Q3reRBv6jj6FyxBo5phA1yKzYg");
// 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()
.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(); // 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;
}
我的 objective 目前只验证签名而不验证其他密钥对值。
但是,当我 运行 代码时,我得到:
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.....]
而“->”右边的token确实是我的token
所以我怀疑我没有正确设置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;
}
extractedToken 签名来自与此处生成的不同的 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);
您正在根据另一个 public 密钥(不是最初使用的密钥)验证您的令牌
为了使 JWT 令牌的签名生效,您必须保留原始 RSA 对,然后像这样将 public 密钥放入 JWTConsumer 中:
.setVerificationKey(originalPublicKey)