【问题标题】:Bash equivalent MD5 function from Ruby来自 Ruby 的 Bash 等效 MD5 函数
【发布时间】:2023-04-01 01:15:01
【问题描述】:

我正在使用ApiAuth gem 来验证 API 请求。现在我需要编写一个使用 cURL 发送测试请求的 shell 脚本。所以我需要生成 POST 正文的 MD5 并对其进行 base64 编码,以便它与 ApiAuth 在服务器上所做的相匹配:

我的 shell 脚本:

query="{\"document\":{\"recipient_id\":\"$ACCESS_ID\",\"data\":{\"id\":\"$ACCESS_ID\"}},\"vendor_string\":\"test\",\"patient\":{\"document\":{\"recipient_id\":\"$ACCESS_ID\",\"data\":{\"id\":\"$ACCESS_ID\"}}}}"

# need to figure how to get a base64 encoded md5 the same way Ruby does
content_md5=$(echo -n "$query" | openssl md5 -binary | base64)
content_type='application/json'
request_uri="$API_BASE/test"
httpdate=$(date -u +"%a, %_d %b %Y %H:%M:%S GMT")
accept_header='application/vnd.test+json; version=1'

canonical_string="$content_type,$content_md5,$request_uri,$httpdate"
signature=$(echo -n "$canonical_string" | openssl dgst -sha1 -hmac "$SECRET_KEY" -binary | base64)

curl -H "Authorization: APIAuth $ACCESS_ID:$signature"\
     -H "Content-MD5: $content_md5" \
     -H "Date: $httpdate" \
     -H "Accept: $accept_header" \
     -H "Content-type: $content_type" \
     -d $query \
     -v \
     $request_uri

失败的第一件事是将我发送的 Content-MD5 与 ApiAuth 计算的内容 MD5 进行比较:

https://github.com/mgomes/api_auth/blob/master/lib/api_auth/base.rb#L37

def authentic?(request, secret_key)
  return false if secret_key.nil?

  return !md5_mismatch?(request) && signatures_match?(request, secret_key) && !request_too_old?(request)
end

这里md5_mismatch?(request) 方法返回false。它使用这些方法来计算 MD5:

https://github.com/mgomes/api_auth/blob/master/lib/api_auth/request_drivers/action_controller.rb

def calculated_md5
  if @request.env.has_key?('RAW_POST_DATA')
    body = @request.raw_post
  else
    body = ''
  end
  md5_base64digest(body)
end

https://github.com/mgomes/api_auth/blob/master/lib/api_auth/helpers.rb

def b64_encode(string)
  if Base64.respond_to?(:strict_encode64)
    Base64.strict_encode64(string)
  else
    # Fall back to stripping out newlines on Ruby 1.8.
    Base64.encode64(string).gsub(/\n/, '')
  end
end

def md5_base64digest(string)
  if Digest::MD5.respond_to?(:base64digest)
    Digest::MD5.base64digest(string)
  else
    b64_encode(Digest::MD5.digest(string))
  end
end

所以我认为它归结为完全匹配正在发生的事情:

Digest::MD5.base64digest

我的尝试是:

content_md5=$(echo -n "$query" | openssl md5 -binary | base64)

如何使 bash 脚本等效于 ruby​​ 方法?

我尝试过使用和不使用 -binary 标志。

我检查了 bash 中的 $query 与 Ruby 中的 @request.raw_post 完全相同,并且没有尾随换行符,因为我使用的是 echo -n

更新:

bash 的输出:

echo $query
{"document":{"recipient_id":"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o","data":{"id":"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o"}},"vendor_string":"kipusystems","patient":{"document":{"recipient_id":"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o","data":{"id":"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o"}}}}

echo $content_md5
Lsb/vxJKHUxyRAqMhOMeOw==

红宝石的输出:

puts body
{"document":{"recipient_id":"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o","data":{"id":"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o"}},"vendor_string":"kipusystems","patient":{"document":{"recipient_id":"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o","data":{"id":"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o"}}}}

puts md5_base64digest(body)
/DdffT+N+sZZjaTC5TJNcg==

我分别从运行 bash 脚本和 rails 服务器的终端中选择并复制了 $querybody 字符串。从这个意义上说,它们都是完全相同的,我该如何进一步缩小这个问题的范围?

更新 2:可能是字符编码问题?

我将此文字粘贴到 (mac bash) shell 提示符中:

echo -n "{\"document\":{\"recipient_id\":\"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o\",\"data\":{\"id\":\"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o\"}},\"vendor_string\":\"kipusystems\",\"patient\":{\"document\":{\"recipient_id\":\"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o\",\"data\":{\"id\":\"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o\"}}}}" | openssl dgst -md5 -binary | base64

然后输出:/DdffT+N+sZZjaTC5TJNcg== 这很好!这就是 Ruby 端的输出。好酷。

但是,当我使用刚刚在上面粘贴的确切文字命令运行我的 shell 脚本时,它会输出:Lsb/vxJKHUxyRAqMhOMeOw==,这与我最初使用的 content-md5 相同(最初发布的脚本)。

当我运行echo $LANG 时,我得到en_US.UTF-8

更新 3:

我使用以下命令运行 shell 脚本:

sh script.sh

当我回显这个命令时,它会输出Lsb/vxJKHUxyRAqMhOMeOw==

echo -n "{\"document\":{\"recipient_id\":\"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o\",\"data\":{\"id\":\"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o\"}},\"vendor_string\":\"kipusystems\",\"patient\":{\"document\":{\"recipient_id\":\"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o\",\"data\":{\"id\":\"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o\"}}}}" | openssl dgst -md5 -binary | base64

