使用ES512在Postman中创建的JWT验证不稳定

问题描述 投票:0回答:1

我使用以下方法在 Postman 中创建并签署 JWT (ES512)(前脚本):

var CryptoJS  = require("crypto-js");
var navigator = {};
var window    = {};

eval(pm.globals.get("jsrsasign-js"));

let issuerId   = pm.environment.get("issuerId");
let audienceId = pm.environment.get("audienceId");
let privateKey = pm.environment.get("privateKey");

let body     = pm.request.body.raw;    
let bodyHash = calculateHash(body);

const pl     = payload(issuerId, audienceId, bodyHash)
const jwt    = signJwt(pl, privateKey)

pm.collectionVariables.set("bearerToken", jwt);

function calculateHash(message) {
  const hash = CryptoJS.SHA256(message??'');
  return hash.toString(CryptoJS.enc.Base64);
}

function payload(iss, aud, bodyHash) {
  return {
    "iss": iss,
    "aud": aud,
    "sub": "api-request",
    "rbh": bodyHash,
    "exp": Math.round(Date.now() / 1000) + 59,
  };
}

function signJwt(payload, privateKey) {
  const alg      = 'ES512';
  const header   = {"alg":alg};
  const sHeader  = JSON.stringify(header);
  const sPayload = JSON.stringify(payload);
  return KJUR.jws.JWS.sign('ES512', sHeader, sPayload, privateKey);
}

私钥生成:

openssl ecparam -name secp521r1 -genkey -noout -out private.pem

公钥:

openssl ec -in private.pem -pubin -outform PEM -out public.pem

服务器使用:https://mvnrepository.com/artifact/com.nimbusds/nimbus-jose-jwt/9.40来验证JWT:

ECDSAVerifier(parseFromPEMEncodedObjects(publicKey).toECKey())

Jwt验证相当不稳定。有时成功(80%),有时失败(20%)。
原因是有时jws签名长度是130字节(不行),应该是132,有时是132。

好的智威汤逊:

eyJhbGciOiJFUzUxMiJ9.eyJpc3MiOiJhZTA3YmE1My0xOTYwLTRiMDYtYTYyZi01ZjdkYjRkYWM3MGYiLCJhdWQiOiJkMF9ha0IyNWJZaDNtI1ByekVmPHVLMXZRTGp7Q3hweUdIUHRMWkwpIiwic3ViIjoib3BlbmFwaS1yZXF1ZXN0IiwicmJoIjoiY2FyZ3VqY3lINHJiSDl0U3BxVWo4dFpkZmM0eHUyL1dvOGZJbzJYek1Nbz0iLCJleHAiOjE3MjI3NDc4NTV9.AJTc6e_4ymPhtQPSx6XoeYyOScFIYf5axTrSTXz2rtXuH9KZAFZBuoTMD35siR7-MIpRpIk2QslSNvU4A7KQSUVFAeH0Mb7yfwC2bN5ncJhWxg6j5dh6sJfPwLi3buGuw9i_TViF6mnLs8WClzXw8NqFBoNSnHexQPkWba8J-trAxoKo

不好智威汤逊:

eyJhbGciOiJFUzUxMiJ9.eyJpc3MiOiJhZTA3YmE1My0xOTYwLTRiMDYtYTYyZi01ZjdkYjRkYWM3MGYiLCJhdWQiOiJkMF9ha0IyNWJZaDNtI1ByekVmPHVLMXZRTGp7Q3hweUdIUHRMWkwpIiwic3ViIjoib3BlbmFwaS1yZXF1ZXN0IiwicmJoIjoiY2FyZ3VqY3lINHJiSDl0U3BxVWo4dFpkZmM0eHUyL1dvOGZJbzJYek1Nbz0iLCJleHAiOjE3MjI3NDc5MTZ9.gcJl3McLyAML3nfbb-ZXXtvfP-TRm0rjpEYX7iiagiFGSNoMi-8yu7jre_QTB0SW2_POrp4OcGg0QY2EeRCf6JnuApCmFTThF3qiwQ82HLS4OjNcHJkKRjxsDVC3aWhcyd7N9N30pWK5vzPCnaxAy1_3u0mHbByY_HMROuixXF07hw

公钥(PEM格式):

-----BEGIN PUBLIC KEY-----
MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBm3GiQ0HmPdCyeuy7yWnxjwlQnjn9
dytNIqD/nsuhX1SVuDFH2iD12SM+csYrqLAwZKP4Y3BNIH+5QUFgpbCBwEEBu3w6
FeqM/5rJvxjG2/YJU2p1aUGed8Br/bZVEFVN6ohWgFGrC/hXxYuxVVwIwuzXnd8N
bXCVGZZeVKM5YrbcZQM=
-----END PUBLIC KEY-----

如果我使用java客户端(nimbus-jose-jwt)创建并签署JWT,验证是稳定的。

我是否错误地使用了 jsrsasign 库?

