jwt - 如何使用在线工具手动验证 JWT 签名

标签 jwt

据我所知,验证 JWT 签名是一个直接的过程。但是当我使用一些在线工具为我执行此操作时,它不匹配。如何在不使用 JWT 库的情况下手动验证 JWT 签名?我需要一种快速方法(使用可用的在线工具)来演示这是如何完成的。

我使用以下信息在 https://jwt.io/#debugger-io 上创建了我的 JWT:

  • 算法: HS256
  • secret : hONPMX3tHWIp9jwLDtoCUwFAtH0RwSK6
  • 标题:

    {
    "alg": "HS256",
    “类型”:“JWT”
    }
  • 有效载荷:

    {
    "sub": "1234567890",
    "name": "约翰·多伊",
    “iat”:1516239022
    }
  • 验证签名(部分):
  • Secret 值更改为
  • 以上
  • “已检查”的 secret base64 编码(无论是否检查,仍然得到不同的值)

  • JWT:

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.wDs8hd8hdkwd8hdkwdkwdkwd8hdkwdkwd82


    手动 JWT 签名验证尝试:

    使用 base64UrlEncode 计算器 ( http://www.simplycalc.com/base64url-encode.phphttps://www.base64encode.org/ )

    如果我:(不是网站上的实际值(value),经过修改以显示工具最终将为我构建什么)

    base64UrlEncode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9") + "."+ base64UrlEncode("eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ")

    我得到:

    ZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5.ZXlKemRXSWlPaUl4TWpNME5UWTNPRGt3SWl3aWJtRnRaU0k2SWtwdmFHNGdSRzlsSWl3RFElMFXRqEl3RFElMFXRqE

    NOTE: there's some confusion on my part if I should be encoding the already encoded values, or use the already encoded values as-is.

    (i.e. using base64UrlEncode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9") + "." + base64UrlEncode("eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ") vs "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ").

    Regardless on which I should do, the end result still doesn't match the signature. I'm leaning towards that I should NOT re-encode the encoded value, whether that's true or not.



    然后使用 HMAC Generator 计算器 ( https://codebeautify.org/hmac-generatorhttps://www.freeformatter.com/hmac-generator.html#ad-output )

    (不是网站上的实际值(value),经过修改以显示工具最终将为我构建什么)

    HMACSHA256(
    "ZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5.ZXlKemRXSWlPaUl4TWpNME5UWTNPRGt3SWl3aWJtRnRaU0k2SWtwdmFHNGdSRzlsSWlTVl3RQTamTamrFhWQ0o5.ZXlKemRXSWlPaUl4TWpNME5UWTNPRGt3SWl3aWJtRnRaU0k2SWtwdm
    "hONPMX3tHWIp9jwLDtoCUwFAtH0RwSK6"
    )

    这让我:

    a2de322575675ba19ec272e83634755d4c3c2cd74e9e23c8e4c45e1683536e01

    这与 JWT 的签名部分不匹配:
    wDQ2mU5n89f2HsHm1dluHGNebbXeNr748yJ9kUNDNCAM != a2de322575675ba19ec272e83634755d4c3c2cd74e9e23c8e4c45e1683536e01
    目的:

    我需要确认这一点的原因是为了证明能够验证 JWT 未被篡改,而无需解码 JWT

    我的客户端 Web 界面不需要解码 JWT ,因此他们不需要安装 jwt 包 来执行此操作。在存储 JWT 以供将来的 API 调用之前,他们只需要进行简单的验证以确认 JWT 没有被篡改(尽管可能性不大)。

    最佳答案

    这完全是格式和编码的问题。

    https://jwt.io你根据你的输入值和 secret 得到这个 token :

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.3pIaKksiX9Zv8Jg-hWbrD24VhL36hBIFaNpA4fVx29M
    

    我们要证明签名:
    3pIaKksiX9Zv8Jg-hWbrD24VhL36hBIFaNpA4fVx29M
    

    是正确的。

    签名是 Base64url 编码的 HMAC-SHA256 哈希。
    (如 RFC7515 中所述)

    当您使用 online HMAC generator计算哈希值
    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
    

    带着 secret
    hONPMX3tHWIp9jwLDtoCUwFAtH0RwSK6
    

    你得到
    de921a2a4b225fd66ff0983e8566eb0f6e1584bdfa84120568da40e1f571dbd3
    

    结果,这是一个 HMAC-SHA256 值,但不是 Base64url 编码的。这个散列是一个大数的十六进制字符串表示。

    将其与来自 https://jwt.io 的值进行比较您需要将值从它的十六进制字符串表示形式转换回一个数字,然后 Base64url 对其进行编码。

    以下脚本正在执行此操作,并且还使用 crypto-js 来计算它自己的哈希值。这也可以是您在没有 JWT 库的情况下进行验证的一种方式。

    var CryptoJS = require("crypto-js");
    
    // the input values
    var base64Header = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";
    var base64Payload = "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ";
    var secret = "hONPMX3tHWIp9jwLDtoCUwFAtH0RwSK6";
    
    // two hashes from different online tools
    var signatureJWTIO = "3pIaKksiX9Zv8Jg-hWbrD24VhL36hBIFaNpA4fVx29M";
    var onlineCaluclatedHS256 =  "de921a2a4b225fd66ff0983e8566eb0f6e1584bdfa84120568da40e1f571dbd3";
    
    // hash calculation with Crypto-JS. 
    // The two replace expressions convert Base64 to Base64url format by replacing 
    // '+' with '-', '/' with '_' and stripping the '=' padding
    var base64Signature = CryptoJS.HmacSHA256(base64Header + "." + base64Payload , secret).toString(CryptoJS.enc.Base64).replace(/\+/g,'-').replace(/\//g,'_').replace(/\=+$/m,'');
    
    // converting the online calculated value to Base64 representation
    var base64hash = new Buffer.from(onlineCaluclatedHS256, 'hex').toString('base64').replace(/\//g,'_').replace(/\+/g,'-').replace(/\=+$/m,'')
    
    
    // the results:
    console.log("Signature from JWT.IO             : " + signatureJWTIO);
    console.log("NodeJS calculated hash            : " + base64Signature);
    console.log("online calulated hash (converted) : " + base64hash);
    

    结果是:
    Signature from JWT.IO             : 3pIaKksiX9Zv8Jg-hWbrD24VhL36hBIFaNpA4fVx29M
    
    NodeJS calculated hash            : 3pIaKksiX9Zv8Jg-hWbrD24VhL36hBIFaNpA4fVx29M
    
    online calulated hash (converted) : 3pIaKksiX9Zv8Jg-hWbrD24VhL36hBIFaNpA4fVx29M
    

    一模一样!

    结论:

    不同在线工具计算出的值都是正确的,但由于格式和编码不同,不能直接比较。
    如上所示的小脚本可能是更好的解决方案。

    关于jwt - 如何使用在线工具手动验证 JWT 签名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50121763/

    相关文章:

    javascript - 在 PHP 可读的 JavaScript 中创建 PEM key 对

    authentication - jwt上的私有(private)和公共(public) claim 有什么区别

    asp.net - 使用 Auth0 在 ASP.NET 中按角色/组进行授权

    security - JWT 作为邮件验证 token

    reactjs - 在 SPA 中刷新 JWT 的最佳实践?

    java - 使用 RS256 生成 token ,Jwts.builder().signWith() 产生无效签名

    node.js - 如何使用 node.js 对外部 webapp 的用户进行身份验证?

    c# - Multi-Tenancy Asp.net Core网站中基于参数的JWT认证

    java - 使用 Apple Java 用户验证登录

    javascript - 如何使用 JWT token 作为登录验证?