更新 4:

奇怪!所以,我一直在使用sh script.sh 运行(上面发布的 shell 脚本),这向我展示了一个与我在 Ruby 中看到的不同的 md5 结果。现在,我 chmod +x'ed 脚本并直接运行它:script.sh 现在我得到了正确的 md5!!

但是,现在 ApiAuth 中的 signatures_match? 方法仍然返回 false :'(

【问题讨论】:

  • 只是好奇。您是否进行了测试以确保您正在编码的字符串在您的测试双方都是 100% 相同的?我将针对使用 openssl/base64 编码的同一字符串测试字符串的 Ruby Digest::MD5.base64digest,并验证您是否可以首先以这种方式获得相同的结果,以消除输入可能不同的任何可能的测试差异.
  • 顺便说一句,出于好奇,为了我的启迪,您是否有理由明确使用 openssl 创建字符串的 md5 散列而不是使用 md5sum?
  • 有区别吗?我没有安装 md5sum。
  • 是的,正如问题最后一行所解释的,我已经对两边的字符串进行了简单的复制粘贴比较。我相信它们都是 UTF-8,但还没有检查 bash 方面。感谢您的关注,我很难过!
  • 脚本中的echo 是否相同? (如果你使用#!/bin/sh,你可能不是。)试试printf '%s' "$query";它在任何情况下都更便携。

标签: ruby bash base64 md5


【解决方案1】:

所以,我可能会尝试以不同的方式来攻击它。我推测您获得不同结果的原因是因为您的输入不同,因此您获得输出的方法不同。我突然想到的一件事是您正在创建一个 md5 字符串并将其从 openssl 输出为二进制字符串,然后对其进行 base64 编码。我认为使用 md5sum 来获取您想要比较的哈希值会更干净:

$ echo -n 'this is a test' | md5sum -- | cut -d ' ' -f 1
54b0c58c7ce9f2a8b551351102ee0938

irb(main):028:0* Digest::MD5.hexdigest('this is a test')
=> "54b0c58c7ce9f2a8b551351102ee0938"

所以,对于你的情况,我会改变:

content_md5=$(echo -n "$query" | openssl md5 -binary | base64)

content_md5=$(echo -n "$query" | md5sum -- | cut -d ' ' -f 1)

...看看这是否更适合您。这是在黑暗中的初步尝试。

所以使用base64编码:

$ echo -n 'this is a test' | md5sum -- | cut -d ' ' -f 1 | base64
NTRiMGM1OGM3Y2U5ZjJhOGI1NTEzNTExMDJlZTA5MzgK

irb(main):037:0> require 'base64'
=> true
irb(main):038:0> require 'digest'
=> true
irb(main):039:0> Base64.strict_encode64(Digest::MD5.hexdigest('this is a test'))
=> "NTRiMGM1OGM3Y2U5ZjJhOGI1NTEzNTExMDJlZTA5Mzg="

那就更近了。最后一个字节不同,我不是 100% 确定为什么。我正在调查那个。只是想把它放在这里,因为它可能只是通过删除两个输出上的最后一个字节,你就会得到你需要的东西。

更新 -- 我的结果现在匹配 OP 结果更新 #2

irb(main):050:0* Digest::MD5.base64digest('{"document":{"recipient_id":"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o","data":{"id":"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o"}},"vendor_string":"kipusystems","patient":{"document":{"recipient_id":"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o","data":{"id":"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o"}}}}')
=> "/DdffT+N+sZZjaTC5TJNcg=="
irb(main):051:0> 
[1]+  Stopped                 irb
$ echo -n '{"document":{"recipient_id":"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o","data":{"id":"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o"}},"vendor_string":"kipusystems","patient":{"document":{"recipient_id":"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o","data":{"id":"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o"}}}}' | openssl dgst -md5 -binary | base64
/DdffT+N+sZZjaTC5TJNcg==

【讨论】:

  • 就我而言,ApiAuth 没有使用Digest::MD5.hexdigest,它使用的是Digest::MD5.digest,我看到输出字节数,这就是我在bash 端使用-binary 开关的原因。跨度>
  • 对。在我进入你的最后评论之前,我需要指出我的建议是有缺陷的,因为我正在对 content_md5 字符串进行 base64 处理,这与 Ruby 的 Digest::MD5.base64digest 1:1 不匹配所以我我必须调查并修复它。
  • 关于您的评论,如果 MD5.digest 的字节输出是实际字节形式,我不是 100% 清楚。当我检查它时,它不是真正的字节表示,但我不确定 Ruby 是否为了可读性打印了字符串的友好形式。我用 Ruby 编码才几年。
  • 在mac上,我使用的是md5,它应该相当于linux的md5sum。可悲的是,我最初使用它,直到我切换到 openssl 的 md5 实现。你帮助想到了一些事情。感谢您到目前为止的兴趣! :)
  • 啊!苹果电脑!我错过了那个美好的花絮。让我看看我是否可以为你收集更多数据。 md5 不会返回正确的 md5sum 吗?
【解决方案2】:

原来我只需要直接运行我的测试脚本,而不是使用sh 命令。

不确定这是否与使用 sh 运行脚本有关,并且脚本顶部将 #!/bin/bash 声明为 @tripleee 所暗示的。

注意:在 Mac 上使用带有 bash 的 iTerm 2。

【讨论】:

  • sh 在兼容模式下运行 Bash,或者可能是完全不同的程序(在许多 Linux 上,/bin/shDash)。 Bash 手册页有很长但可能并不详尽的list of things which work differentlysh
猜你喜欢
  • 2017-02-26
  • 1970-01-01
  • 2020-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多