How to send SAML Response message using NSURLConnection?

I am creating an iPhone application that allows users to interact with a secure server. However, for user authentication, the application must go through SAML from the server to which the user logs on to the server on which the data is located (with single sign-on). Thus, the application simulates the sending of SAML HTTP requests (where the browser usually browses).

The process works with Ruby using the Mechanize mechanism, but now I can not get it to work on the iPhone with NSURLConnection.

The problem I am facing is as follows: it seems that the base64 encoded SAML request is not being sent properly by NSURLConnection to the server. The + signs are replaced by spaces, which causes the decoding of the SAML response on the server side to fail. Using URL encoding with stringByAddingPercentEscapesUsingEncoding results in the same decoding error.

This is code extraction and sending SAML response:

// Extract the SAML Response from the form that is sent by the server
body = [[NSString alloc] initWithData:[self receivedData]     encoding:NSUTF8StringEncoding];
NSString *searchSAMLResponseStart = @"name=\"SAMLResponse\" value=\"";
NSString *searchSAMLResponseEnd = @">\n<NOSCRIPT><INPUT TYPE=\"SUBMIT\"";
NSRange samlResponseStartRange = [body rangeOfString:searchSAMLResponseStart options:0];
NSRange samlResponseEndRange = [body rangeOfString:searchSAMLResponseEnd options:0];
NSRange range = NSMakeRange (NSMaxRange(samlResponseStartRange), samlResponseEndRange.location - NSMaxRange(samlResponseStartRange) -1);
NSString* samlResponseString = [body substringWithRange:range];

// Construct the SAML POST request
NSURL               *url;
NSMutableURLRequest *request;
url = [NSURL URLWithString:kSamlPostUrl];
request = [NSMutableURLRequest requestWithURL:url];
NSString *userAgent = kUserAgent;
[request setValue:userAgent forHTTPHeaderField:@"User-Agent"];
[request setHTTPMethod:@"POST"];
NSString *post = [@"RelayState=https://example.com&SAMLResponse=" stringByAppendingString:samlResponseString];
NSData *postData = [post dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:NO];
NSString *postLength = [NSString stringWithFormat:@"%d", [postData length]];
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:postData];

// Send the request        
self.connectionPostSaml = [NSURLConnection connectionWithRequest:request delegate:self];

The SAML form that the server sends and where the SAML response comes from is as follows:

<html>
<HEAD><META HTTP-EQUIV='PRAGMA' CONTENT='NO-CACHE'><META HTTP-EQUIV='CACHE-CONTROL'     CONTENT='NO-CACHE'><TITLE>SAML 2.0 Auto-POST form</TITLE></HEAD>
   <body onLoad="document.forms[0].submit()">
<NOSCRIPT>Your browser does not support JavaScript.  Please click the 'Continue' button     below to proceed. <br><br></NOSCRIPT>
      <form action="https://sso.example.com/saml2" method="POST">
         <input type="hidden" name="RelayState" value="https://example.com">
         <input type="hidden" name="SAMLResponse"     value="PFJlc3BvbnNlIHhtbG5zPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIERl
c3RpbmF0aW9uPSJodHRwczovL3Nzby5vcG93ZXIuY29tL3NwL0FDUy5zYW1sMiIgSUQ9Il85ZWY0
M2MzMGRkZmQ0YzY1ODNiMjgxZjAwNDU5ZWQyMjZmMjQiIElzc3VlSW5zdGFudD0iMjAxMi0wMy0y
OFQyMTo0MDowNloiIFZlcnNpb249IjIuMCI+CiAgICA8bnMxOklzc3VlciB4bWxuczpuczE9InVy
bjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIEZvcm1hdD0idXJuOm9hc2lzOm5h
bWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6ZW50aXR5Ij5wZ2UuY29tPC9uczE6SXNzdWVy
(…)
cnRpb24+CjwvUmVzcG9uc2U+">
<NOSCRIPT><INPUT TYPE="SUBMIT" VALUE="Continue"></NOSCRIPT>
      </form>
   </body>
</html>

And the error that the server produces after receiving the POST request:

Unable to parse incoming SAML2 message, raw:     PFJlc3BvbnNlIHhtbG5zPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIERl
c3RpbmF0aW9uPSJodHRwczovL3Nzby5vcG93ZXIuY29tL3NwL0FDUy5zYW1sMiIgSUQ9Il9iN2Q5
OTZhMmI1MjhiNWRkNjY0YWM4YjUwZmVjM2M2OTMzMWEiIElzc3VlSW5zdGFudD0iMjAxMi0wMy0y
OVQyMjowMzoxNVoiIFZlcnNpb249IjIuMCI CiAgICA8bnMxOklzc3VlciB4bWxuczpuczE9InVy
bjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIEZvcm1hdD0idXJuOm9hc2lzOm5h
bWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6ZW50aXR5Ij5wZ2UuY29tPC9uczE6SXNzdWVy
PgogICAgPFN0YXR1cz4KICAgICAgICA8U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVz
OnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8 CiAgICA8L1N0YXR1cz4KICAgIDxuczI6QXNz
(…)

Note that the + signs in the original answer change to spaces.

It seems that the normal behavior of the server is to decode the received response and, therefore, replace the characters with + spaces: why does it work when the SAML form is submitted from the browser, but not from NSURLConnection?

, :

  • SAML. , NSUTF8StringEncoding, : NSASCIIStringEncoding, NSNEXTSTEPStringEncoding, NSNonLossyASCIIStringEncoding, NSUnicodeStringEncoding

  • stringByAddingPercentEscapesUsingEncoding, :

    NSString * samlResponseString = [body substringWithRange: range];

:

NSString* samlResponseString = [[body substringWithRange:range] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
  • enctypes , : /-, /

. !

+5
1

base64 url. :

NSString *post = [@"RelayState=https://example.com&SAMLResponse=" stringByAppendingString:
        (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,
        (__bridge CFStringRef)samlResponseString, NULL, (CFStringRef)@"!*'\"();:@&=+$,/?%#[]% ",
        CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding))];
0
source

All Articles