C#.NET Transfer Script.

This is a complete transfer script written in C#. I've been working on this the past couple days, and now that it's finished, I'd like to help others avoid some of the difficulties I've had.

I'd like to note that MUCH of this code (mostly the hard parts) is based on the examples found in this thread: http://discussions.apple.com/thread.jspa?messageID=2710342&#2710342. I've tried to credit the orignal poster in my comments.

protected void Page_Load(object sender, EventArgs e)
{
// Define your site's information. Replace these
// values with ones appropriate for your site.
string debugSuffix = "/your debug suffix";
string sharedSecret = "YOUR SHARED SECRET";
string credential1 = "your credential@urn:mace:itunesu.com:sites:school.edu";
string siteUrl = "https://deimos.apple.com/WebObjects/Core.woa/Browse/school.edu";


// Define the user information. Replace the credentials with the
// credentials you want to grant to that user, and the optional
// identity information with the identity of the current user.
// For initial testing and site setup, use the singe administrator
// credential defined when your iTunes U site was created. Once
// you have access to your iTunes U site, you will be able to define
// additional credentials and the iTunes U access they provide.
string[] credentialArray = {};
string displayName = "Demo";
string emailAddress = "demo@school.edu";
string userName = "demo";
string userIdentifier = "0";

// Append your site's debug suffix to the destination if you
// want to receive an HTML page providing information about
// the transmission of credentials and identity between this
// program and iTunes U. Remove this code after initial
// testing to instead receive the destination page requested.
// uncomment the line below to access debug information from the
// iTunes U server.
//siteUrl = siteUrl + debugSuffix;

//We create the token pieces...
string identity = getIdentityString(displayName, emailAddress, userName, userIdentifier);
string credentials = getCredentialsString(credentialArray);
string currentTime = getCurrentTime();

//...then encode and concatenate them...
string token = "credentials=" + Uri.EscapeDataString(credentials) + "&identity=" + Uri.EscapeDataString(identity) + "&time=" + currentTime;

//...and then perform three replaces because Uri.EscapeDataString doesn't properly encode spaces, "(", or ")"
token = token.Replace("%20", "+");
token = token.Replace("(", "%28");
token = token.Replace(")", "%29");

//Generate the signature string
string signature = "&signature=" + GenerateSignature(token, sharedSecret);

//Pass the token, signature, and url to the actual page request
requestSite(token + signature, siteUrl);
}

//This method creates the time needed for the signature it is based on code originally posted by "Ed at NAIT"
private string getCurrentTime()
{
DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0);
TimeSpan span = DateTime.UtcNow - epoch;
int elapsed = (int)span.TotalSeconds;

return elapsed.ToString();
}

//Concatenate the identity string from the defined user information
private string getIdentityString(string displayName, string emailAddress, string userName, string userIdentifier)
{
string identity = "\"" + displayName + "\" " + "<"(" + userName + ") " + "[" + userIdentifier + "]";
return identity;
}

// concatenates all the passed in credentials into a string
// with a semicolon delimiting the credentials in the string.
private string getCredentialsString(Array credentialArray)
{
string credential = "";
int credentialCount = credentialArray.Length;

for (int i = 0; i < credentialCount; i++)
{
if (i == 0)
{
credential = credentialArray.GetValue(i).ToString();
}
else
{
credential += ";" + credentialArray.GetValue(i).ToString();
}
}
return credential;
}

//This method creates hex encoded signature. It is code originally posted by "Ed at NAIT"
public string GenerateSignature(string token, string sharedSecret)
{
//get the bytes of shared key
byte[] key = System.Text.Encoding.ASCII.GetBytes(sharedSecret);

//Get the HMACSHA256 Object for the shared key
System.Security.Cryptography.HMACSHA256 hmac = new System.Security.Cryptography.HMACSHA256(key);

//calculate the new key
byte[] newKey = hmac.ComputeHash(System.Text.Encoding.ASCII.GetBytes(token));

//return the hex of the new key
return ToHexString(newKey);
}

//This method converts the from ASCII to Hex. It is based on code originally posted by "Ed at NAIT"
public static string ToHexString(byte[] bytes)
{
string hexString = "";
for (int i = 0; i < bytes.Length; i++)
{
hexString += String.Format("{0:x2}", bytes.GetValue(i));
}
return hexString;
}