还有另一个 JavaScript 库:https://github.com/panva/jose
不幸的是,我无法将其导入 Postman。

jwt postman elliptic-curve
1个回答
0
投票

正如评论中已经怀疑的那样,jsrsasign库在将ECDSA签名转换为P1363格式时似乎有一个错误,至少对于曲线P-521来说是这样。在 P1363 格式中,r 和 s 值表示为无符号、大端和串联。对于 P-521,r 和 s 的大小均为 66 字节。如果值较短,则它会变为 66 字节并带有前导 0x00 值,以便签名具有 132 字节的恒定长度。这是必要的,以便可以清楚地识别 r 和 s 值。

对于发布的 130 字节签名,r 和 s 值都缺少填充,即

r-length/s-length = 65bytes/65bytes
,可以轻松检查签名是否无效(空格仅用于显示目的):

Base64url: -GJ6N27KEqRKHgmdyRIApgN6861cFbAXtksTPv3GVDVV-80Jjm266tiQqh3ORfLt_ucijNoKlvdZ8b90H1nb7SQSj3Mo0JZT8-Ht6fP9DA13LsWdTQuX7TTIUEqPegAI9lCmgZMFpbNeGqVarsOfgSIrpFvp7rFCdo7aFM7OaShGKw
hex:       f8627a376eca12a44a1e099dc91200a6037af3ad5c15b017b64b133efdc6543555fbcd098e6dbaead890aa1dce45f2edfee7228cda0a96f759f1bf741f59dbed24 128f7328d09653f3e1ede9f3fd0c0d772ec59d4d0b97ed34c8504a8f7a0008f650a6819305a5b35e1aa55aaec39f81222ba45be9eeb142768eda14cece6928462b

手动修复:

hex:       00f8627a376eca12a44a1e099dc91200a6037af3ad5c15b017b64b133efdc6543555fbcd098e6dbaead890aa1dce45f2edfee7228cda0a96f759f1bf741f59dbed24 00128f7328d09653f3e1ede9f3fd0c0d772ec59d4d0b97ed34c8504a8f7a0008f650a6819305a5b35e1aa55aaec39f81222ba45be9eeb142768eda14cece6928462b
Base64url: APhiejduyhKkSh4JnckSAKYDevOtXBWwF7ZLEz79xlQ1VfvNCY5tuurYkKodzkXy7f7nIozaCpb3WfG_dB9Z2-0kABKPcyjQllPz4e3p8_0MDXcuxZ1NC5ftNMhQSo96AAj2UKaBkwWls14apVquw5-BIiukW-nusUJ2jtoUzs5pKEYr

则固定令牌为:

eyJhbGciOiJFUzUxMiJ9.eyJpc3MiOiJhZTA3YmE1My0xOTYwLTRiMDYtYTYyZi01ZjdkYjRkYWM3MGYiLCJhdWQiOiJkMF9ha0IyNWJZaDNtI1ByekVmPHVLMXZRTGp7Q3hweUdIUHRMWkwpIiwic3ViIjoib3BlbmFwaS1yZXF1ZXN0IiwicmJoIjoiY2FyZ3VqY3lINHJiSDl0U3BxVWo4dFpkZmM0eHUyL1dvOGZJbzJYek1Nbz0iLCJleHAiOjE3MjI4NTAxMzJ9.APhiejduyhKkSh4JnckSAKYDevOtXBWwF7ZLEz79xlQ1VfvNCY5tuurYkKodzkXy7f7nIozaCpb3WfG_dB9Z2-0kABKPcyjQllPz4e3p8_0MDXcuxZ1NC5ftNMhQSo96AAj2UKaBkwWls14apVquw5-BIiukW-nusUJ2jtoUzs5pKEYr

此令牌现在有效,并将由任何 JWS 兼容库验证是否有效,请参阅例如在线访问 jwt.io.


但是,此修复并非 100% 可靠。理论上,不同长度的 r 和 s 值也是可能的(例如

r-length/s-length = 66bytes/64bytes
)。由于缺乏明确性,无法先验地确定哪种情况适用,因此必须进行尝试。然而,
r-length/s-length = 65bytes/65bytes
签名的假设是合理的,因为较短签名的概率较小(正如以下估计使得合理的那样:前导 0x00 值的概率:1/256,两个前导 0x00 值的概率:1/256^ 2 ...).
除此之外,其他(无效)签名大小,例如 131 字节,理论上也是可以想象的。这里只能尝试一下哪些部分需要填充。

这些理论上可能的情况是否真的发生,最终取决于错误,如果不进一步分析代码就无法回答。在我的测试中,我只在 1000 个生成的签名中发现长度为 130 和 132 字节的签名。然而,我在签名时偶尔收到一条错误消息unknown ECDSA sig s length error,这表明该错误更加复杂。

在我看来,最合理的解决方案是切换到另一个库(至少对于P-521)。如果您找不到适合您环境的解决方案,则可以使用上述手动修复方法。

© www.soinside.com 2019 - 2024. All rights reserved.