Converting a Hexadecimal Digest to Base64 in Ruby

Converting a hexadecimal digest to base64 in Ruby

Seems pretty clear and efficient to me. You can save the call to strip by specifying 0 count for the 'm' pack format (if count is 0, no line feed are added, see RFC 4648)

def hex_to_base64_digest(hexdigest)
[[hexdigest].pack("H*")].pack("m0")
end

How to convert hexadecimal to base64urlsafe?

The following should do it:

Base64.urlsafe_encode64(Array(hex_string).pack('H*'))

The method urlsafe_encode64 expects binary input, so you need to convert the hex representation to binary first, as shown above.

Convert Python's base64.b64encode(hmac.new().hexdigest()) to Ruby

First, '\n' in Ruby means literally \ and n. To represent newline, you need to use double-quoted string: "\n".

The order of parameter of Digest::HMAC.hexdigest is different from python version. And according to the Digest::HMAC documetation, its usage is discouraged. Use OpenSSL:HMAC instead.

So the ruby script should be read as:

require 'base64'
require 'openssl'
key = ''
text = "GET\n\n\nSat, 22 Mar 2014 13:49:42 +0000\n"
# hex = Digest::HMAC.hexdigest(text, key, Digest::SHA1) # <-- discouraged
hex = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), key, text)
p Base64.urlsafe_encode64(hex)
# => "ZmUxN2YxMDQxMTdlODg3MDljMTRjYjY0ZGU1NDk1MjgwNzI2Yjk1Zg=="

Convert Between Digest::MD5.digest and Digest::MD5.hexdigest

Using then unpack method will do the trick. (Checkout the official documentation)

Digest::MD5.digest('http://www.example.com').unpack('H*').first == Digest::MD5.hexdigest('http://www.example.com')

Why is hex - base64 so different from base64 - hex using pack and unpack?

to_hex is the exact inverse of to_base64:

to_base64

  1. put string in an array: [self]
  2. call pack with H*: [self].pack("H*")
  3. put string in an array: [[self].pack("H*")]
  4. call pack with m0: [[self].pack("H*")].pack("m0")

to_hex

  1. call unpack with m0: self.unpack("m0")
  2. extract string from array: self.unpack("m0").first
  3. call unpack with H*: self.unpack("m0").first.unpack("H*")
  4. extract string from array: self.unpack("m0").first.unpack("H*").first

That's how you undo operations, by applying the inverse operations:

a = 5
(a + 4) * 3
#=> 27

And the other way around:

a = 27
(a / 3) - 4
#=> 5

a.pack is the inverse of a.unpack and a.first is the inverse of [a]

How to convert a Base64 encoded string to UUID format

I was familiar with unpack but this prompted me to learn the directive as pointed out by cremno.

simplest form:

b64 = 'FWLalpF2T5mmyxS03Q+hNQ0K'
b64.unpack("m0").first.unpack("H8H4H4H4H12").join('-')

#=> "1562da96-9176-4f99-a6cb-14b4dd0fa135"

b64.unpack("m0")

give us:

#=> ["\x15b\xDA\x96\x91vO\x99\xA6\xCB\x14\xB4\xDD\x0F\xA15\r\n"]

which is an array so we use .first to grab the string and unpack again using the directive to format it in the 8-4-4-4-12 format:

b64.unpack("m0").first.unpack("H8H4H4H4H12")

gives us:

#=> ["1562da96", "9176", "4f99", "a6cb", "14b4dd0fa135"]

an array of strings, so now we just join it with the -:

b64.unpack("m0").first.unpack("H8H4H4H4H12").join('-')

#=> "1562da96-9176-4f99-a6cb-14b4dd0fa135"

With Ruby, how to convert binary data to highly compressed, but readable, format

A simple method uses Base64 encoding to encode the value. It's very similar to Hex encoding (which is Base16), but uses a longer dictionary.

Base64 strings, when properly prepared, contain only printable characters. This is a benefit for copy/paste and for sharing.

The secondary benefit is that it has a 3:4 encoding ratio, which means that it's reasonably efficient. A 3:4 encoding ration means that for each 3 bytes in the input, 4 bytes are used to encode (75% efficient); Hex encoding is a less efficient 1:2 encoding ratio, or for each 1 byte of input, 2 bytes are used to encode (50% efficient).

You can use the Ruby standard library Base64 implementation to encode and decode, like so:

require "base64"

encoded = Base64.encode64("Taste the thunder!") # <== "VGFzdGUgdGhlIHRodW5kZXIh\n"
decoded = Base64.decode64(encoded) # <== "Taste the thunder!"

Note that there is a (mostly) URL-safe version, as well, so that you can include an encoded value anywhere in a URL without requiring any additional URL encoding. This would allow you to pass information in a URL in an obscured way, and especially information that normally wouldn't be easily passed in that manner.

Try this to encode your data:

encoded_url_param = Base64.urlsafe_encode64("cake+pie=yummy!")  # <== "Y2FrZStwaWU9eXVtbXkh"
decoded_url_param = Base64.urlsafe_decode64(encoded_url_param) # <== "cake+pie=yummy!"

Using Base64 in a URL, while actually not "security", will help keep prying eyes from your data and intent. The only potential downside to using Base64 values in a URL is that the URL must remain case-sensitive, and some applications don't honor that requirement. See the Should URL be case sensitive SO question for more information.

Ruby version of CryptoJS Base64 stringify

CrytoJS' HmacSHA1 returns binary data whereas Ruby's hexdigest returns a (hex-encoded) string representation.

To get the same result, have to base64-encode the binary digest instead:

secret = 'NzAwZmIwMGQ0YTJiNDhkMzZjYzc3YjQ5OGQyYWMzOTI='
signature = "date: Mon, 25 Jul 2016 16:36:07 GMT\nx-mod-nonce: 28154b2-9c62b93cc22a-24c9e2-5536d7d"

hmac = OpenSSL::HMAC.digest('sha1', secret, signature)
#=> "X\x13+\xFD\x87a\xCA\xC6\xE6\x88\x81$u:\xDF\xDA\x13\xFBI\xF0"

Base64.strict_encode64(hmac)
#=> "WBMr/YdhysbmiIEkdTrf2hP7SfA="

Ruby pack and unpack. How is this hexadecimal conversion being done? Could use some assistance

encrypted_message is a string starting with the characters .YI. Let's convert those characters to Hex and then binary using the ASCII table:

ASCII  .        |Y        |I
Hex 2 e |5 9 |4 9
Binary 0010 1110|0101 1001|0100 1001

Notice that the hex is what you see at the beginning of the unpack(H*) result. If you were to call encrypted_message.unpack("B*") (bit string), you would similarly see it start with

001011100101100101001001

The point is

  1. There is no "encrypted message format". encrypted_message is meaningless, structureless binary data.
  2. When you call unpack, you're saying "Take this meaningless binary data, and show it to me with a different representation." In this case, hexadecimal digits. You can similarly see it as binary, like I did above. Or you can look at it as ASCII characters (the default), with \x indicating a byte that doesn't have an ASCII representation. It's all the same binary data just being presented in different human-readable ways.


Related Topics



Leave a reply



Submit