如何使用NSURLConnection连接SSL为一个不受信任的证书?

我有以下简单的代码连接到SSL网页

NSMutableURLRequest *urlRequest=[NSMutableURLRequest requestWithURL:url];
[ NSURLConnection sendSynchronousRequest: urlRequest returningResponse: nil error: &error ];

除非它给出一个错误,如果证书是自签名的Error Domain=NSURLErrorDomain Code=-1202 UserInfo=0xd29930 "untrusted server certificate".是否有一种方法来设置它接受连接(就像在浏览器中,你可以按下接受)或一种方法来绕过它?

270197 次浏览

NSURLRequest有一个名为setAllowsAnyHTTPSCertificate:forHost:的私有方法,它将做你想做的事情。你可以通过一个类别在NSURLRequest上定义allowsAnyHTTPSCertificateForHost:方法,并设置它为你想要覆盖的主机返回YES

如果你不愿意(或不能)使用私有api,有一个名为ASIHTTPRequest的开源(BSD许可)库,它提供了底层CFNetwork APIs的包装器。他们最近通过-setValidatesSecureCertificate: API引入了允许HTTPS connections使用自签名或不受信任证书的能力。如果不想提取整个库,可以使用源代码作为自己实现相同功能的参考。

有一个支持的API来完成这个!在你的NSURLConnection委托中添加如下内容:

- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}


- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
if ([trustedHosts containsObject:challenge.protectionSpace.host])
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];


[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}

注意connection:didReceiveAuthenticationChallenge:可以将它的消息发送给challenge。发送方(很久以后),在必要时向用户呈现对话框之后,等等。

我不能为此获得任何荣誉,但我找到了这个工作得很好,满足了我的需求。shouldAllowSelfSignedCert是我的BOOL变量。只要添加到你的NSURLConnection委托,你应该在每个连接的基础上快速绕过。

- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)space {
if([[space authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if(shouldAllowSelfSignedCert) {
return YES; // Self-signed cert will be accepted
} else {
return NO;  // Self-signed cert will be rejected
}
// Note: it doesn't seem to matter what you return for a proper SSL cert
//       only self-signed certs
}
// If no other authentication is required, return NO for everything else
// Otherwise maybe YES for NSURLAuthenticationMethodDefault and etc.
return NO;
}
由Nathan de Vries发布的category workaround将通过AppStore私有API检查,并且在你无法控制NSUrlConnection对象的情况下非常有用。 一个例子是NSXMLParser,它将打开你提供的URL,但不公开NSURLRequestNSURLConnection.

. 0

在iOS 4中,该方法似乎仍然有效,但仅在设备上,模拟器不再调用allowsAnyHTTPSCertificateForHost:方法。

理想情况下,iOS应用程序需要接受不受信任的证书时,应该只有两种情况。

场景A:您连接到使用自签名证书的测试环境。

场景B:您正在使用MITM Proxy like Burp Suite, Fiddler, OWASP ZAP, etc.代理HTTPS流量。代理将返回一个由自签名CA签名的证书,以便代理能够捕获HTTPS流量。

生产主机不应该为显而易见的原因使用不受信任的证书。

如果你需要让iOS模拟器接受一个不受信任的证书用于测试目的,强烈建议你不要为了禁用NSURLConnection api提供的内置证书验证而更改应用程序逻辑。如果应用程序在没有删除此逻辑的情况下发布给公众,它将容易受到中间人攻击。

出于测试目的而接受不受信任证书的推荐方法是将签署证书的证书颁发机构(CA)证书导入您的iOS模拟器或iOS设备。我写了一篇快速的博客文章来演示如何在iOS模拟器中做到这一点:

使用ios模拟器接受不受信任的证书

我发布了一些要点代码(基于别人的工作,我注意到),让你正确地验证自己生成的证书(以及如何获得免费证书-见Cocoanetics底部的评论)

我的代码在这里github

如果你想继续使用sendSynchronousRequest,我在这个解决方案:

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

你可以在这里看到:Objective-C SSL同步连接

为了补充已接受的答案,为了更好的安全性,你可以将你的服务器证书或你自己的根CA证书添加到keychain(https://stackoverflow.com/a/9941559/1432048),但是这样做不会使NSURLConnection自动验证你的自签名服务器。你仍然需要将下面的代码添加到你的NSURLConnection委托中,它是从Apple样例代码AdvancedURLConnections中复制的,你需要从Apple样例代码中添加两个文件(Credentials.h, Credentials.m)到你的项目中。

- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}


- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
//        if ([trustedHosts containsObject:challenge.protectionSpace.host])


OSStatus                err;
NSURLProtectionSpace *  protectionSpace;
SecTrustRef             trust;
SecTrustResultType      trustResult;
BOOL                    trusted;


protectionSpace = [challenge protectionSpace];
assert(protectionSpace != nil);


trust = [protectionSpace serverTrust];
assert(trust != NULL);
err = SecTrustEvaluate(trust, &trustResult);
trusted = (err == noErr) && ((trustResult == kSecTrustResultProceed) || (trustResult == kSecTrustResultUnspecified));


// If that fails, apply our certificates as anchors and see if that helps.
//
// It's perfectly acceptable to apply all of our certificates to the SecTrust
// object, and let the SecTrust object sort out the mess.  Of course, this assumes
// that the user trusts all certificates equally in all situations, which is implicit
// in our user interface; you could provide a more sophisticated user interface
// to allow the user to trust certain certificates for certain sites and so on).


if ( ! trusted ) {
err = SecTrustSetAnchorCertificates(trust, (CFArrayRef) [Credentials sharedCredentials].certificates);
if (err == noErr) {
err = SecTrustEvaluate(trust, &trustResult);
}
trusted = (err == noErr) && ((trustResult == kSecTrustResultProceed) || (trustResult == kSecTrustResultUnspecified));
}
if(trusted)
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
}


[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}

你必须使用NSURLConnectionDelegate来允许HTTPS连接,iOS8有新的回调。

弃用:

connection:canAuthenticateAgainstProtectionSpace:
connection:didCancelAuthenticationChallenge:
connection:didReceiveAuthenticationChallenge:

相反,你需要声明:

connectionShouldUseCredentialStorage: -发送来确定URL加载器是否应该使用凭据存储来验证连接。

connection:willSendRequestForAuthenticationChallenge: -告诉委托连接将发送一个身份验证挑战请求。

对于willSendRequestForAuthenticationChallenge,你可以像使用已弃用的方法一样使用challenge,例如:

// Trusting and not trusting connection to host: Self-signed certificate
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];

在iOS 9中,对于所有无效或自签名的证书,SSL连接将失败。这是iOS 9.0或更高版本以及OS X 10.11及更高版本中新的应用传输安全特性的默认行为。

你可以在Info.plist中重写此行为,方法是在NSAppTransportSecurity字典中将NSAllowsArbitraryLoads设置为YES但是,我建议仅为测试目的重写此设置。

enter image description here

有关信息,请参阅App Transport Technote 在这里

AFNetworking我已经成功地消费https webservice与以下代码,

NSString *aStrServerUrl = WS_URL;


// Initialize AFHTTPRequestOperationManager...
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
manager.responseSerializer = [AFJSONResponseSerializer serializer];


[manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
manager.securityPolicy.allowInvalidCertificates = YES;
[manager POST:aStrServerUrl parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject)
{
successBlock(operation, responseObject);


} failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
errorBlock(operation, error);
}];

您可以使用此代码

-(void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodServerTrust)
{
[[challenge sender] useCredential:[NSURLCredential credentialForTrust:[[challenge protectionSpace] serverTrust]] forAuthenticationChallenge:challenge];
}
}

使用-connection:willSendRequestForAuthenticationChallenge:代替这些弃用方法

弃用:

-(BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
-(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
-(void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge