Skip to content

iTunes U. Authentication Gotchas

While working on an implemention of the iTunes U. authentication algorithm in C#, I came upon a few major gotchas that I thought would be useful to share. I ported the Java implementation that is a part of the example code(zip) package downloadable from the iTunes U. support site.

Gotcha #1: iTunes U. Signature Must be Lowercase

One issue I encountered when trying to debug my code was that despite generating the proper signature string, iTunes U. would reject it as invalid. The reason? The string I was generating was uppercase. To fix it, I simply called ToLower() on my generated string before returning it. The HMAC-SHA256 algorithm is already implemented in the .NET libraries, so it is trivial to write the signature algorithm. It is so short, here it is in its entirety:

public string HMACSHA256(string message, byte[] key)
    {
        UTF8Encoding ue = new UTF8Encoding();
        HMACSHA256 hmac = new HMACSHA256(key);
        byte[] hash = hmac.ComputeHash(ue.GetBytes(message.ToCharArray()));

        // Convert the bytes to hex
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < hash.Length; i++)
        {
            sb.Append(hash[i].ToString("X2"));
        }

        return sb.ToString().ToLower();
    }

Gotcha #2: iTunes U. is Picky About URL Encoding

Even after I had the signature algorithm working properly, iTunes U. would still reject my requests. After some investigation, it turns out that the iTunes U. server is very picky about how you URL encode your credentials and identity strings. If you don’t do it just right, the signature will not be valid. There are two issues with the URL encoding that I needed to fix to get everything working:

  1. The HttpUtility.UrlEncode() method does not encode ‘(‘ or ‘)’ characters. But, iTunes U. expects that these characters are encoded as %28 and %29, respectively.

  2. Also, in a reverse of Gotcha #1, the HttpUtility.UrlEncode() method outputs entity codes in lowercase, but iTunes U. expects uppercase. For example, a token data string like,

    credentials=foo&identity=<jdoe@example.edu>"jdoe"&time=1139331600000

    needs to be encoded as:

    credentials=foo&identity=%3Cjdoe@example.edu%3E%22jdoe%22&time=1139331600

    Notice the ‘%3C’ instead of ‘%3c’. It makes a difference.

I worked around the problem by first URL encoding my string and passing that string to a function that fixed these two above problems. Since this is another short function, I reproduce it below:

private string FixURLEncoding(string UrlEncodedString)
        {
            StringBuilder s = new StringBuilder(UrlEncodedString);
            for (int i = 0; i < s.Length; i++)
            {
                // Find an encoded character and make the letter
                // part upper case because the iTunes U algorithm
                // is very sensitive.
                if (s[i] == '%')
                {
                    if (Char.IsDigit(s[i + 1]) && Char.IsLetter(s[i + 2]))
                    {
                        s[i + 2] = Char.ToUpper(s[i + 2]);
                    }
                }
            }

            // Encode '(' and ')' b/c UrlEncode doesn't do it and iTunes U
            // is sensitive to this
            s = s.Replace("(", "%28");
            s = s.Replace(")", "%29");

            return s.ToString();
        }

Gotcha #3: iTunes U. Timestamp is in Unix Epoch Format

This is a minor gotcha. You need to convert your time to UTC and then format it as Unix time. This is mentioned in the documentation, but I think only very briefly. If you are unfamiliar with Unix time, Wikipedia says it is “the number of seconds elapsed since midnight UTC of January 1, 1970, not counting leap seconds.” You just need to take your DateTime object and create a new TimeSpan object that is equal to your time (in UTC) minus the epoch date. Then, take the TotalSeconds of the TimeSpan and you have your timestamp for the iTunes U. request.

Gotcha #4: Keep Your Clock Synced

Another minor issue that was caught early on was that if your computer’s clock is not synced with an accurate external time source, your timestamp can be off enough to cause iTunes U. to reject your request. This happened during the initial testing of credentials with the supplied Java test script. It only takes a minute or two inaccuracy in your computer’s time to cause requests to be rejected. It is something to remember if you are deploying an application that implements the iTunes U. authentication algorithm.

9 Comments

  1. Brian Dishaw wrote:

    This post was a life saver. Their documentation was lacking and it was taking a long time to figure out what exactly all the gotchas were.

    Thank you so much for posting this!

    Thursday, January 3, 2008 at 11:25 am | Permalink
  2. Thanks. I’m glad someone found it useful.

    Friday, January 4, 2008 at 11:15 am | Permalink
  3. Peter Waite wrote:

    Thanks, this is helpful. I’m not an expert at porting the code from Java, can you send me a copy of the entire C# script? Thanks!

    Friday, January 25, 2008 at 4:25 pm | Permalink
  4. Jesse Baker wrote:

    That’s great thanks.

    You mention UTC it in number 3, but I’d glossed over it because I knew how to generate a timestamp in unix format. So of course I was sending local time.

    Sunday, March 30, 2008 at 10:36 pm | Permalink
  5. Phatency wrote:

    Thanks for the FixUrlEncoding function, I had the exact same problem (not with iTunes).

    Monday, August 4, 2008 at 2:48 pm | Permalink
  6. Phatency,

    This problem seems to happen a lot. There is a similar ParameterEncode() method in the RestChess OAuth source code.

    Monday, August 4, 2008 at 2:57 pm | Permalink
  7. Phatency wrote:

    Oh, by the way, there is no “letter part” in url encoded characters, they are both hexadecimal numerals. For example, scandinavian letter Ä is %C4, which your function wouldn’t encode properly. There’s no need for checking if the character is numeral or letter either, Char.ToUpper returns the original value if it’s a numeral. Sufficient simple change: if (s[i] == ‘%’) { s[i + 1] = Char.ToUpper(s[i + 1]); s[1 + 2] = Char.ToUpper(s[i + 2]); }

    Monday, August 4, 2008 at 3:13 pm | Permalink
  8. Phatency,

    Thanks for that simple fix. I’ll update the function the next time I get into that code base.

    That function was written very quickly, to fix a very specific issue, and even though it is very simple, it is not perfect. It just shows that even very simple functions can contain bugs or have room for optimization and that the original programmer may not even see it. That is basically an argument for open source development practices, right there.

    Monday, August 4, 2008 at 4:07 pm | Permalink
  9. Chip Willis wrote:

    Colud you also send me a copy of the entire C# script? Thank you, Chip

    Monday, December 15, 2008 at 1:04 pm | Permalink

Post a Comment

Your email is never published nor shared. Required fields are marked *
*
*