签署JWT-我做错了吗?

Rob*_*bok 3 javascript jwt

我正在尝试为教育目的而用JavaScript制作JWT生成器。有一个jwt.io工具可以创建和/或验证JWT。

我正在努力使我的结果与验证器的结果匹配。问题是签名。

这是我的代码:

function base64url(input) {
    return btoa(typeof input === 'string' ? input : JSON.stringify(input))
        .replace(/=+$/, '')
        .replace(/\+/g, '-')
        .replace(/\//g, '_');
}

const JWT = {
    encode(header, payload, secret) {
        const unsigned = [base64url(header), base64url(payload)].join('.');

        return [unsigned, base64url(sha256.hmac(secret, unsigned))].join('.');
    }
};
Run Code Online (Sandbox Code Playgroud)

为了加密HMAC SHA256,我将js-sha256库与sha256.hmac(key, value)原型一起使用。我将其与在线工具进行了比较,并且效果很好。

现在,我用以下代码对其进行测试:

const jwt = JWT.encode(
    {
        alg: 'HS256',
        typ: 'JWT'
    },
    123,
    'xxx'
);
Run Code Online (Sandbox Code Playgroud)

我得到的结果是:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.MTIz.NzhlNTFmYzUxOGQ2YjNlZDFiOTM0ZGRhOTUwNDFmMzEwMzdlNmZkZWRhNGFlMjdlNDU3ZTZQNY

另一方面,jwt.io的结果是:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.MTIz.eOUfxRjWs-0bk03alQQfMQN-b97aSuJ-RX5qXatFwbE

如您所见,JWT的三个大块中的两个在我的结果和jwt.io结果中是相同的。签名是不同的,如果您问我,它生成的签名非常短。该工具还将我自己的JWT标记为无效。

我检查了在线HMAC SHA256生成器,看起来我的代码创建了有效的签名,因此:

base64url(sha256.hmac('xxx', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.MTIz')) ===
'NzhlNTFmYzUxOGQ2YjNlZDFiOTM0ZGRhOTUwNDFmMzEwMzdlNmZkZWRhNGFlMjdlNDU3ZTZhNWRhYjQ1YzFiMQ'
Run Code Online (Sandbox Code Playgroud)

被jwt.io刚刚打破或者它做一些其他的方式?

jps*_*jps 5

I wouldn't say you're doing it wrong, but you missed a small but important detail. The result from jwt.io is correct and the hash you calculate is also correct. But the signature you create with your hash is not correct.

您使用的哈希值sha256.hmac(secret, unsigned)是一个大数字,但是函数的返回值是该大数字的十六进制字符串表示形式。对于签名,您需要base64url编码原始数字,而不是其字符串表示形式。

我修改了代码,以便将哈希值直接编码为base64url(node.js版本):

const JWT = {
    encode(header, payload, secret) {
        const unsigned = [base64url(header), base64url(payload)].join('.');
        const hash  = sha256.hmac(secret, unsigned);
        console.log(hash);        
        var signature = new Buffer.from(hash, 'hex').toString('base64').replace(/\+/g,'-').replace(/\=+$/m,'');

        return [unsigned, signature].join('.');
    }
};
Run Code Online (Sandbox Code Playgroud)

或者,如果您不使用node.js,则可以改用它(如Robo Robok的建议):

const JWT = {
    encode(header, payload, secret) {
        const unsigned = [base64url(header), base64url(payload)].join('.');

        return [unsigned, base64url(sha256.hmac(secret, unsigned).replace(/\w{2}/g, byte => String.fromCharCode(parseInt(byte, 16))))].join('.');
    }
};
Run Code Online (Sandbox Code Playgroud)

结果是一个令牌,与使用jwt.io创建的令牌相同:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.MTIz.eOUfxRjWs-0bk03alQQfMQN-b97aSuJ-RX5qXatFwbE

另请参阅此处的答案,其中解释了比较不同工具的结果的步骤。