//This method makes the HTTP request. It is based on code originally posted by "Ed at NAIT"
public void requestSite(string signature, string siteUrl)
{
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(siteUrl);
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";

using (StreamWriter sw = new StreamWriter(req.GetRequestStream()))
{
//The token is the query string (ie. credentials=foo&identity=bar&time=111&signature=key)
sw.Write(signature);
}

using (WebResponse objResponse = req.GetResponse())
{
using (StreamReader sr = new StreamReader(objResponse.GetResponseStream()))
{
//This will write the html to launch iTunes if succsessful or the error if unsuccessful
Response.Write(sr.ReadToEnd());
}
}

}
}

20" iMac, Mac OS X (10.5.1), Dual Boot w/XP

Posted on Nov 30, 2007 11:58 AM

Reply
6 replies

Nov 30, 2007 1:33 PM in response to Tony Wilsman

Thank you for the code.

I have a problem though, no matter what code I use. I seem to get stuck in an endless loop when I try to access our ITunes U site.

I created the .net page with all the proper settings. I configured ITunes U to use that site as the Login URL on the settings page.

When I access the URL, it redirects to the page, trys to access the site, redirects to the page, trys the site, redirects to the page......

Any ideas??? This happens if I use the perl module as well

Apr 15, 2008 9:11 AM in response to Tony Wilsman

Tony, I'm new (very) with C# and using your transfer script (thank you very much). But I'm having trouble with this line that I'm hoping you or someone else can clarify:

string identity = "\"" + displayName + "\" " + "<" + userName + ") " + "\" + userIdentifier + \"";

If I run debug on it:
Compiler Error Message: CS1056: Unexpected character '\'

Thank you.

Apr 15, 2008 6:26 PM in response to Rush Fan

Either way, the error will be the same. If you think about the code this way:

if (nameString.Length > 0)
nameString = "\"" + nameString + "\"";
if (emailString.Length > 0)
emailString = "<" + emailString + ">";
if (usernameString.Length > 0)
usernameString = "(" + usernameString + ")";
if (usernameString.Length > 0)
useridString = "\[" + useridString + "\]";
string identityString = nameString + emailString + usernameString + useridString;

it should (hopefully! 🙂 ) be more obvious how the identity string is composed.

Just a quickie note ... recall that characters have special meanings in these forums ... for example, to get the less-than symbol to come out correctly, I actually had to substitute ampersand-l-t-semicolon ... just like you would in HTML. The "Preview" tab is your friend. 🙂

Also, 'nother couple of quickie notes for .NET fans ...

My sample code above does not do the URL-encoding ... it just composes the identity string. You still have to run that past a URL encoder. Now you'd "think" you could use the URL encoder in HttpServerUtility:

http://msdn2.microsoft.com/en-us/library/zttxte6w.aspx

But that won't work! Why? Because it uses teensy-tiny lower-case as its percent escapes when those come out as hex A, B, C (e.g., "%3a" as opposed to "%3A"). When you hmac-sha256 a percent encoded string, case matters!

In other words, hmac-sha256("RICH", "secret") will return a result different than hmac-sha256("rich", "secret") because the case in "RICH" vs. "rich" is different. In a similar way, hmac-sha256("%3a", "secret") will return something different than hmac-sha256("%3A", "secret").

So go see how HttpServerUtility encodes escapes ... that's okay, I can wait. 🙂 Okay, so you see how it does the escapes ... lower case. Now guuess how Java does it? Yup, upper-case. And for your CGI to work, you have to make sure you URL encode Java's way. In other words, when you URL encode something in Java, then hamc-sha it, life is good. When you do the same thing in C#, life is whacked because C# doesn't do URL escaping in all-caps. This means you have to write your own URL-escaper. 🙂

tokenString = tokenString.Replace(" ", "+");
tokenString = tokenString.Replace("\"", "%23");
tokenString = tokenString.Replace("(", "%28");
tokenString = tokenString.Replace(")", "%29");
tokenString = tokenString.Replace(":", "%3A");
tokenString = tokenString.Replace("<", "%3C");
tokenString = tokenString.Replace(">", "%3E");
tokenString = tokenString.Replace("@", "%40");
tokenString = tokenString.Replace("[", "%5B");
tokenString = tokenString.Replace("]", "%5D");

I hope you all enjoyed that digression into the joys of .NET as much as I enjoyed writing it for you. 😀

This thread has been closed by the system or the community team. You may vote for any posts you find helpful, or search the Community for additional answers.

C#.NET Transfer Script.

Welcome to Apple Support Community
A forum where Apple customers help each other with their products. Get started with your Apple Account.