如何使用Ruby验证Webhook?

时间:2019-09-09 12:20:09

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

我正在使用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,但仍无法解决。

2 个答案:

答案 0 :(得分:2)

您链接到的桨文档中的示例建议在使用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\";}"

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

实际上,您可以以他们的整个红宝石示例为例:

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)

答案 1 :(得分:0)

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

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

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

  

要验证String签名,必须提供摘要(OpenSSL :: Digest的实例)以重新计算原始数据(也是String)的消息摘要。如果签名有效,则返回值为true,否则为false。如果发生错误,则会引发PKeyError。 Digest实例的任何先前状态与验证结果无关,在操作过程中,digest实例将重置为其初始状态。

示例:

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`