ios 如何使用 NSURLConnection 连接 SSL 以获得不受信任的证书?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/933331/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to use NSURLConnection to connect with SSL for an untrusted cert?
提问by erotsppa
I have the following simple code to connect to a SSL webpage
我有以下简单的代码来连接到 SSL 网页
NSMutableURLRequest *urlRequest=[NSMutableURLRequest requestWithURL:url];
[ NSURLConnection sendSynchronousRequest: urlRequest returningResponse: nil error: &error ];
Except it gives an error if the cert is a self signed one Error Domain=NSURLErrorDomain Code=-1202 UserInfo=0xd29930 "untrusted server certificate".
Is there a way to set it to accept connections anyway (just like in a browser you can press accept) or a way to bypass it?
除非证书是自签名证书,否则它会给出错误Error Domain=NSURLErrorDomain Code=-1202 UserInfo=0xd29930 "untrusted server certificate".
是否有办法将其设置为接受连接(就像在浏览器中可以按接受)或绕过它的方法?
回答by Gordon Henriksen
There is a supported API for accomplishing this! Add something like this to your NSURLConnection
delegate:
有一个支持的 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];
}
Note that connection:didReceiveAuthenticationChallenge:
can send its message to challenge.sender (much) later, after presenting a dialog box to the user if necessary, etc.
请注意,connection:didReceiveAuthenticationChallenge:
如果需要,可以在向用户显示对话框之后(很长时间)将其消息发送到challenge.sender。
回答by Nathan de Vries
If you're unwilling (or unable) to use private APIs, there's an open source (BSD license) library called ASIHTTPRequestthat provides a wrapper around the lower-level CFNetwork APIs
. They recently introduced the ability to allow HTTPS connections
using self-signed or untrusted certificates with the -setValidatesSecureCertificate:
API. If you don't want to pull in the whole library, you could use the source as a reference for implementing the same functionality yourself.
如果您不愿意(或无法)使用私有 API,则有一个名为ASIHTTPRequest 的开源(BSD 许可)库,它提供了一个围绕较低级别CFNetwork APIs
. 他们最近引入了允许HTTPS connections
在-setValidatesSecureCertificate:
API 中使用自签名或不受信任的证书的功能。如果您不想拉入整个库,您可以使用源代码作为自己实现相同功能的参考。
回答by user890103
Ideally, there should only be two scenarios of when an iOS application would need to accept an un-trusted certificate.
理想情况下,当 iOS 应用程序需要接受不受信任的证书时,应该只有两种情况。
Scenario A: You are connected to a test environment which is using a self-signed certificate.
场景 A:您连接到使用自签名证书的测试环境。
Scenario B: You are Proxying HTTPS
traffic using a MITM Proxy like Burp Suite, Fiddler, OWASP ZAP, etc.
The Proxies will return a certificate signed by a self-signed CA so that the proxy is able to capture HTTPS
traffic.
场景 B:您正在HTTPS
使用代理代理流量MITM Proxy like Burp Suite, Fiddler, OWASP ZAP, etc.
代理将返回由自签名 CA 签名的证书,以便代理能够捕获HTTPS
流量。
Production hosts should never use un-trusted certificates for obvious reasons.
出于显而易见的原因,生产主机不应该使用不受信任的证书。
If you need to have the iOS simulator accept an un-trusted certificate for testing purposes it is highly recommended that you do not change application logic in order disable the built in certificate validation provided by the NSURLConnection
APIs. If the application is released to the public without removing this logic, it will be susceptible to man-in-the-middle attacks.
如果您需要让 iOS 模拟器接受不受信任的证书以进行测试,强烈建议您不要更改应用程序逻辑以禁用NSURLConnection
API提供的内置证书验证。如果应用程序在不删除此逻辑的情况下向公众发布,它将容易受到中间人攻击。
The recommended way to accept un-trusted certificates for testing purposes is to import the Certificate Authority(CA) certificate which signed the certificate onto your iOS Simulator or iOS device. I wrote up a quick blog post which demonstrates how to do this which an iOS Simulator at:
接受不受信任的证书以进行测试的推荐方法是将签署证书的证书颁发机构 (CA) 证书导入您的 iOS 模拟器或 iOS 设备。我写了一篇快速博客文章,演示了如何在 iOS 模拟器中执行此操作:
回答by Nathan de Vries
NSURLRequest
has a private method called setAllowsAnyHTTPSCertificate:forHost:
, which will do exactly what you'd like. You could define the allowsAnyHTTPSCertificateForHost:
method on NSURLRequest
via a category, and set it to return YES
for the host that you'd like to override.
NSURLRequest
有一个名为 的私有方法setAllowsAnyHTTPSCertificate:forHost:
,它将完全按照您的意愿行事。您可以通过类别定义allowsAnyHTTPSCertificateForHost:
方法NSURLRequest
,并将其设置为返回YES
您想要覆盖的主机。
回答by xiang
To complement the accepted answer, for much better security, you could add your server certificate or your own root CA certificate to keychain( https://stackoverflow.com/a/9941559/1432048), however doing this alone won't make NSURLConnection authenticate your self-signed server automatically. You still need to add the below code to your NSURLConnection delegate, it's copied from Apple sample code AdvancedURLConnections, and you need to add two files(Credentials.h, Credentials.m) from apple sample code to your projects.
为了补充接受的答案,为了更好的安全性,您可以将您的服务器证书或您自己的根 CA 证书添加到钥匙串(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];
}
回答by Ryna
I can't take any credit for this, but this one I foundworked really well for my needs. shouldAllowSelfSignedCert
is my BOOL
variable. Just add to your NSURLConnection
delegate and you should be rockin for a quick bypass on a per connection basis.
我不能因此而获得任何荣誉,但我发现这个非常适合我的需求。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;
}
回答by johnnieb
In iOS 9, SSL connections will fail for all invalid or self-signed certificates. This is the default behavior of the new App Transport Securityfeature in iOS 9.0 or later, and on OS X 10.11 and later.
在 iOS 9 中,所有无效或自签名证书的 SSL 连接都将失败。这是iOS 9.0 或更高版本以及 OS X 10.11 和更高版本中新应用传输安全功能的默认行为。
You can override this behavior in the Info.plist
, by setting NSAllowsArbitraryLoads
to YES
in the NSAppTransportSecurity
dictionary. However, I recommend overriding this setting for testing purposes only.
您可以Info.plist
通过在字典中设置NSAllowsArbitraryLoads
to来覆盖 , 中的此行为。但是,我建议仅出于测试目的覆盖此设置。YES
NSAppTransportSecurity
For information see App Transport Technote here.
有关信息,请参阅此处的应用传输技术说明。
回答by Alex Suzuki
The category workaround posted by Nathan de Vries will pass the AppStore private API checks, and is useful in cases where you do not have control of the NSUrlConnection
object.
One example is NSXMLParser
which will open the URL you supply, but does not expose the NSURLRequest
or NSURLConnection
.
Nathan de Vries 发布的类别解决方法将通过 AppStore 私有 API 检查,并且在您无法控制NSUrlConnection
对象的情况下非常有用。一个示例是NSXMLParser
它将打开您提供的 URL,但不公开NSURLRequest
或NSURLConnection
。
In iOS 4 the workaround still seems to work, but only on the device, the Simulator does not invoke the allowsAnyHTTPSCertificateForHost:
method anymore.
在 iOS 4 中,解决方法似乎仍然有效,但仅在设备上,模拟器不再调用该allowsAnyHTTPSCertificateForHost:
方法。
回答by ricardopereira
You have to use NSURLConnectionDelegate
to allow HTTPS connections and there are new callbacks with iOS8.
您必须使用NSURLConnectionDelegate
以允许 HTTPS 连接,并且 iOS8 有新的回调。
Deprecated:
弃用:
connection:canAuthenticateAgainstProtectionSpace:
connection:didCancelAuthenticationChallenge:
connection:didReceiveAuthenticationChallenge:
Instead those, you need to declare:
相反,您需要声明:
connectionShouldUseCredentialStorage:
- Sent to determine whether the URL loader should use the credential storage for authenticating the connection.
connection:willSendRequestForAuthenticationChallenge:
- Tells the delegate that the connection will send a request for an authentication challenge.
connectionShouldUseCredentialStorage:
- 发送以确定 URL 加载程序是否应使用凭据存储来验证连接。
connection:willSendRequestForAuthenticationChallenge:
- 告诉委托,该连接将发送一个身份验证挑战请求。
With willSendRequestForAuthenticationChallenge
you can use challenge
like you did with the deprecated methods, for example:
有了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];
回答by David H
I posted some gist code (based on someone else's work which I note) that lets you properly authenticate against a self generated certificate (and how to get a free certificate - see comments bottom of Cocoanetics)
我发布了一些 gist 代码(基于我注意到的其他人的工作),可让您正确验证自生成的证书(以及如何获得免费证书 - 请参阅Cocoanetics底部的评论)
My code is here github
我的代码在这里github