1

I'm trying to generate a CosmosDb Auth token by following the instructions here: https://learn.microsoft.com/en-us/rest/api/documentdb/access-control-on-documentdb-resources.

Here's my implementation in GoLang (I replaced all the parameters with literal values found in the "Example Encoding" section from the doc above):

import(
    "crypto/hmac"
    "crypto/sha256"
    "encoding/base64"
    "net/url")

func generateAuthToken(
    verb string,
    resourceType string,
    resourceId string,
    date string,
    base64Key string) string {

    // Example Key
    base64Key := "dsZQi3KtZmCv1ljt3VNWNm7sQUF1y5rJfC6kv5JiwvW0EndXdDku/dkKBp8/ufDToSxLzR4y+O/0H/t4bQtVNw=="
    msg := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n",
        "get",                            //verb
        "dbs",                            //resourceId
        "dbs/todolist",                   //resourceLink
        "thu, 27 apr 2017 00:51:12 gmt",  //RFC1123 date
        "")

    hasher := hmac.New(sha256.New, []byte(base64Key))
    hasher.Write([]byte(msg))
    signature := base64.StdEncoding.EncodeToString(hasher.Sum(nil))

    authHeader := fmt.Sprintf("type=master&ver=1.0&sig=%s", signature)
    return url.QueryEscape(authHeader)
}

I also took the C# example from the link and ran it with the same parameters as a reference.

This is what I get from the C# implementation (the reference):

"type%3Dmaster%26ver%3D1.0%26sig%3DSGWmGNFZlBH%2Bt9QCvuMy%2FVsbBAOKLbxsgy3Z7aG0PdA%3D"

And this is what I get from my GoLang implementation:

"type%3Dmaster%26ver%3D1.0%26sig%3Dwst1NDxfOeoYMurn69DgZtJUQOrgxFz%2Bp6A2vKnXxEI%3D"

Clearly I'm doing something wrong in the GoLang implementation since the two aren't identical (maybe mis-using the hashing libraries?)

For easy reference, here's the C# implementation:

static void Main(string[] args) {
      string token = GenerateAuthToken(
             "get",
             "dbs",
             "dbs/todolist",
             "thu, 27 apri 2017 00:51:12 gmt", 
             "dsZQi3KtZmCv1ljt3VNWNm7sQUF1y5rJfC6kv5JiwvW0EndXdDku/dkKBp8/ufDToSxLzR4y+O/0H/t4bQtVNw==",
             "master",
             "1.0");
}

static string GenerateAuthToken(string verb, string resourceType, string resourceId, string date, string key, string keyType, string tokenVersion)
{
      var hmacSha256 = new System.Security.Cryptography.HMACSHA256 { Key = Convert.FromBase64String(key) };

      verb = verb ?? "";
      resourceType = resourceType ?? "";
      resourceId = resourceId ?? "";

      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);

      return System.Net.WebUtility.UrlEncode(String.Format(System.Globalization.CultureInfo.InvariantCulture, "type={0}&ver={1}&sig={2}",
            keyType,
            tokenVersion,
            signature));
}

1 Answer 1

4

This took me longer to find than I would have hoped. There is one obvious problem, you are not base64 decoding the key on this line

hasher := hmac.New(sha256.New, []byte(base64Key))

You should do something like

hmacKey, _ := base64.StdEncoding.DecodeString(base64Key)
// handle error
hasher := hmac.New(sha256.New, hmacKey)

But even that didn't fix it. Not until I finally figured out this discrepancy

C# "thu, 27 apri 2017 00:51:12 gmt"
Go "thu, 27 apr 2017 00:51:12 gmt" 

This works

func generateAuthToken(
    verb string,
    resourceType string,
    resourceId string,
    date string,
    base64Key string) string {

    msg := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n",
        verb,
        resourceType,
        resourceId,
        date,
        "")

    hmacKey, _ := base64.StdEncoding.DecodeString(base64Key)
    // handle error
    hasher := hmac.New(sha256.New, hmacKey)
    hasher.Write([]byte(msg))
    signature := base64.StdEncoding.EncodeToString(hasher.Sum(nil))

    authHeader := fmt.Sprintf("type=master&ver=1.0&sig=%s", signature)
    return url.QueryEscape(authHeader)
}

And here is a Go Playground Link

So, the Go version was nearly right (except for the key) and the C# part was almost right (except for the non-RFC1123 format).

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

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.