【译】JWT(JSON Web Token) 入门指南 (5)

RSA公钥类似如下:

-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdlatRjRjogo3WojgGHFHYLugdUWAY9iR3fy4arWNA1KoS8kVw33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQsHUfQrSDv+MuSUMAe8jzKE4qW+jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5Do2kQ+X5xK9cipRgEKwIDAQAB -----END PUBLIC KEY-----

这看起来很复杂,但是它仅仅是一个唯一的字串,可以被命令行工具像openssl产生,或者这个网上工具产生。

这个公钥是被公开的,攻击者是可以获取的。

对应于公钥,有一个特定的私钥与之对应:

-----BEGIN RSA PRIVATE KEY----- MIICWwIBAAKBgQDdlatRjRjogo3WojgGHFHYLugdUWAY9iR3fy4arWNA1KoS8kVw33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQsHUfQrSDv+MuSUMAe8jzKE4qW+jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5Do2kQ+X5xK9cipRgEKwIDAQABAoGAD+onAtVye4ic7VR7V50DF9bOnwRwNXrARcDhq9LWNRrRGElESYYTQ6EbatXS3MCyjjX2eMhu/aF5YhXBwkppwxg+EOmXeh+MzL7Zh284OuPbkglAaGhV9bb6/5CpuGb1esyPbYW+Ty2PC0GSZfIXkXs76jXAu9TOBvD0ybc2YlkCQQDywg2R/7t3Q2OE2+yo382CLJdrlSLVROWKwb4tb2PjhY4XAwV8d1vy0RenxTB+K5Mu57uVSTHtrMK0GAtFr833AkEA6avx20OHo61Yela/4k5kQDtjEf1N0LfI+BcWZtxsS3jDM3i1Hp0KSu5rsCPb8acJo5RO26gGVrfAsDcIXKC+bQJAZZ2XIpsitLyPpuiMOvBbzPavd4gY6Z8KWrfYzJoI/Q9FuBo6rKwl4BFoToD7WIUS+hpkagwWiz+6zLoX1dbOZwJACmH5fSSjAkLRi54PKJ8TFUeOP15h9sQzydI8zJU+upvDEKZsZc/UhT/SySDOxQ4G/523Y0sz/OZtSWcol/UMgQJALesy++GdvoIDLfJX5GBQpuFgFenRiRDabxrE9MNUZ2aPFaFp+DyAe+b4nDwuJaW2LURbr8AEZga7oQj0uYxcYw== -----END RSA PRIVATE KEY-----

好消息是攻击者不可能知道这个私钥!

我们要记住这两个秘钥是相关的。那么我们是怎么通过这个算法来产生一个签名的呢?

为什么不仅仅用RSA算法加密载体

我们尝试这样给JWT加签:获取头和载体,然后使用私钥进行加密,然后发送。

接受者接受JWT,然后使用公钥解密,验证结果。

如果解密成功了,并成功获取了JSON载体,是不是就可以认为token是认证服务器产生并加签后的,对吗?

确实是这样,它足够我们验证token的正确性。但是我们不这么做是因为一些实用性的原因。

RSA算法相比哈希算法来说比较慢。对于大的载体,性能是一个阻碍,这是唯一一个原因。

那么,让我们看看HS256算法是如何运用在RSA中的。

实用RSA 和 SHA-256算法加签

实际实用中,我们通过哈希算法把头和载体处理,如:SHA-256。

这一步很快,然后我们获得了一个代表了输入的唯一数据,它比实际数据要小很多。

然后我们使用私钥加密这个哈希后的数据,我们就得到了RS256 签名。

这时我们就可以把它放在token的第三部分,然后发送出去。

接受者怎么验证 RS256签名

接受者接受信息之后,按如下处理:

把头和载体进行 SHA256 哈希

然后使用公钥解密签名,获得签名哈希

比较1、2步的哈希串

如果是一致的,说明JWT确实是认证服务器创建的。

任何人都可以计算哈希,但是只有认证服务器可以使用私钥加签数据。

到这里你认为还有其他遗漏吗?我们先动手实践怎么验证RS256,稍后再看看这个问题。

手把手验证RS256 JWT签名

我们首先从<jwt.io>上获取一个经RS256签名的JWT:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.EkN-DOsnsuRjRO6BxXemmJDm3HbxrbRzXglbN2S4sOkopdU4IsDxTI8jO19W_A4K8ZPJijNLis4EZsHeY559a4DFOd50_OqgHGuERTqYZyuhtF39yxJPAjUESwxk2J5k_4zM3O-vtd1Ghyo4IbqKKSy6J9mTniYJPenn5-HIirE

我们可以看到这和HS256签名后的JWT没有什么明显的区别,但是它确实是经过RSA私钥加密后的字符串。

我们单独看看头和载体:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

我们只需要使用SHA256进行哈希,然后使用私钥进行加密即可。

加密后的结果就是JWT签名!我们一起使用Node的加密组件来验证吧!无需安装额外的组件,因为node已经自带了。

这个模块自带了和一些其他的签名函数可以用来产生签名。

首先,我们需要把私钥保存在一个文件中,起名private.key.

然后在命令行中,我们运行一下这段代码:

const crypto = require(\'crypto\'); const fs = require(\'fs\'); const sign = crypto.createSign(\'RSA-SHA256\'); // copy / paste here the header and the payload of your JWT only sign.write(\'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9 .eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9\'); sign.end(); var privateKey = fs.readFileSync(\'./private.key\'); console.log(sign.sign(privateKey, \'base64\'));

如果你使用和上面不同的JWT token,你只需要复制前面两部分到write方法里即可。

然后我们得到了下面的结果:

EkN+DOsnsuRjRO6BxXemmJDm3HbxrbRzXglbN2S4sOkopdU4IsDxTI8jO19W/A4K8ZPJijNLis4EZsHeY559a4DFOd50/OqgHGuERTqYZyuhtF39yxJPAjUESwxk2J5k/4zM3O+vtd1Ghyo4IbqKKSy6J9mTniYJPenn5+HIirE=

这和上面的JWT签名完全不一样呢!!但是等待,这里为什么会有斜杠、等号?这是不经过转义是不可能放在url中的。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwfsys.html