如何签署 JWT?

Posted

技术标签:

【中文标题】如何签署 JWT?【英文标题】:How to sign JWT? 【发布时间】:2015-04-03 11:49:03 【问题描述】:

我正在尝试保护 Sinatra API。

我正在使用ruby-jwt 创建 JWT,但我不知道具体用什么签名。

我正在尝试使用用户的 BCrypt password_digest,但每次调用 password_digest 时它都会更改,导致我去验证时签名无效。

【问题讨论】:

@joelparkerhenderson 我评论了你的答案,但我从来没有得到答案。虽然是的,但它帮助了我。我会标记它,但请回答。 @joelparkerhenderson 我对答案投了赞成票,但没有接受。 @joelparkerhenderson 我现在才看到,谢谢。这是一个很好的答案。我在网上找不到任何关于这方面的好方法的信息,而且它真的很简单。 【参考方案1】:

对于 RS256 公钥和私钥策略,您可以使用 Ruby OpenSSL lib:

生成密钥:

key = OpenSSL::PKey::RSA.new 2048

open 'private_key.pem', 'w' do |io| io.write key.to_pem end
open 'public_key.pem', 'w' do |io| io.write key.public_key.to_pem end

从 .pem 文件加载密钥以签署令牌:

priv_key = OpenSSL::PKey::RSA.new File.read 'private_key.pem'
token = JWT.encode payload, priv_key, 'RS256'

从 .pem 文件加载密钥以验证令牌(为此创建一个中间件):

      begin
        # env.fetch gets http header
        bearer = env.fetch('HTTP_AUTHORIZATION').slice(7..-1)
        pub_key = OpenSSL::PKey::RSA.new File.read 'public_key.pem'
        payload = JWT.decode bearer, pub_key, true,  algorithm: 'RS256'
        
        # access your payload here
  
        @app.call env

      rescue JWT::ExpiredSignature
        [403,  'Content-Type' => 'text/plain' , ['The token has expired.']]
      rescue JWT::DecodeError
        [401,  'Content-Type' => 'text/plain' , ['A token must be passed.']]
      rescue JWT::InvalidIssuerError
        [403,  'Content-Type' => 'text/plain' , ['The token does not have a valid issuer.']]
      rescue JWT::InvalidIatError
        [403,  'Content-Type' => 'text/plain' , ['The token does not have a valid "issued at" time.']]
      end

要在 .env 中使用 RSA 密钥而不是加载文件,您需要使用 gem 'dotenv' 并使用换行符 '\n' 将密钥作为单行变量导入。检查此question 以了解如何操作。示例:

PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nmineminemineminemine\nmineminemineminemine\nmineminemine...\n-----END PUBLIC KEY-----\n"

作为.envPUBLIC_KEY变量,加载key会变成这样:

key = OpenSSL::PKey::RSA.new ENV['PUBLIC_KEY']

【讨论】:

【参考方案2】:

根据wikipedia,密码学中使用的密钥基本上就是这样,一把打开锁的钥匙。密钥应一致且可靠,但不易复制,就像您在家中使用的密钥一样。

正如this answer 中所述,密钥应该是随机生成的。但是,您仍然希望保留密钥以在整个应用程序中使用。通过使用来自 bcrypt 的密码摘要,您实际上是在使用从基本密钥(密码)派生的散列密钥。因为散列是随机的,所以正如你所说,这不是一个可靠的密钥。

使用SecureRandom.hex(64) 的上一个答案是创建初始基本应用程序密钥的好方法。但是,在生产系统中,您应该将其作为配置变量并将其存储,以便在应用程序的多次运行(例如,在服务器重新启动后,您不应该使所有用户的 JWT 无效)或在多个分布式服务器。 This article 给出了一个从 Rails 环境变量中提取密钥的示例。

【讨论】:

【参考方案3】:

使用任何类型的应用程序密钥,而不是用户的 bcrypt 密码摘要。

例如,使用 dot env gem 和一个 .env 文件,其条目如下:

JWT_KEY=YOURSIGNINGKEYGOESHERE

我个人使用简单的随机十六进制字符串生成密钥:

SecureRandom.hex(64)

十六进制字符串仅包含 0-9 和 a-f,因此该字符串是 URL 安全的。

【讨论】:

好的,但是我应该用什么来生成实际的密钥?我在想Base64.encode64(SecureRandom.random_bytes(128))之类的东西。这样可以吗?它应该更长还是不同?

以上是关于如何签署 JWT?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 CryptoJS 中使用私钥 (pem) 签署 JWT?

使用 x509 证书签署 JWT 令牌时遇到问题

签署令牌如何工作? (无效签名错误)

如何在 python 中使用 RSA SHA-256 签署令牌?

Azure KeyVault - 签署 JWT 令牌

如何使用 jwt-auth 在 Laravel 中正确设置 JWT 机密?