-1

I have the following code in C# which generates a hash value from a Base64 encoded string.

var hmacSha256 = new System.Security.Cryptography.HMACSHA256 { Key = Convert.FromBase64String(key) };

string verb = "post";
string resourceType = "docs";
string resourceId = "dbs/ToDoList/colls/Items";
string date = DateTime.UtcNow.ToString("R");

string payLoad = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}\n{1}\n{2}\n{3}\n{4}\n",
        verb.ToLowerInvariant(),
        resourceType.ToLowerInvariant(),
        resourceId,
        date.ToLowerInvariant(),
        ""
);

byte[] hashPayLoad = hmacSha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(payLoad));
string signature = Convert.ToBase64String(hashPayLoad);

string authToken = System.Web.HttpUtility.UrlEncode(String.Format(System.Globalization.CultureInfo.InvariantCulture, "type={0}&ver={1}&sig={2}",
    keyType,
    tokenVersion,
    signature));

This works perfectly fine and I wanted to convert it to Java code for my Android app. I checked references from these sources-

C# vs Java HmacSHA1 and then base64

c# and java - difference between hmacsha256 hash

and wrote below code in Java-

String restServiceVersion = "2017-02-22";

String verb = "post";
String resourceType = "docs";
String resourceId = "dbs/ToDoList/colls/Items";

String dateString = org.apache.http.impl.cookie.DateUtils.formatDate(new Date(System.currentTimeMillis()));

String gmtIndex = "GMT";
int index = dateString.indexOf(gmtIndex);

String dateStringFinal = dateString.substring(0, index + 3).toLowerCase();

String payLoad = verb +"\n" + resourceType + "\n" + resourceId + "\n" + dateStringFinal + "\n\n";

System.out.println(payLoad);

String secretAccessKey = MASTER_KEY;
String data = payLoad;
byte[] secretKey = Base64.decode(secretAccessKey, Base64.DEFAULT);
SecretKeySpec signingKey = new SecretKeySpec(secretKey, "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(signingKey);
byte[] bytes = data.getBytes("UTF-8");
byte[] rawHmac = mac.doFinal(bytes);

String authToken = "type=master&ver=1.0&sig=" + Base64.encodeToString(rawHmac, Base64.DEFAULT);

The authToken value generated in Java does not match with C#. Also the byte array generated from Base64 decoding differs.

I am not sure if this is the correct approach. Can someone please take a look? All I need is to convert the above working C# code to Java for my Android app.

Souvik Ghosh
  • 4,456
  • 13
  • 56
  • 78
  • So the values don't match. How very informative..... **NOT** – Andreas Dec 10 '17 at 04:17
  • If the bytes of the key after base64 decoding differ then the sha256 hash will definitely differ too. Are you absolutely 100% certain the source base64-encoded key is the same in both? Can you show us for a given input what output you're getting under each platform? (if the exact key you're using is only for local testing purposes you can just use that). – Dylan Nicholson Dec 10 '17 at 04:28
  • @DylanNicholson Yes the source key and other parameters are same. Just worried about if I have constructed them properly. – Souvik Ghosh Dec 10 '17 at 04:33
  • I will say regardless of the key being different, given your input text includes a date based on the current time, the hash IS always going to be different even on the same platform! And are you sure `org.apache.http.impl.cookie.DateUtils.formatDate` generates the same format as `.ToString("R")` ? But if you're not going to post more info about what you're seeing I doubt anyone is going to solve it for you. – Dylan Nicholson Dec 10 '17 at 04:50
  • @DylanNicholson True. But actually I have replaced and hard coded the same date value for the sake of debugging. Even the initial value of the byte array- byte[] secretKey = Base64.decode(secretAccessKey, Base64.DEFAULT); differs from the C#- Convert.FromBase64String(key) – Souvik Ghosh Dec 10 '17 at 05:13
  • Souvik I've asked twice now for you to tell us what actual data you're passing in and getting back, and you haven't provided anything, so I'm sorry I really can't help you. – Dylan Nicholson Dec 10 '17 at 05:15
  • @DylanNicholson I am passing a string value as the `key` in this format- `xR2LZ9e9WeiRorb8X2eCTNJE2jzivdfzGwXasqvnIqsyYv9hq5zYqjsOM4EVDLgxxtVFETZGL77zq2FfeyGCig==`. Then I am constructing the `payload` value like this- `post\ndocs\ndbs/ToDoList/colls/Items\nfri, 08 dec 2017 16:49:06 gmt`. Then a hash value is generated using the the `payload` and the hash value is concatenated at the end to get the `authToken` like this- `type=master&ver=1.0&sig=cJNl4oF4pY9czi5OGa5pY+9NVyZ5hICcvgBy2lB1shE=` – Souvik Ghosh Dec 10 '17 at 05:32
  • When I copy and paste that base-64 string it seems there are some extra invalid data points in there, e.g. char codes e2 80 8c before 'V‌​DLgxx'. I believe this is UTF-8 encoding for a zero-width space (or actually 'non-joiner'). – Dylan Nicholson Dec 10 '17 at 05:41

1 Answers1

0

Most likely explanation: you have invisible unicode characters (e.g. "zero-width non-joiner") embedded in your BASE64 strings. One platform is stripping these out when base-64 decoding, the other is not, resulting in different keys.

Dylan Nicholson
  • 1,301
  • 9
  • 23
  • Given all the information he had provided (read above comment thread) this is almost certainly exactly the only answer that explains the behaviour he was observing (NB technically there was no question other than "Can someone please take a look?"). – Dylan Nicholson Dec 11 '17 at 08:41