Objective-C and HTTP Basic Authentication

Objective-C and HTTP Basic Authentication

For all the really nice stuff Objective-C lets you do on the iPhone, there are many, many holes left for very common tasks.

One of those tasks is Base64 encoding.

Base64 encoding is used to convert binary data into text that can be transmitted using HTTP (like, say, for embedding images in email) or for somewhat obfuscating user ids and passwords to be sent “in the clear” over HTTP (like for Basic Authentication).

Without much further ado, I present one of a gagillion implementations of Base64 encoding in C:

static char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789"
"+/";

int encode(unsigned s_len, char *src, unsigned d_len, char *dst)
{
unsigned triad;

for (triad = 0; triad < s_len; triad += 3)
{
unsigned long int sr;
unsigned byte;

for (byte = 0; (byte<3)&&(triad+byte<s_len); ++byte)
{
sr <<= 8;
sr |= (*(src+triad+byte) & 0xff);
}

sr <<= (6-((8*byte)%6))%6; /*shift left to next 6bit alignment*/

if (d_len < 4) return 1; /* error - dest too short */

*(dst+0) = *(dst+1) = *(dst+2) = *(dst+3) = '=';
switch(byte)
{
case 3:
*(dst+3) = base64[sr&0x3f];
sr >>= 6;
case 2:
*(dst+2) = base64[sr&0x3f];
sr >>= 6;
case 1:
*(dst+1) = base64[sr&0x3f];
sr >>= 6;
*(dst+0) = base64[sr&0x3f];
}
dst += 4; d_len -= 4;
}

return 0;

}

And here is how one may transmit a user id and password (using NSURLConnection) to establish a connection using Basic Authentication:

myApp.loginString    = (NSMutableString*)[[NSUserDefaults standardUserDefaults] stringForKey:kloginKey];
myApp.passwordString = (NSMutableString*)[[NSUserDefaults standardUserDefaults] stringForKey:kpasswordKey];

NSMutableString *dataStr = (NSMutableString*)[@"" stringByAppendingFormat:@"%@:%@", myApp.loginString, myApp.passwordString];

NSData *encodeData = [dataStr dataUsingEncoding:NSUTF8StringEncoding];
char encodeArray[512];

memset(encodeArray, '\0', sizeof(encodeArray));

// Base64 Encode username and password
encode([encodeData length], (char *)[encodeData bytes], sizeof(encodeArray), encodeArray);

dataStr = [NSString stringWithCString:encodeArray length:strlen(encodeArray)];
myApp.authenticationString = [@"" stringByAppendingFormat:@"Basic %@", dataStr];

// Create asynchronous request
NSMutableURLRequest * theRequest=(NSMutableURLRequest*)[NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://www.somewebdomain.com"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
[theRequest addValue:myApp.authenticationString forHTTPHeaderField:@"Authorization"];

NSURLConnection * theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];

[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
if (theConnection) {
receivedData = [[NSMutableData data] retain];
}
else {
[myApp addTextToLog:@"Could not connect to the network" withCaption:@"MyApp"];
}

Hopefully, this will help people get cracking that were having a hard time getting a handle on the fact that Objective-C is really C – albeit with funky class extensions.

11 thoughts on “Objective-C and HTTP Basic Authentication

  1. hello,I tried you code today,but I found the encode function did not work for me.
    I need to encode @”hello:world”
    the result is:
    aGVsbG86d29ybGQ=\355\377\277-\333\220`\345\345
    the right result should be this:
    aGVsbG86d29ybGQ

    Like

    1. Before this line of code in the calling function:

      // Base64 Encode username and password
      encode([encodeData length], (char *)[encodeData bytes], sizeof(encodeArray), encodeArray);

      put in a memset function to clear out the receiving buffer first, like:

      memset(encodeArray, '\0', sizeof(encodeArray));

      I had somehow left this out when typing in my client code for the post. The post has been modified to reflect this oversight.

      Like

  2. Hi David,

    I used your code, it run with error, no surprise that i need digest authentication than basic authentication. How can I change this code to do digest authentication ?

    what should i provide with kloginKey and kpasswordKey ? my websites’ username and password ? I supplied that, but v and passwordString came as nil. Is it ok ?

    please help me in resolving this . I got a response from server like

    Notice: You are trying to use HTTP Basic authentication but I am expecting HTTP Digest in /var/www/iphone.compnor.net/htdocs/xmlrpc/httpdigest.php on line 107

    Like

  3. Hey, LIKE your tech grit! Digging in and finding what’s really going on. I look forward to finding more here.

    A suggestion though, lose the black background? This one article, and the one on Apple email from an Iphone is very difficult to read on laptops.

    Besides, you don’t want everything to be black all round anyway, right?

    Like

  4. Thanks for the code! Can you help me with an issue, however? I’ve verified the username/password get Base64-encoded correctly, and I don’t set any HTTP header besides Authorization, yet I still get a response code 400 (the request was badly formed). Weird thing is when I manually set an HTTP proxy to sniff the request, it works! As soon as I turn it off, I get 400 again. If I don’t set the authorization header, I get 401 (authorization required), as expected. I’m a newbie at this, so any help is greatly appreciated, thank you!!

    Like

  5. Hi David,
    I wrote the C-function ‘encode’ as it is, except that i included break at the end of each case.
    But im not getting the src string in base64 format in dst string. dst in my case is returning blank.
    Please suggest where am i going wrong?

    Like

    1. The case statements in this function are intended to “drop through”; Your code should work if you use the function “as written.”

      I know it’s tempting to try and “fix” code that looks like it has a flaw. In this case, resist the urge. 🙂

      Like

Leave a comment