如何使用 NSURLConnection 连接不可信证书的SSL站点?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (10)
  • 关注 (0)
  • 查看 (339)

我使用以下代码连接到使用了SSL的网页

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

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

提问于
用户回答回答于

有一个 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 的 sender (更多),以此类推。

用户回答回答于

我贴了一些代码在 gist 上,这些代码给予我关注的其他人的工作。这些代码允许你使用自己生成的证书进行验证。

我的代码在这里GitHub

用户回答回答于

你必须用NSURLConnectionDelegate来允许使用HTTPS连接,在 iOS 8 中有一个新的回调。

过期的:

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];
用户回答回答于

Nathan de Vries 发布的解决方案可以避过 Appstore 的私有 API 检查,这对你无法控制 NSUrlConnection 时非常有用。

比如说, NSXMLParser 会打开你提供的URL,但是不会使用公开的 NSURLRequest 和 NSURLConnection .在 IOS 4 设备中,这个方法依然奏效,在模拟器中不会调用 allowsAnyHTTPSCertificateForHost 方法。

用户回答回答于

在IOS 9中,所有无效或自签名证书的SSL连接都将报错。这是新的应用程序传输安全在iOS 9.0或更高版本以及OSX 10.11及更高版本中的所规定的特性。

可以在Info.plist,通过在NSAppTransportSecurity设置NSAllowsArbitraryLoadsYES但是,我建议仅为测试目的修改此设置。

有关信息,请参阅官方的AppTransportTechnote ,地址在这儿...

用户回答回答于

我找到了这个对我的需求很有帮助。shouldAllowSelfSignedCert是我的BOOL变量。只要加上你的NSURLConnection委托和你应该是摇滚乐的一个快速旁路的基础上,每个连接。

我没有打算获取信用点,不过我找到了对我自己的需求非常有帮助的方法,shouldAllowSelfSignedCert是我的BOOL变量,只需要在你的代理中添加这样一个 Workaround 方法来实现。

- (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;
}
用户回答回答于

为了补充接受的答案,为了获得更好的安全性,您可以将服务器证书或自己的根CA证书添加到密钥链(https://stackoverflow.com/a/9941559/1432048),但是单独这样做并不会使NSURLConnection自动对您的自签名服务器进行身份验证。

你可以将服务器证书或自己的根 CA 证书添加到密钥链中,不过单独这样做没用。你需要将下面的代码加入到 NSURLConnection 的代理中,这是我从苹果官方的文档中复制出来的高级连接,你需要从官方的演示项目中添加两个文件(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];
}
用户回答回答于

NSURLRequest有一个名为setAllowsAnyHTTPSCertificate:forHost:的API,它能做你想做的事。您可以在NSURLRequest上定义allowsAnyHTTPSCertificateForHost:方法,并将其设置为返回YES来覆盖你需要的的主机。

用户回答回答于

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

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

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

生产环境的主机不应使用不受信任的证书。原因看这里...

如果您需要让IOS模拟器因测试而接受不受信任的证书,强烈建议您不要更改应用程序逻辑,去禁用NSURLConnectionAPI。如果该应用程序向公众发布而不删除此逻辑,它会很容易受到中间人攻击。

因测试目的而接受不受信任证书的推荐方法是将证书颁发机构(CA)证书导入到IOS模拟器或IOS设备上,该证书将该证书签名到您的IOS模拟器或IOS设备上。

我写了一篇简短的博客文章,演示了iOS模拟器如何做到这一点:

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

用户回答回答于

如果您不愿意(或无法)使用私有API,则有一个开源(BSD许可证)的库ASIHTTPRequest,它提供了一个较低级别的包装器CFNetwork APIs

他们最近引入了允许HTTPS connections,使用自签名或不受信任的证书-setValidatesSecureCertificate:API。

如果您不想使用整个库,您可以使用其源码源作为自己实现相同功能的参考。

所属标签

可能回答问题的人

  • 西风

    renzha.net · 站长 (已认证)

    7 粉丝1 提问9 回答
  • 四无君

    0 粉丝0 提问3 回答
  • 拉布拉多拉不多

    1 粉丝0 提问2 回答
  • 小书虫

    0 粉丝1 提问2 回答

扫码关注云+社区

领取腾讯云代金券