来自Ruby的Bash等效MD5函数

时间:2014-09-24 22:39:58

标签: ruby bash base64 md5

我使用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完全相同,并且因为我使用{{1}而没有跟踪换行符}。

更新

来自bash的输出:

echo -n

ruby​​的输出:

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==

我分别从运行bash脚本和rails服务器的终端中选择并复制了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== $query字符串。从这个意义上说,它们两者完全相同,我怎样才能进一步缩小这个问题呢?

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

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

body

并且输出: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 这很好!这就是Ruby方面输出的内容。好的。

但是当我使用上面粘贴的确切文字命令运行我的shell脚本时,它会输出:/DdffT+N+sZZjaTC5TJNcg==这与我最初开始使用的内容-md5相同(脚本最初发布)。

当我运行Lsb/vxJKHUxyRAqMhOMeOw==时,我得到echo $LANG

更新3:

我用:

运行shell脚本
en_US.UTF-8

当我回显出这个命令时输出sh script.sh

Lsb/vxJKHUxyRAqMhOMeOw==

更新4:

奇怪!所以,我一直在使用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 运行(上面发布的shell脚本),这显示了md5结果与我在Ruby中看到的结果不同。现在,我sh script.sh编写了脚本并直接运行它:chmod +x现在我得到了正确的md5 !!

但是,现在ApiAuth中的script.sh方法仍然返回false:'(

2 个答案:

答案 0 :(得分:0)

所以,我可能试图以不同的方式攻击它。我猜测你得到不同结果的原因是因为你的输出方法因输入不同而不同。突然出现的一件事是你正在创建一个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==

答案 1 :(得分:0)

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

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

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