28

I'm writing a small ruby program to play with Twitter over OAuth and have yet to find a right way to do the HMAC-SHA1 signature. So far, I messed around with

Base64.encode64(OpenSSL::HMAC.hexdigest(digest, key, stuff)).chomp

But this outputs something that Twitter rejects, not being a valid signature. I actually solved it in the worse way possible, please try not to slap me:

php -r "echo rawurlencode(base64_encode(hash_hmac('sha1', '#{@signature}', '#{llave}', true)));"

This last one actually works and I can go around doing my stuff.

I'd like some tips on how to do actually do this without reverting back to PHP. I'm not much of a fan of libraries while I'm trying to learn a language, so gems are pretty much out of the question.

Thanks!

Scott Arciszewski
  • 30,409
  • 16
  • 85
  • 198
Roberto
  • 1,804
  • 1
  • 29
  • 41
  • Just to know, why don't you use standard ruby-oath and twitter gems? – Nicolas Blanco Nov 03 '10 at 15:42
  • 1
    @slainer68 I was amining to learn the language by "translating" small pieces of a larger production application into ruby. When I become as proficient as I am in PHP then I would have no trouble using gems. – Roberto Oct 20 '11 at 06:21
  • in php you are doing a url-encoding while you aren't doing that in ruby. Try that first and see how it goes. – Phobos Apr 27 '15 at 19:57

2 Answers2

42

The following is equivalent to your PHP code, though I chose not to wrap it in a single line.

I'm using the gem ruby-hmac, because it works with 1.8 as well as Ruby 1.9. If you're exclusively using Ruby 1.9 I believe the standard library package 'digest' has HMAC implemented (but this is missing in the 1.8 version of the package). Make sure to gem install ruby-hmac

require 'rubygems'
require 'base64'
require 'cgi'
require 'hmac-sha1'

key = '1234'
signature = 'abcdef'
hmac = HMAC::SHA1.new(key)
hmac.update(signature)
puts CGI.escape(Base64.encode64("#{hmac.digest}\n"))

# equivalent to:
# php -r "echo rawurlencode(base64_encode(hash_hmac('sha1', 'abcdef', '1234', true)));"

Better yet, use the standard library package OpenSSL (which most Linux and MacOS have out of the box). This code will work on Ruby 1.8 and 1.9:

require 'base64'
require 'cgi'
require 'openssl'

key = '1234'
signature = 'abcdef'
puts CGI.escape(Base64.encode64("#{OpenSSL::HMAC.digest('sha1',key, signature)}\n"))

# equivalent to:
# php -r "echo rawurlencode(base64_encode(hash_hmac('sha1', 'abcdef', '1234', true)));"
Ronen Botzer
  • 6,611
  • 20
  • 36
  • 8
    Also if you want to do the same thing as `hash_hmac('sha1', key, signature, false)` in PHP, then do `OpenSSL::HMAC.hexdigest('sha1', key, signature)` – swrobel Oct 11 '11 at 19:38
  • Be wary of using the digest/hmac library in Ruby 1.9.3. From the [docs](http://www.ruby-doc.org/stdlib-1.9.3/libdoc/digest/rdoc/Digest/HMAC.html): "CAUTION: Use of this library is discouraged, because this implementation was meant to be experimental but somehow got into the 1.9 series without being noticed. Please use OpenSSL::HMAC in the “openssl” library instead." – Ryan Feb 18 '14 at 17:42
8
def hmac_sha1(data, secret=HOST_KEY)
    require 'base64'
    require 'cgi'
    require 'openssl'
    hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new('sha1'), secret.encode("ASCII"), data.encode("ASCII"))
    return hmac
end
ipegasus
  • 12,519
  • 7
  • 45
  • 66
  • 2
    If you are searching for a rails based solution too, you can use this guy's @ipegasus answer, and just copy everything after the `hmac =...` from that line (no need to require and return is what i'm saying). Rather than bogart his answer maybe he can edit too. – pjammer Sep 30 '14 at 13:44
  • `OpenSSL::Digest::Digest` is deprecated – akostadinov Jul 12 '18 at 21:13
  • @akostadinov what's recommended instead? – Crashalot Sep 16 '19 at 05:03
  • Alternative to the deprecated `OpenSSL::Digest::Digest` is to use `OpenSSL::Digest`: https://stackoverflow.com/a/24904850/144088 – Crashalot Sep 16 '19 at 05:32
  • Actually I use `OpenSSL::HMAC.digest`, check https://github.com/openshift/verification-tests/blob/master/lib/launchers/alicloud.rb#L108-L114 – akostadinov Sep 17 '19 at 06:50