【问题标题】:How to verify a webhook with Ruby?如何使用 Ruby 验证 webhook?
【发布时间】:2019-09-09 12:20:09
【问题描述】:

我正在使用 Ruby 2.5.5 和 Rails 5.2。我正在尝试验证 Paddle webhook,但无法进行验证。我尝试了各种方法,这里是其中一种。我将在我的控制台上完成它:

这是数据:

data = {
"alert_id"=>"1", 
"alert_name"=>"alert_created", 
"cancel_url"=>"https://...", 
"checkout_id"=>"1", 
"user_id"=>"1", 
"p_signature"=>"fwWXqR9C..."
} 

public_key = "-----BEGIN PUBLIC KEY-----
FAKEdfsdfsdfsdfsdfsdfsdf23423423423423KCAgEAnyBxU0cUW9NGQU1Ur0MD
/M+VxdMsv7PMXsZFAKEKEYlskdfjlskdjflsjlskdj8i7+D9xZT57VhIfv8hInGy
IQFmJRrWxgmHjSW4cKbjg6Qg0NgOEuPdEIixsQZ/AfIar4KNObDul0EXL92B6ru/
...
-----END PUBLIC KEY-----"

然后我执行以下操作:

signature = Base64.decode64(data['p_signature'])

data.delete('p_signature')

data.each {|key, value|data[key] = String(value)}

data_sorted = data.sort_by{|key, value| key}

data_serialized = data_sorted.to_json

digest    = OpenSSL::Digest::SHA1.new

pub_key   = OpenSSL::PKey::RSA.new(public_key)

verified  = pub_key.verify(digest, signature, data_serialized)

最后verified总是false。我究竟做错了什么?我问了一个相关的问题here,但还不能让它工作。

【问题讨论】:

    标签: ruby-on-rails ruby openssl cryptography rsa


    【解决方案1】:

    您已链接到的 paddle 文档中的示例建议在使用 php-serialize gem 进行验证之前将数据序列化为 PHP 格式,这与常规 json 不同:

    # ... data = ...
    data_sorted.to_json
     => "[[\"alert_id\",\"1\"],[\"alert_name\",\"alert_created\"],[\"cancel_url\",\"https://...\"],[\"checkout_id\",\"1\"],[\"user_id\",\"1\"]]"
    PHP.serialize(data_sorted, true)
     => "a:5:{s:8:\"alert_id\";s:1:\"1\";s:10:\"alert_name\";s:13:\"alert_created\";s:10:\"cancel_url\";s:11:\"https://...\";s:11:\"checkout_id\";s:1:\"1\";s:7:\"user_id\";s:1:\"1\";}"
    

    如您所见 - 它给出不同的结果,因此显然签名将不匹配。

    其实,你可以拿他们的整个 ruby​​ 做例子:

    require 'base64'
    require 'php_serialize' # https://github.com/jqr/php-serialize
    require 'openssl'
    
    public_key = '-----BEGIN PUBLIC KEY-----
    MIICIjANBgkqh...'
    
    # 'data' represents all of the POST fields sent with the request.
    signature = Base64.decode64(data['p_signature'])
    data.delete('p_signature')
    data.each {|key, value|data[key] = String(value)}
    data_sorted = data.sort_by{|key, value| key}
    data_serialized = PHP.serialize(data_sorted, true)
    
    digest    = OpenSSL::Digest::SHA1.new
    pub_key   = OpenSSL::PKey::RSA.new(public_key).public_key
    verified  = pub_key.verify(digest, signature, data_serialized)
    

    【讨论】:

    • 您好 Vasfed,谢谢!有没有办法在没有 PHP 的情况下在 Ruby 中做到这一点? PHP.serialize(data_sorted, true)
    • @userden 是的,提到 php_serialize gem 是这样做的
    • 不要被PHP 欺骗——它是那个宝石的红宝石类
    • 感谢 Vasfed!我在使用 Rails 时遇到了问题,不得不在那里进行一些调整,但现在一切正常。谢谢!
    【解决方案2】:

    我无法完全解释,但我认为 false 与您的签名或 data_serialized 参数有关,我使用相同的摘要。另一个区别是在下面的示例中,在键上调用了 sign:

    验证(摘要、签名、数据)

    来自:https://ruby-doc.org/stdlib-2.6.3/libdoc/openssl/rdoc/OpenSSL/PKey/PKey.html

    为了验证字符串签名,必须提供一个 OpenSSL::Digest 实例摘要来重新计算原始数据的消息摘要,也就是一个字符串。如果签名有效,则返回值为 true,否则为 false。如果发生错误,则会引发 PKeyError。摘要实例的任何先前状态都与验证结果无关,摘要实例在操作期间被重置为其初始状态。

    例子:

    data = 'Sign me!'
    digest = OpenSSL::Digest::SHA256.new
    pkey = OpenSSL::PKey::RSA.new(2048)
    signature = pkey.sign(digest, data)
    pub_key = pkey.public_key
    puts pub_key.verify(digest, signature, data) # => true`
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-03-16
      • 2023-01-31
      • 1970-01-01
      • 1970-01-01
      • 2016-06-27
      • 2016-04-21
      • 2016-05-30
      • 2020-08-22
      相关资源
      最近更新 更多