How to manually validate a JWT signature using online tools

后端 未结 2 1181
星月不相逢
星月不相逢 2020-12-01 19:12

From what I can understand, it\'s a straight forward process to validate a JWT signature. But when I use some online tools to do this for me, it doesn\'t match.

2条回答
  •  野趣味
    野趣味 (楼主)
    2020-12-01 19:42

    It's all a matter of formats and encoding.

    On https://jwt.io you get this token based on your input values and secret:

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.3pIaKksiX9Zv8Jg-hWbrD24VhL36hBIFaNpA4fVx29M
    

    We want to prove that the signature:

    3pIaKksiX9Zv8Jg-hWbrD24VhL36hBIFaNpA4fVx29M
    

    is correct.

    The signature is a HMAC-SHA256 hash that is Base64url encoded. (as described in RFC7515)

    When you use the online HMAC generator to calculate a hash for

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
    

    with the secret

    hONPMX3tHWIp9jwLDtoCUwFAtH0RwSK6
    

    you get

    de921a2a4b225fd66ff0983e8566eb0f6e1584bdfa84120568da40e1f571dbd3
    

    as result, which is a HMAC-SHA256 value, but not Base64url encoded. This hash is a hexadecimal string representation of a large number.

    To compare it with the value from https://jwt.io you need to convert the value from it's hexadecimal string representation back to a number and Base64url encode it.

    The following script is doing that and also uses crypto-js to calculate it's own hash. This can also be a way for you to verify without JWT libraries.

    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);
    

    The results are:

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

    identical!

    Conclusion:

    The values calculated by the different online tools are all correct but not directly comparable due to different formats and encodings. A little script as shown above might be a better solution.

提交回复
热议问题