美文网首页iOS CollectioniOS_小蟹专题iOS
AFNetworking 3.0与服务端 自签名证书 https

AFNetworking 3.0与服务端 自签名证书 https

作者: 好迪 | 来源:发表于2016-04-01 11:25 被阅读7344次

    iOS网络安全现在越来越重要,一个抓包工具(charles等)随随便便就能抓到你所请求的数据,这些数据如果是明码的后果很严重,不是指明文,可以通过这些数据来判定服务端部署的数据接口,更能够嗅探到服务端的漏洞。

    所以最近因公司业务及数据安全上的需要准备使用https方式进行数据请求。而且苹果现在默认是https方式请求。

    Apple CSR CER P12 mobileprovition 到底是什么

    • CSR(Certificate Signing Request)钥匙串文件 为生成证书做基础,要生成CER证书必须要有CSR私钥,此私钥包含了用户自己的一些信息。这个文件是保存在我们的mac的(keychain)里面的, 此文件包含了(公钥和私钥)
    • CER 包含了开发者信息和公钥
    • P12 它不仅包含CER的信息,还有私钥信息,即: P12备份文件 = CER文件 + 私钥;所以有了这个p12就再也不用担心证书丢失了
    • mobileprovition 包含了上述所有内容 Certificate && App ID && Device, 这个Provisioning Profile文件会在打包时嵌入到.ipa的包里。本人理解的是 可以真机调试的凭证。

    https原理

    HTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。 它是一个URI scheme(抽象标识符体系),句法类同http:体系。用于安全的HTTP数据传输。https:URL表明它使用了HTTP,但HTTPS存在不同于HTTP的默认端口及一个加密/身份验证层(在HTTP与TCP之间)。这个系统的最初研发由网景公司(Netscape)进行,并内置于其浏览器Netscape Navigator中,提供了身份验证与加密通讯方法。(摘自百度百科)

    PKI(公钥基础设施)技术是HTTPS的基础,PKI与非对称密钥加密技术密切相关,包括消息摘要、数字签名和加密服务,而数字证书以及证书机构(CA – Certificate Authority)是PKI中的重要概念。

    看看这篇文章

    双向认证原理图:


    httpsyuanlitu.png

    需要准备的自签名证书

    1.服务器私钥 2.由CA签发的含有服务器公钥的数字证书 3.CA的数字证书。在双向认证的实践中,通常服务器可以自己作为证书机构,并且由服务器CA签发服务器证书和客户端证书。

    1.客户端私钥 2. 由CA签发的含有客户端公钥的数字证书。为了避免中间人攻击,客户端还需要内置服务器证书,用来验证所连接的服务器是否是指定的服务器。

    • 服务端 .cer
    • 客户端 .p12

    使用AFNetworking 3.0

        NSString *certFilePath = [[NSBundle mainBundle] pathForResource:@"server" ofType:@"der"];
        NSData *certData = [NSData dataWithContentsOfFile:certFilePath];
        NSSet *certSet = [NSSet setWithObject:certData];
        AFSecurityPolicy *policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate withPinnedCertificates:certSet];
        policy.allowInvalidCertificates = YES;
        policy.validatesDomainName = NO;
         
        _manager = [AFHTTPSessionManager manager];
        _manager.securityPolicy = policy;
        _manager.requestSerializer = [AFHTTPRequestSerializer serializer];
        _manager.responseSerializer = [AFHTTPResponseSerializer serializer];
        _manager.responseSerializer.acceptableContentTypes =  [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript",@"text/plain", nil];
        //关闭缓存避免干扰测试r
        _manager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
        [_manager setSessionDidBecomeInvalidBlock:^(NSURLSession * _Nonnull session, NSError * _Nonnull error) {
            DLog(@"setSessionDidBecomeInvalidBlock");
        }];
    
    //客服端请求验证 重写 setSessionDidReceiveAuthenticationChallengeBlock 方法
    __weak typeof(self)weakSelf = self;   
    [_manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession*session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing*_credential) {
        NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        __autoreleasing NSURLCredential *credential =nil;
        if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
            if([weakSelf.manager.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
                credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
                if(credential) {
                    disposition =NSURLSessionAuthChallengeUseCredential;
                } else {
                    disposition =NSURLSessionAuthChallengePerformDefaultHandling;
                }
            } else {
                disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
            }
        } else {
            // client authentication
            SecIdentityRef identity = NULL;
            SecTrustRef trust = NULL;
            NSString *p12 = [[NSBundle mainBundle] pathForResource:@"client"ofType:@"p12"];
            NSFileManager *fileManager =[NSFileManager defaultManager];
            
            if(![fileManager fileExistsAtPath:p12])
            {
                NSLog(@"client.p12:not exist");
            }
            else
            {
                NSData *PKCS12Data = [NSData dataWithContentsOfFile:p12];
                
                if ([[weakSelf class]extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data])
                {
                    SecCertificateRef certificate = NULL;
                    SecIdentityCopyCertificate(identity, &certificate);
                    const void*certs[] = {certificate};
                    CFArrayRef certArray =CFArrayCreate(kCFAllocatorDefault, certs,1,NULL);
                    credential =[NSURLCredential credentialWithIdentity:identity certificates:(__bridge  NSArray*)certArray persistence:NSURLCredentialPersistencePermanent];
                    disposition =NSURLSessionAuthChallengeUseCredential;
                }
            }
        }
        *_credential = credential;
        return disposition;
    }];
    
    +(BOOL)extractIdentity:(SecIdentityRef*)outIdentity andTrust:(SecTrustRef *)outTrust fromPKCS12Data:(NSData *)inPKCS12Data {
        OSStatus securityError = errSecSuccess;
        //client certificate password
        NSDictionary*optionsDictionary = [NSDictionary dictionaryWithObject:@"你的p12密码"
                                                                    forKey:(__bridge id)kSecImportExportPassphrase];
        
        CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
        securityError = SecPKCS12Import((__bridge CFDataRef)inPKCS12Data,(__bridge CFDictionaryRef)optionsDictionary,&items);
        
        if(securityError == 0) {
            CFDictionaryRef myIdentityAndTrust =CFArrayGetValueAtIndex(items,0);
            const void*tempIdentity =NULL;
            tempIdentity= CFDictionaryGetValue (myIdentityAndTrust,kSecImportItemIdentity);
            *outIdentity = (SecIdentityRef)tempIdentity;
            const void*tempTrust =NULL;
            tempTrust = CFDictionaryGetValue(myIdentityAndTrust,kSecImportItemTrust);
            *outTrust = (SecTrustRef)tempTrust;
        } else {
            NSLog(@"Failedwith error code %d",(int)securityError);
            return NO;
        }
        return YES;
    }
    

    完毕

    • 使用MKNetworkKit 与 使用苹果官方原生 代码片段 详见Demo

    Demo

    https://github.com/cuiwe000/HttpsDemo.git

    参考文档

    相关文章

      网友评论

      • MasterY:我在前端做了检验,结果证书过期了。导致APP无法访问。有没有解决办法
      • hzy1314:您好,楼主,setSessionDidReceiveAuthenticationChallengeBlock:的回调中关于P12的那部分可以详细说明下吗,谢谢
      • 叁號選手:你好!想问个问题,这种是不是服务器那边证书没有配置好的原因?看了很多的文章都和你这一样的,就是不明白为什么一直这个样子的错误。想问下是你能帮我解决下吗?多谢!

        Error Domain=com.alamofire.error.serialization.response Code=-1011 "Request failed: bad request (400)" UserInfo={NSLocalizedDescription=Request failed: bad request (400), NSErrorFailingURLKey=https://api.ff2.pw/index.php, com.alamofire.serialization.response.error.data=<3c68746d 6c3e0d0a 3c686561 643e3c74 69746c65 3e343030 204e6f20 72657175 69726564 2053534c 20636572 74696669 63617465 20776173 2073656e 743c2f74 69746c65 3e3c2f68 6561643e 0d0a3c62 6f647920 6267636f 6c6f723d 22776869 7465223e 0d0a3c63 656e7465 723e3c68 313e3430 30204261 64205265 71756573 743c2f68 313e3c2f 63656e74 65723e0d 0a3c6365 6e746572 3e4e6f20 72657175 69726564 2053534c 20636572 74696669 63617465 20776173 2073656e 743c2f63 656e7465 723e0d0a 3c68723e 3c63656e 7465723e 6e67696e 782f312e 31332e38 3c2f6365 6e746572 3e0d0a3c 2f626f64 793e0d0a 3c2f6874 6d6c3e0d 0a>, com.alamofire.serialization.response.error.response=<NSHTTPURLResponse: 0x1c022db80> { URL: https://api.ff2.pw/index.php } { Status Code: 400, Headers {
        Connection = (
        close
        );
        "Content-Length" = (
        253
        );
        "Content-Type" = (
        "text/html"
        );
        Date = (
        "Fri, 09 Mar 2018 13:06:21 GMT"
        );
        Server = (
        "nginx/1.13.8"
        );
        } }}
      • Ayaoguai:AFSSLPinningModeCertificate 如果我设置这种类型的,就会出-999.但是我设置AFSSLPinningModeNone就不会有这个问题。
        Ayaoguai:@qsyxw 没有
        939aa01ddf09:我遇到的问题和你的是一样,但是一直没有找到原因,你找到原因没呢?
      • Hengry:如何测出 客户端验证服务器有没有成功呢
      • Hengry:双项认证时,疑问:以下代码没有机会执行的吗?
        // client authentication
        SecIdentityRef identity = NULL;
        SecTrustRef trust = NULL;
        NSString *p12 = [[NSBundle mainBundle] pathForResource:@"client"ofType:@"p12"];
        NSFileManager *fileManager =[NSFileManager defaultManager];

        if(![fileManager fileExistsAtPath:p12]){
        NSLog(@"client.p12:not exist");
        }else{

        NSData *PKCS12Data = [NSData dataWithContentsOfFile:p12];
        if ([[weakSelf class] extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data]){

        SecCertificateRef certificate = NULL;
        SecIdentityCopyCertificate(identity, &certificate);
        const void*certs[] = {certificate};
        CFArrayRef certArray = CFArrayCreate(kCFAllocatorDefault, certs,1,NULL);
        credential = [NSURLCredential credentialWithIdentity:identity certificates:(__bridge NSArray*)certArray persistence:NSURLCredentialPersistencePermanent];
        disposition = NSURLSessionAuthChallengeUseCredential;
        }
        }
        Hengry:@DevHank 如果有人碰到请求服务器接口,提示已取消问题。那是因为服务器的域名与证书的域名不一致导致。不需要检验服务器域名即可。
        Hengry:@DevHank 原来是服务器还没做好双向认证。
      • kingboyrang:楼主有没有证书生成的文章?
        好迪:@kingboyrang 没有 你网上看看 很容易的
      • GJCode:您好,您的现在的接口是不是有问题,运行到最后走的是failure,错误信息是Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo={NSErrorFailingURLKey=https://sevnew.welian.com/, NSLocalizedDescription=cancelled, NSErrorFailingURLStringKey=https://sevnew.welian.com/} 请教下是怎么回事儿呢?
        GJCode:@好迪 奥,走到这一步说明双向认证是没有问题的,仅仅就是最后发送数据的的接口问题,这么理解没错吧
        好迪:@GJCode 这已经是一年多前的demo,你自己搭个服务器玩吧
      • KeepMoveingOn:您好,请问下如果内置的证书过期了怎么办? 博主有关于的内置证书的更新的经验或者链接吗? :pray: 感谢
      • LiLi_哩哩:您好,根据您的文章写的方法成功了。但是iOS9 上请求超时,iOS10 和iOS8 都可以。不知道有没有什么思路可以提供
      • oneDemo:我的按照楼主方法写,怎么又内存泄漏了
      • 11eddd582230:请教个问题,我看服务端下发的证书链,AFN中只能通过 NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);,但是这样获取到的NSData类型的,有门有办法获取证书的详细信息
        好迪:@IOS书童 我不是很记得了 ,你可以用 data转String试试,我觉得应该是一串公钥值,获取的data值生成一个.cer文件,然后用 attributesOfItemAtPath,获取文件的属性,大概这样的思路 试试
      • oneDemo: if ([[weakSelf class]extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data])我的怎么这行有内存泄漏
      • leftwater:赞一个
      • maretell:楼主! 证书什么的公司都给弄好了 ,我们前端是不是就把http 改成https就ok了? 不用改什么吧
      • ae52fd37c031:楼主你好。我进也是在弄https。我是用session做的网络请求。证书什么的也是服务器那边弄的免费的。。为什么我弄出来以后在xcode7以上就会报 NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made."。。在低版本的Xcode6上完美运行的。。
        ae52fd37c031:https应该不需要禁用ats吧。实际上五也有设置过,无论怎么设置还是会报错。我怀疑是不是https的自签名证书无法通过ATS的验证
        好迪:@hy082510
        plist里 有木有设置 NSAllowsArbitraryLoads 为YES
      • 小czy:AFN3.1也一样使用吗
        好迪:@小czy 3.1我觉得应该一样,毕竟不是大的改动
      • 27d6e27a9447:楼主,请教一下,这里,是不是应该修改成这样?

        policy.allowInvalidCertificates = NO;
        policy.validatesDomainName = YES;
        27d6e27a9447:喔,刚看见说明,如果是自建证书(无效证书),这个需要设置为YES。
        27d6e27a9447:你好,不知道我理解是否正确,如果allowInvalidCertificates设置为YES,是否就意味着客户端的作用就失效了?因为所有无效证书均可以验证通过。
        好迪:@whitestone 这里我的理解是 是否允许无效的(或者说非法的)证书,一个是否验证的过程
      • 雪_晟:楼主 你这个是单项验证还是双向
        那根经痛了:@雪_晟 您好!请问有单项的链接或者Demo吗。
        雪_晟:@好迪 嗯嗯 刚搞明白,我刚弄好单项的
        好迪:@miss李manman 双向
      • 倒骑毛驴看风月:客户端p12如何生成,后台给了一个pem和一个key的,我生成了,服务端的证书,但是哪个客户端的p12怎么处理
        倒骑毛驴看风月:@好迪 嗯好,谢谢!
        好迪:@_Smile淰憶 你最好让服务端去生成证书导给你,因为我之前就是服务端的证书和我本地证书不匹配
      • liuyan3176:麻烦问下server.der也是服务器端给的吗?
        另外我看plist里面ATS设为了YES,那不是仍通不过17年1月的审核吗
        好迪:@liuyan3176 我都是服务端给的
      • LvCode:大神,明年也就是2017年1月1日,苹果App Store中的所有App都必须启用 App Transport Security(ATS)安全功能。
        有两个问题请教下:
        1、你知道到时候允许自签名证书吗?
        2、自签名证书,图片怎么加载的呢?SDWebImage设置options : SDWebImageAllowInvalidSSLCertificates 不管用。
        LiLi_哩哩:@LvCode 请问您的SDWebImage图片加载的问题解决了吗?如果解决了是用什么方法。
      • 31e4be245ecf:demo编译报错,linker command failed with exit code 1 (use -v to see invocation).
        一叶__知秋:你的找到原因了吗
        一叶__知秋:@好迪 我也报这个问题
        好迪:@CodeIsLife 编译不过,你用cocoapod了吗
      • 南方小金豆:楼主,我把NSAllowsArbitraryLoads下面我设置的NSExceptionDomains 。然后我配置的https请求,全部报错了Error Domain=NSURLErrorDomain Code=-1200 "发生了 SSL 错误,无法建立与该服务器的安全连接。" UserInfo={_kCFStreamErrorCodeKey=-9824, NSLocalizedRecoverySuggestion=您仍要连接此服务器吗?, NSUnderlyingError=0x1744500b0 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, _kCFNetworkCFStreamSSLErrorOriginalValue=-9824, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9824}}, NSLocalizedDescription=发生了 SSL 错误,无法建立与该服务器的安全连接。
        能指点下吗?
        ae52fd37c031:@那份牵挂给了谁 朋友,我也是这个问题,由于公司的需要我还咋使用xcode6.我的https在xcode6上完美运行,一点问题也没有。但是到了7上就有你上面的这种问题了。各种plist设置无效,综合你出现的情况,你觉得我的问题在哪?对了我用的session请求,证书在服务端,证书是免费的
        南方小金豆:@Mandola 服务器证书环境配置有问题
        Mandola:@那份牵挂给了谁 朋友,你的问题解决了吗?我也碰到这个问题了
      • 逍然:我这里按照你的方法,试了之后,还是报:Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." 错误,是证书有问题?证书是私有的证书,一个server的,一个client的。
        好迪:@随意奔跑 不会的,苹果鼓励和支持 https的
        逍然:@好迪 嗯。是跑通了,原因是没有在info.plist里加NSAllowsArbitraryLoaads,不过加上这个貌似就把苹果的安全保障关闭了,不知道会不会对https有影响。
        好迪:@随意奔跑 按照 我那边写 我不是有个 cer嘛 还有个 p12的 ,需要 密码的,填上去,服务端 要配好 服务端 自己 跑通了吗
      • 逍然:您好,问下,重写的setSessionDidReceiveAuthenticationChallengeBlock方法,为啥manger那里却调用的setSessionDidBecomeInvalidBlock方法,另外您的demo里,没有你上面的代码。
        好迪:@随意奔跑 demo 怎么会没有,有啊 你全局 搜搜,setSessionDidBecomeInvalidBlock 可以不用调用 那只是验证的一个 操作。我是这么理解的
      • LvCode:请教下,//客服端请求验证 重写 setSessionDidReceiveAuthenticationChallengeBlock 方法,这个方法怎么修改才能放到AFN中呢,想把这个方法直接放到AFN里面
        好迪:@LvCode 找到原来 要调用重写的代码 直接替换就可以了
        LvCode:@好迪 对的,楼主,怎么修改呢,想直接放进去,不想每次重写
        好迪:@LvCode 你的意思是 修改 AFN 的里面的方法吗
      • 西西西瓜sama:感谢文章,但有个疑问,如果按照理论来讲 cer证书应该是向后台请求数据时 后台返回给我们 我们再进行安装 但代码中是自己生成 然后放到本地 这样的话不符合流程 另外我用您代码试了一下 成功了 但返回的是nsdata数据 不知道这种情况该如何处理
        weineeL:@西西西瓜sama 我也遇到这个问题, 把 _manager.responseSerializer = [AFHTTPResponseSerializer serializer]; 改成 _manager.responseSerializer = [AFJSONResponseSerializer serializer]; 就可以了
        好迪:@西西西瓜sama https 有单项认证和双向认证,这些概念你可以 谷歌 和百度 一堆,你的data返回 不知道 是什么类型,我这不是,你可以转NSString 试试,

      本文标题:AFNetworking 3.0与服务端 自签名证书 https

      本文链接:https://www.haomeiwen.com/subject/yykylttx.html