Reputation: 4536
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.
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.
Upvotes: 4
Views: 1249
Reputation: 18772
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.
Upvotes: 5
Reputation: 4536
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.
Upvotes: 0
Reputation: 79
I think you should change new UnicodeEncoding()
to new UTF8Encoding()
or just use Encoding.UTF8
Unicode is UTF-16 in C#
Upvotes: 1