iOS 9 ATS使用Self-Signed Certficate阻止对服务器的HTTPS请求

时间:2016-02-04 17:04:34

标签: ios objective-c self-signed app-transport-security

这个问题遍布Stack Overflow,我花了两天时间尝试了无数的ATP配置组合,让我的应用程序正常运行。我将彻底解决我的问题,因为看起来最微小的事情会影响如何解决这个问题。

我刚刚建立了一个启用了SSL和TLS 1.2的Ubuntu 14服务器。在我的服务器上是我的应用程序所依赖的服务器端脚本。在我的应用程序中,我使用NSMutableURLRequest从服务器请求我的API,如下所示:

NSString * noteDataString = [NSString stringWithFormat:@"email=%@&password=%@&type=%@", companyEmail, companyPassword, @"login"];
NSData * postData = [noteDataString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString * postLength = [NSString stringWithFormat:@"%lu", (unsigned long)[postData length]];

NSMutableURLRequest * request = [[NSMutableURLRequest alloc] init];

[request setURL:[NSURL URLWithString:@"https://mydomain.me:443/path/to/file.php"]];

[request setHTTPMethod:@"POST"];

[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:postData];

NSHTTPURLResponse * urlResponse = nil;
NSError * error = nil;
NSData * responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&error];
NSString * result = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];

NSLog(@"Response Code: %ld", (long)[urlResponse statusCode]);

当我将网址复制到Chrome时,会显示正确的PHP返回值。在iOS 9上从Xcode 7请求时,我收到此错误:

NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813)

我已经使用了无数的info.plist设置,就像在类似问题中看到的那样。我已经尝试禁用前向保密的需要,我已尝试启用任意加载,我已尝试使用异常域mydomain.me及其子域。我实现的唯一进步是错误代码-9813切换到-9802。

我知道为NSURLRequest添加委托方法,但这些方法从未被调用,并且假定为解决此问题的冗余。

我使用localhost和http在MAMP服务器上构建了很多应用程序,当我启用任意加载时请求工作正常,所以我的plist没有任何问题。

为什么我有一个特殊情况令人难以置信,我知道Stack Overflow就是这种情况的地方!

谢谢,我希望这可以帮助更多的开发人员解决问题。

2 个答案:

答案 0 :(得分:1)

解。感谢大家在评论中指出我正确的方向。解决方案是创建一个NSObject,为这种特殊情况处理NSURLRequests。

感谢:https://www.cocoanetics.com/2010/12/nsurlconnection-with-self-signed-certificates/

以下几乎是链接中教程的直接副本,但我认为留在这里会更容易。

所以,

我必须使用以下代码创建一个新的NSObject类:

BWWebService.h

#import <Foundation/Foundation.h>

@interface BWWebService : NSObject{
    NSMutableData *receivedData;
    NSURLConnection *connection;
    NSStringEncoding encoding;
}

- (id)initWithURL:(NSURL *)url;

@end

BWWebService.m

#import "BWWebService.h"

@implementation BWWebService

- (id)initWithURL:(NSURL *)url{
    if (self = [super init]){
        NSURLRequest * request = [NSURLRequest requestWithURL:url];

        connection = [NSURLConnection connectionWithRequest:request delegate:self];

        [connection start];
    }

    return self;
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
    // Every response could mean a redirect
    receivedData = nil;

    CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)
                                                                               [response textEncodingName]);
encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
    if (!receivedData){
        // No store yet, make one
        receivedData = [[NSMutableData alloc] initWithData:data];
    }else{
        // Append to previous chunks
        [receivedData appendData:data];
    }
}

// All worked
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
    NSString * xml = [[NSString alloc] initWithData:receivedData encoding:encoding];
    NSLog(@"%@", xml);
}

// And error occurred
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
     NSLog(@"Error retrieving data, %@", [error localizedDescription]);
}

// To deal with self-signed certificates
- (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]){
        // we only trust our own domain
        if ([challenge.protectionSpace.host isEqualToString:@"myuntrusteddomain.me"]){
           NSURLCredential * credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
            [challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
        }
    }

    [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}
@end

如果这还不够,要发出请求我使用以下内容替换原来的请求:

NSURL * url = [NSURL URLWithString:@"https://myuntrusteddomain.me:443/path/to/script.php"];
BWWebService * webService;
webService = [[BWWebService alloc] initWithURL:url];

我知道这不会像原版那样POST数据,但后来会出现这种情况。我确信这将是在initWithURL中处理POST的问题。

谢谢大家。

编辑:此解决方案的此应用程序仅适用于允许任意载荷设置为YES。

答案 1 :(得分:0)

打开Xcode,Command-Shift-2,输入9813,你立即找到-9813 = errSSLNoRootCert,这是预期的,因为你的自签名证书没有根证书,而-9802 = errSSLFatalAlert(你真的把它弄错了) )。

问题似乎是出于安全原因,某些软件并不喜欢自签名证书。这通常可以通过创建和安装您自己的根证书并使用您自己的根证书签署的证书来解决。