Обработка синхронного SSL-сертификата на iPhone
Мне было интересно, может ли кто-нибудь помочь мне понять, как добавить обработку SSL-сертификата в синхронный
подключения к службе https.
Я знаю, как это сделать с асинхронными соединениями, но не синхронно.
NSString *URLpath = @"https://mydomain.com/";
NSURL *myURL = [[NSURL alloc] initWithString:URLpath];
NSMutableURLRequest *myURLRequest = [NSMutableURLRequest requestWithURL:myURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60];
[myURL release];
[myURLRequest setHTTPMethod:@"POST"];
NSString *httpBodystr = @"setting1=1";
[myURLRequest setHTTPBody:[httpBodystr dataUsingEncoding:NSUTF8StringEncoding]];
NSHTTPURLResponse* myURLResponse;
NSError* myError;
NSData* myDataResult = [NSURLConnection sendSynchronousRequest:myURLRequest returningResponse:&myURLResponse error:&myError];
//I guess I am meant to put some SSL handling code here
Спасибо.
Ответы
Ответ 1
Использование статической функции sendSynchronousRequest не представляется возможным, но я нашел альтернативу.
Прежде всего объект NSURLConnectionDataDelegate, как этот
FailCertificateDelegate.h
@interface FailCertificateDelegate : NSObject <NSURLConnectionDataDelegate>
@property(atomic,retain)NSCondition *downloaded;
@property(nonatomic,retain)NSData *dataDownloaded;
-(NSData *)getData;
@end
FailCertificateDelegate.m
#import "FailCertificateDelegate.h"
@implementation FailCertificateDelegate
@synthesize dataDownloaded,downloaded;
-(id)init{
self = [super init];
if (self){
dataDownloaded=nil;
downloaded=[[NSCondition alloc] init];
}
return self;
}
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace: (NSURLProtectionSpace *)protectionSpace {
NSLog(@"canAuthenticateAgainstProtectionSpace:");
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge: (NSURLAuthenticationChallenge *)challenge {
NSLog(@"didReceiveAuthenticationChallenge:");
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[downloaded signal];
[downloaded unlock];
self.hasFinnishLoading = YES;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
[dataDownloaded appendData:data];
[downloaded lock];
}
-(NSData *)getData{
if (!self.hasFinnishLoading){
[downloaded lock];
[downloaded wait];
[downloaded unlock];
}
return dataDownloaded;
}
@end
И для использования
FailCertificateDelegate *fcd=[[FailCertificateDelegate alloc] init];
NSURLConnection *c=[[NSURLConnection alloc] initWithRequest:request delegate:fcd startImmediately:NO];
[c setDelegateQueue:[[NSOperationQueue alloc] init]];
[c start];
NSData *d=[fcd getData];
Теперь у вас будут все преимущества асинхронного использования nsurlconnection и преимуществ простого соединения синхронизации, поток будет заблокирован до тех пор, пока вы не загрузите все данные на делегате, но вы можете улучшить его, добавив некоторый контроль ошибок в класс FailCertificateDelegate
EDIT: исправление для больших данных. на основе комментария Николая Д.С. Большое спасибо
Ответ 2
У меня была аналогичная проблема. В моем случае у меня было асинхронное соединение, работающее с ssl, как требуется, с использованием двух методов делегата, которые позволили мне принять любой сертификат:
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
}
Но я застрял в том, чтобы делать то же самое синхронно. Я искал веб-сайт до тех пор, пока не нашел ваш пост и, к сожалению, еще один postoverflow post, где намечено, что вы не можете выполнять синхронизацию с NSURLConnection и работать с ssl (из-за отсутствия делегат для обработки процесса аутентификации ssl).
То, что я закончил, - это получить ASIHTTPRequest и использовать это. Это было безболезненно, и мне понадобилось около часа, чтобы настроить и отлично работать. вот как я его использую.
+ (NSString *) getSynchronously:(NSDictionary *)parameters {
NSURL *url = [NSURL URLWithString:@"https://localhost:8443/MyApp/";
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
NSString *parameterJSONString = [parameters JSONRepresentation];
[request appendPostString:parameterJSONString];
[request addRequestHeader:@"User-Agent" value:@"MyAgent"];
request.timeOutSeconds = CONNECTION_TIME_OUT_INTERVAL;
[request setValidatesSecureCertificate:NO];
[request startSynchronous];
NSString *responseString = [request responseString];
if (request.error) {
NSLog(@"Server connection failed: %@", [request.error localizedDescription]);
} else {
NSLog(@"Server response: %@", responseString);
}
return responseString;
}
Важной частью, конечно, является
[request setValidatesSecureCertificate:NO];
Еще одна альтернатива для вас - обработать загрузку в другом потоке с помощью a-synch-соединения, используя два вышеописанных метода, и заблокировать поток, из которого вы хотите синхронизировать соединение, до тех пор, пока запрос не будет завершен.
Ответ 3
Im близко к поиску решения для этого с помощью кода ниже. Это работает, но часто падает
вероятно, потому, что я делаю что-то неправильно в том, как я это кодирую, и у меня нет четкого понимания используемых методов. Но если у кого-то есть предложения по улучшению этого
чем пожалуйста, напишите.
Только после строки:
NSError* myError;
и непосредственно перед строкой:
NSData* myDataResult = [NSURLConnection sendSynchronousRequest:myURLRequest
returningResponse:&myURLResponse error:&myError];
добавить:
int failureCount = 0;
NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc]
initWithHost:@"mydomain.com" port:443 protocol:@"https" realm:nil
authenticationMethod:NSURLAuthenticationMethodServerTrust];
NSURLResponse *response = [[NSURLResponse alloc] initWithURL:myURL MIMEType:@"text/html"
expectedContentLength:-1 textEncodingName:nil];
NSURLAuthenticationChallenge *challange = [[NSURLAuthenticationChallenge alloc]
initWithProtectionSpace:protectionSpace proposedCredential:[NSURLCredential
credentialForTrust:protectionSpace.serverTrust] previousFailureCount:failureCount
failureResponse:response error:myError sender:nil];