我正在使用 Dart JsonWebToken 库 https://pub.dev/packages/dart_jsonwebtoken 为 OAuth2 客户端凭证授予生成客户端断言 JWT。
JWT 似乎正确生成,并根据 JWT.IO 正确签名,并使用公钥和私钥进行测试。
但是,当在 client_assertion 字段中使用客户端凭据授予提交时,它会返回无效的令牌签名。
错误:
“invalid_client”“无效的令牌签名”
我已经使用 KJUR 库与邮递员进行了测试,并且在请求中使用了相同的密钥、声明和算法,它有效。因此,我呼吁社区提供帮助,为我指明正确的方向。
代码示例:(Dart - 无效签名)
Future<String> _generateJWT() async {
final current_time = DateTime.now();
final expiration_time = current_time.add(Duration(minutes: 50));
final issued_time = current_time;
final uuid = Uuid();
final jwt = JWT(
header: Map.from({
'alg': 'PS512',
'kid': 'kid1234546',
'typ': 'JWT',
}),
{
'sub': clientID,
'aud':
'https://provider.com/oauth/v2.0/token',
'iss': clientID,
'exp': (expiration_time.millisecondsSinceEpoch ~/ 1000),
'iat': (issued_time.millisecondsSinceEpoch ~/ 1000) - 120,
'jti': uuid.v4(),
},
);
final token = jwt.sign(
RSAPrivateKey(privateKey),
algorithm: JWTAlgorithm.PS512,
);
return token;
}
代码示例:工作(Postman/KJUR)
const clientAssertionJwt = utils.generateSignedRequestJWT(
KJUR.jws.JWS.sign,
{
alg: 'PS512',
kid,
typ: 'JWT',
},
{
sub: clientId,
aud,
iss: clientId,
exp: Math.floor(Date.now() / 1000) + 3590,
iat: Math.floor(Date.now() / 1000) - 120,
jti: v4(),
},
privateKey,
);
库实用函数
const generateSignedRequestJWT = (sign, header, payload, privateKey) => {
if (!header || !payload || !privateKey) {
throw new Error(
`utils.generateSignedRequestJWT - Missing required argument. Following arguments have been provided: header: ${!!header}, payload: ${!!payload}, privateKey: ${!!privateKey}`,
);
}
try {
// KJUR.jws.JWS.sign
return sign(
header.alg,
JSON.stringify(header),
JSON.stringify(payload),
privateKey,
);
} catch (err) {
console.error(
'utils.generateSignedRequestJWT - Failed to generate signed request JWT',
err,
{
header,
payload,
privateKeyDefined: !!privateKey,
},
);
return null;
}
};
这是 Dart 库中的一个错误。除了PS512之外,PS384也不能使用。另一方面,PS256 可以工作。
原因是该库对 PSS 使用 32 字节的固定盐长度,请参阅此处。
但是,32 字节对于 PS384 和 PS512 来说是不正确的。 RFC 7518 在章节3.5 使用 RSASSA-PSS 进行数字签名中定义:
盐值的大小与 哈希函数输出。
这意味着对于以 SHA-256 作为摘要的 PS256,盐长度必须为 32 字节,对于以 SHA-384 作为摘要的 PS384,盐长度必须为 48 字节,对于以 SHA-512 作为摘要的 PS512,盐长度必须为 64 字节。因此PS256可以工作,但PS384和PS512不行。
这也可以通过使用 RSA/PSS 显式验证 PS512 签名来进行检查。 PointyCastle 并应用 32 字节的盐长度。则验证成功。
显然您已经提交了问题(#58)。
请注意,使用 KJUR 生成的签名令牌较小,因为由于某种原因 KJUR 没有考虑
jti
声明。签名本身(第三部分)大小相同。
您应该删除这部分,因为这只会分散对实际问题的注意力,而是将固定盐长度命名为原因。