【问题标题】:How to verify a signed JWT with SubtleCrypto of the Web Crypto API?如何使用 Web Crypto API 的 SubtleCrypto 验证签名的 JWT?
【发布时间】:2019-01-12 20:30:15
【问题描述】:

我正在尝试使用 Web Crypto API 的 SubtleCrypto 接口验证 JWT 的签名。

我的代码不会验证令牌签名,而 JWT.io 上的调试工具会,我不知道为什么。这是我的验证功能:

function verify (jwToken, jwKey) {
  const partialToken = jwToken.split('.').slice(0, 2).join('.')
  const signaturePart = jwToken.split('.')[2]
  const encoder = new TextEncoder()
  return window.crypto.subtle
    .importKey('jwk', jwKey, { 
         name: 'RSASSA-PKCS1-v1_5', 
         hash: { name: 'SHA-256' } 
       }, false, ['verify'])
    .then(publicKey =>
      window.crypto.subtle.verify(
        { name: 'RSASSA-PKCS1-v1_5' },
        publicKey,
        encoder.encode(atob(signaturePart)),
        encoder.encode(partialToken)
      ).then(isValid => alert(isValid ? 'Valid token' : 'Invalid token'))
    )
}

我希望该代码能够正常工作并为正确签名的 JWT 提供正面验证。相反,示例代码无法验证签名令牌。对我来说,该示例在 Chrome 71 中失败。

我还使用 RFC 7520 中的示例数据设置了一些 tests

【问题讨论】:

  • signaturePartpartialToken 是 base64url 编码的。在使用verify之前你需要解码它们
  • @pedrofb 你说得对,signaturePart 需要被解码。然而,partialToken(JWS 签名输入)不应在 RFC 7515 中的第 5 节中为验证步骤进行解码。
  • 我的错误。无需解码partialToken。签名是 base64url 编码的,与 base64 略有不同。在应用atob 之前,您需要将+ 替换为-,并将/ 替换为_
  • 是的,使用btoa() 真的很糟糕,因为它无法处理输出中的非字母字符,因此函数名称中二进制的“b”并不是真的。我改用 rfc4648.js 并且效果很好。 :)
  • 您可能想用解决方案来回答您自己的问题以备将来使用

标签: javascript jwt webcrypto-api


【解决方案1】:

要使用 SubtleCrypto 验证 JWS,您需要小心地在二进制和 base64url 表示之间正确编码和解码数据。不幸的是,btoa()atob() 的浏览器中的 standard implementation 很难使用,因为它们使用“一个 Unicode 字符串,只包含 U+0000 到 U+00FF 范围内的字符,每个字符代表一个值为 0x00 的二进制字节分别为0xFF”作为二进制数据的表示。

在 Javascript 中表示二进制数据的更好解决方案是使用 ES6 对象 TypedArray 并使用 Javascript 库(或自己编写编码器)将它们转换为支持 RFC 4648 的 base64url。

旁注:base64 和 base64url 的区别在于标准中为值 62 和 63 选择的字符,base64 将它们编码为 +/,而 base64url 编码为 -_

JavaScript 中此类库的一个示例是 rfc4648.js

import { base64url } from 'rfc4648'

async function verify (jwsObject, jwKey) {
  const jwsSigningInput = jwsObject.split('.').slice(0, 2).join('.')
  const jwsSignature = jwsObject.split('.')[2]
  return window.crypto.subtle
    .importKey('jwk', jwKey, { 
         name: 'RSASSA-PKCS1-v1_5', 
         hash: { name: 'SHA-256' } 
       }, false, ['verify'])
    .then(key=>
      window.crypto.subtle.verify(
        { name: 'RSASSA-PKCS1-v1_5' },
        key,
        base64url.parse(jwsSignature, { loose: true }),
        new TextEncoder().encode(jwsSigningInput))
      ).then(isValid => alert(isValid ? 'Valid token' : 'Invalid token'))
    )
}

【讨论】:

  • 像魅力一样工作。非常感谢!
猜你喜欢
  • 1970-01-01
  • 2014-01-15
  • 2018-09-16
  • 1970-01-01
  • 2014-05-29
  • 2019-11-14
  • 2016-07-10
  • 2019-05-20
  • 2020-11-26
相关资源
最近更新 更多