4

I have the C# code:

byte[] bytes = new UnicodeEncoding().GetBytes(input);
return Convert.ToBase64String(new SHA256Managed().ComputeHash(bytes));

that encodes a string to an SHA2 hash, which is then base 64 encoded. I need to convert this into Ruby.

I tried several ways. This is one of them:

hash = Digest::SHA256.digest(val.encode('utf-8'))
encoded = Base64.urlsafe_encode64(hash)

My code all produce the same result that doesn't match. I cannot get them to work. Any help on converting this would be appreciated.

Update

After some messing around I was able to get it working with a hard coded array, the issue is that the C# code adds a 0 after every element in the array. Here is the working ruby code (with the hard coded array):

Digest::SHA256.base64digest([99,0,104,0,97,0,100,0].pack('C*').force_encoding('utf-16'))

I suppose I could iterate through the array but that seems unnecessary.

3 Answers 3

5

Solution:

You need to use UTF-16LE encoding to get same SHA-256 Hash

p hash = Digest::SHA256.base64digest("Hello".encode('UTF-16LE'))

How did I find that?

I used Online C# Tool to create the hash of a string "Hello" using the code below:

using System.IO;
using System;
using System.Text;
using System.Security.Cryptography;

class Program
{
    static void Main()
    {
        byte[] bytes = new UnicodeEncoding().GetBytes("Hello");
        String s =  Convert.ToBase64String(new SHA256Managed().ComputeHash(bytes));
        Console.WriteLine(s);
    }
}

Above program produced the output oH5Pc0MkbIKybzLlb4VBjVGNiy8trnfx1W/nr1Dbl68=

Then, I wrote a Ruby Program to compute Base64-encoded SHA-256 hash of string "Hello", by encoding the string in all the supported char encodings of Ruby to figure out which char encoding will give exact same result.

require "digest/sha2"
require "base64"
s = "Hello"
Encoding.list.each { |e| puts "Encoding: #{e.to_s} => Hash: #{Digest::SHA256.base64digest(s.encode(e)) rescue "error"}"}

After running the program, I came to conclude that if you use UTF-16LE encoding, you will get exact same output.

p hash = Digest::SHA256.base64digest("Hello".encode('UTF-16LE'))

As per documentation of UnicodeEncoding, it:

Represents a UTF-16 encoding of Unicode characters.

Sign up to request clarification or add additional context in comments.

Comments

1

I think you should change new UnicodeEncoding()to new UTF8Encoding() or just use Encoding.UTF8

Unicode is UTF-16 in C#

2 Comments

The code above was taken after decompiling a dll that is used for password hashing. Maybe if I try utf-16 on the ruby side that will work
I think maybe use utf-16le will works(I don't known much about ruby)....utf-16 will encode every character into 2 or 4 bytes...use GetBytes method in C# will not put BOM, I think mybe use little edian will work
0

K so it was a multitude of items. In C# UnicodeEncoding().GetBytes(str) apparently adds a 0 after each character to the byte array. So I had to work around that in Ruby. Luckily I was able to use a little ruby magic to do this. The full working solution is below.

byte_array = input.bytes.zip(input.bytes.map{|b| 0}).flatten.compact
Digest::SHA256.base64digest(byte_array.pack('C*').force_encoding('utf-16'))

This gave me an identical hash output matching the value in the database.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.