Как опубликовать ответное сообщение SAML с помощью NSURLConnection? - PullRequest
6 голосов
/ 30 марта 2012

Я создаю приложение для iPhone, которое позволяет пользователям взаимодействовать с защищенным сервером. Однако для аутентификации пользователя приложение должно пройти через SAML с сервера, на котором пользователь входит на сервер, на котором находятся данные (предоставляя SSO). Таким образом, приложение имитирует отправку HTTP-запросов SAML (где обычно об этом заботится браузер).

Процесс работает с Ruby, используя гем Mechanize, но теперь я не могу заставить его работать на iPhone с NSURLConnection.

Проблема, с которой я сталкиваюсь, заключается в следующем: кажется, что ответ SAML в кодировке base64 неправильно отправляется NSURLConnection на сервер. Знаки + заменяются пробелами, что приводит к сбою декодирования ответа SAML на стороне сервера. Использование URL-кодирования со строкойByAddingPercentEscapesUsingEncoding приводит к той же ошибке декодирования.

Это код, извлекающий и отправляющий ответ SAML:

// 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];

Форма SAML, которую отправляет сервер и откуда берется ответ SAML, выглядит следующим образом:

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

И ошибка, которую сервер выдает после получения запроса POST:

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

Обратите внимание, как знаки + в необработанном ответе превращаются в пробелы.

Кажется нормальным поведение для сервера декодировать полученный ответ и, следовательно, заменять знаки + пробелами, однако: почему это работает, когда форма SAML отправляется из браузера, но не из NSURLConnection?

Чтобы решить, я попытался:

  • с использованием различных кодировок для декодирования и кодирования Ответа SAML. Теперь используя NSUTF8StringEncoding, также попробовал: NSASCIIStringEncoding, NSNEXTSTEPStringEncoding, NSNonLossyASCIIStringEncoding, NSUnicodeStringEncoding

  • с использованием stringByAddingPercentEscapesUsingEncoding, поэтому эта строка кода:

    NSString * samlResponseString = [тело substringWithRange: range];

становится:

NSString* samlResponseString = [[body substringWithRange:range] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
  • используя другие формы для формы, но сервер даже не примет эти запросы: многочастному / форм-данных, текст / обычный

Я очень ценю вашу помощь. Заранее спасибо!

1 Ответ

0 голосов
/ 11 апреля 2013

У меня была проблема с отправкой base64 по почте, и я решил ее с помощью URL-кодировки. Попробуйте это:

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