https验证域名 ios_ios 怎样验证域名是否存在? - CSDN
  • - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {  return [protectionSpace.authenticationMethod isEqualToString:NSU
    <span style="font-size:14px;">
    
    
    - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
    
        return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
    
    }
    
    - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    
        NSLog(@"didReceiveAuthenticationChallenge %@ %zd", [[challenge protectionSpace] authenticationMethod], (ssize_t) [challenge previousFailureCount]);
    
        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]){
    
            [[challenge sender]  useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
            [[challenge sender]  continueWithoutCredentialForAuthenticationChallenge: challenge];
        }
    }
    
    </span>


    展开全文
  • 一、http和https的区别 引用度娘的一段话 HTTPS和HTTP的区别 超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息。HTTP协议以明文方式发送内容,不提供任何方式的数据加密,如果攻击者...

    一、http和https的区别

    引用度娘的一段话

    HTTPS和HTTP的区别

    超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息。HTTP协议以明文方式发送内容,不提供任何方式的数据加密,如果攻击者截取了Web浏览器和网站服务器之间的传输报文,就可以直接读懂其中的信息,因此HTTP协议不适合传输一些敏感信息,比如信用卡号、密码等。

    为了解决HTTP协议的这一缺陷,需要使用另一种协议:安全套接字层超文本传输协议HTTPS。为了数据传输的安全,HTTPS在HTTP的基础上加入了SSL协议,SSL依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。

    HTTPS和HTTP的区别主要为以下四点:

    一、https协议需要到ca申请证书,一般免费证书很少,需要交费。

    二、http是超文本传输协议,信息是明文传输,https 则是具有安全性的ssl加密传输协议。

    三、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。

    四、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。(引自度娘)

    上面的那一坨总结出来就是,http数据为明文传输,被拦截后就直接明文可以看到数据;而https数据为加密传输,即使被拦截到了,那也是乱码。当然,这里应该有抓包过https的小伙伴表示不服,这个下面会说到的。

    因为加上了这一ssl层,所以https在整个传输过程中,大部分时间都是消耗在了ssl的认证、加密中,所以相比于http,速度还是会慢一点的,所以上面说视频流媒体的app可以不用强制https,因为太耗时间了。。。

    二、https之服务器简单聊一聊

    因为本人并不是服务器开发,所以这个服务器这方面就提一下。

    要想从http升级成https,那得先服务器认证。

    就是要证明你这个服务器,是你所声明的服务器。没错,就像天朝的要证明你是你自己,你证明你妈是你吗。。。表示心疼需要证明的小伙伴。。。。

    那怎么证明你妈是你妈,呸。。。不是,是证明你的服务器,就是你所声明的服务器那?你说你是百度,我还说我是那!!

    所以,这里就涉及到了一个权威的机构登场CA (Certificate Authority)!!!

    采用https的服务器必须从CA (Certificate Authority)申请一个用于证明服务器用途类型的证书。该证书只有用于对应的服务器的时候,客户端才信任此主机。(一般这种证书都是要花钱的买的。。。。)

    举个栗子

    你用浏览器访问一些网站的时候,浏览器会自动验证网站的证书,如果证书不是CA签发的,那么浏览器会提示提示你,此网站的证书无效(因为不是指定的机构签发的,有可能是自己签发的),如下图



    当不是正规机构签发的证书

    有木有人看着眼熟,对,你登录12306的时候,有木有!!

    堂堂大铁路局的12306证书竟然不是正规机构签发的!!要知道浏览器连草榴都信任,说明草榴的证书都是正规买的,12306竟然后还是自己签发的。。。就差这点钱么?

    如果服务器买好了证书的话,因为要符合苹果的ATS政策

    ·服务器所有的连接使用TLS1.2以上版本

    ·HTTPS证书必须使用SHA256以上哈希算法签名

    ·HTTPS证书必须使用RSA 2048位或ECC 256位以上公钥算法

    还有证书还要是符合苹果认同的,最近沃通的证书好像就快到期了,所以是沃通签发的赶紧去重新买。

    配置完的可以用这个来查看是否符合苹果的要求,传送门SSL证书 - 腾讯云

    三、https流程

    其实百度https,上面就有https的流程(我是传送门https_百度百科),所以这里简单的说下,其中的各种算法、参数的交换在下面的步骤就不细说了,请自行传送门。

    这里分https的单向认证和双向认证(单向还是双向这个需要服务器去配置的),所以分开来说,要不小伙伴们会蒙的

    单向认证

    材料:买来的服务器证书server.cer(客户端要放一个,用来验证服务端),客户端,服务器。

    数据加密基本原理:RSA加密+对称加密(数据data经对称密钥key加密,然后把对称密钥key经RSA公钥加密)

    1、客户端向服务器发起请求。

    2、服务器响应到请求,同时把服务器的证书发给客户端。

    3、客户端接收到证书,然后和客户端中的证书对比,如果证书不一致或者无效,那么断开连接。如果通过,那么进行第四部。

    4、用户产生一个随机密钥,然后经服务器证书中的公钥进行加密,传给服务端。

    5、服务端拿到加密数据和加密密钥,用服务器的私钥解开密钥,得到对称密钥key。

    6、服务端和客户端互相通讯指定这个密钥为加密密钥。握手结束

    7、客户端和服务端开始通讯,通讯数据由对称密钥加密。


    简易图(对付看吧)


    双向认证

    双向认证比单向认证多了一步,就是服务器要认证客户端,按照百度百科上的步骤,客户端应该有一个由CA(或正规机构)签发的p12证书,和CA根证书(签名的p12就是由这个签名的)

    //上面的CA根证书和p12证书   其实都可以自签的,下面介绍的也是自签的

    材料:买来的服务器证书server.cer(客户端要放一个,用来验证服务端),客户端,服务器端,CA的根证书(放到服务器中,用来验证客户端的证书),p12证书(用来放到客户端,网络请求的时候会传给服务端)。

    步骤:就是在上面第4步的时候,要用p12文件来对一段数据进行签名,然后把签名和p12证书,加密的对称密钥(上面的说过的)传给服务器,然后服务器接到以后,会用CA根证书(或自签的根证书)来对证书和签名数据进行验证,如果正确,通讯继续,否则,断开连接。

    其他的都是一样的。

    四、自签证书的过程

    p12和CA根证书,其实也可以自签的,现在说下自签的过程。

    首先,先有个工具,xca


    xca

    就是这个东西,然后安装以后


    xca界面


    首先,先生生成一个datacode



    新建database



    填写database名字和存放地址



    设置database的密码,并确认

    然后就会生成一个这个东西


    然后根据这个database生成一个CA根证书(当然,是自签的了)




    根证书ca设置

    根证书的具体设置



    根证书CA私钥的设置

    直接点确定,然后就会提示你privatekey创建好了,然后直接确定到下面这个图


    根证书创建成功后会在xca中显示出来的

    然后导出CA根证书


    根证书CA的导出



    就是它

    这时候,自签的CA根证书就创建好了,这个CA证书是要放到服务器上的。一般默认的CA证书时间是10年。

    然后这时候,就要创建自签的p12证书了。选中这个CA证书,然后


    创建p12证书



    设置p12证书的源头

    然后subject里面和设置CA证书一样。

    p12证书默认是一年的,如果感觉短的话   可以在Extensions中设置时间。

    然后把P12证书放到客户端项目里。

    五、iOS端的https双向适配

    AFN的https的双向适配2.x和3.0不一样,先说3.0

    客户端认证服务端,

    shareManager = [AFHTTPSessionManager manager];

    shareManager.securityPolicy = [self customSecurityPolicyWithCerName:HPWalletServerCerName];//调用方法

    //客户端认证服务端的方法

    + (AFSecurityPolicy *)customSecurityPolicyWithCerName:(NSString *)cerName{

    //导入证书路径

    NSString *cerPath = [[NSBundle mainBundle] pathForResource:cerName ofType:@""];

    //加载证书

    NSData *cerData = [NSData dataWithContentsOfFile:cerPath];

    NSSet *certSet = [NSSet setWithObject:cerData];

    //使用AFSSLPinningModeCertificate证书验证模式

    AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate withPinnedCertificates:certSet];

    //allowInvalidCertificates 是否允许无效证书(也就是自建的证书),默认为NO

    //如果是需要验证自建证书,需要设置为YES

    securityPolicy.allowInvalidCertificates = NO;

    //是否需要验证域名,默认为YES

    //假如证书的域名与你请求的域名不一致,需把该项设置为NO;如设成NO的话,即服务器使用其他可信任机构颁发的证书,也可以建立连接,这个非常危险,建议打开。

    //置为NO,主要用于这种情况:客户端请求的是子域名,而证书上的是另外一个域名。因为SSL证书上的域名是独立的,假如证书上注册的域名是www.google.com,那么mail.google.com是无法验证通过的;当然,有钱可以注册通配符的域名*.google.com,但这个还是比较贵的。

    //如置为NO,建议自己添加对应域名的校验逻辑。

    securityPolicy.validatesDomainName = YES;

    return securityPolicy;

    }

    重写AFN的方法  来让服务器认证客户端

    {//新的https

    shareManager.securityPolicy = [self customSecurityPolicyWithCerName:HPWalletServerCerName];

    //客户端请求验证,重写setSessionDidReceiveAuthenticationChallengeBlock方法

    [shareManager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing *_credential) {

    NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;

    __autoreleasing NSURLCredential *credential = nil;

    if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {

    if([shareManager.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:@"p12文件的名字" ofType:@"p12"];

    NSFileManager *fileManager =[NSFileManager defaultManager];

    if(![fileManager fileExistsAtPath:p12]) {

    NSLog(@"hpwallet-client.p12:not exist");

    } else {

    NSData *PKCS12Data = [NSData dataWithContentsOfFile:p12];

    if ([[self 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;

    }


    AFN2.x适配

    客户端认证服务器

    manage.securityPolicy = [HttpServiceAFNetworkImp customSecurityPolicy];

    + (AFSecurityPolicy *)customSecurityPolicy

    {

    NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"服务器.cer证书的名字"ofType:@"der"];

    NSData *cerData = [NSData dataWithContentsOfFile:cerPath];

    AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];

    //allowInvalidCertificates 是否允许无效证书(也就是自建的证书),默认为NO

    //如果是需要验证自建证书,需要设置为YES

    securityPolicy.allowInvalidCertificates = NO;

    //是否需要验证域名,默认为YES

    //假如证书的域名与你请求的域名不一致,需把该项设置为NO;如设成NO的话,即服务器使用其他可信任机构颁发的证书,也可以建立连接,这个非常危险,建议打开。

    //置为NO,主要用于这种情况:客户端请求的是子域名,而证书上的是另外一个域名。因为SSL证书上的域名是独立的,假如证书上注册的域名是www.google.com,那么mail.google.com是无法验证通过的;当然,有钱可以注册通配符的域名*.google.com,但这个还是比较贵的。

    //如置为NO,建议自己添加对应域名的校验逻辑。

    securityPolicy.validatesDomainName = YES;

    //放入证书

    securityPolicy.pinnedCertificates = @[cerData];

    return securityPolicy;

    }


    服务器认证客户端,这个要把AFN的AFURLConnectionOperation.m文件中  - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge方法替换掉  替换成

    - (void)connection:(NSURLConnection *)connection

    willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge

    {

    NSString *thePath = [[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"];

    //倒入证书       NSLog(@"thePath===========%@",thePath);

    NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath];

    CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data;

    SecIdentityRef identity = NULL;

    // extract the ideneity from the certificate

    [self extractIdentity :inPKCS12Data toIdentity:&identity];

    SecCertificateRef certificate = NULL;

    SecIdentityCopyCertificate (identity, &certificate);

    const void *certs[] = {certificate};

    //                        CFArrayRef certArray = CFArrayCreate(kCFAllocatorDefault, certs, 1, NULL);

    // create a credential from the certificate and ideneity, then reply to the challenge with the credential

    //NSLog(@"identity=========%@",identity);

    NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identity certificates:nil persistence:NSURLCredentialPersistencePermanent];

    //           credential = [NSURLCredential credentialWithIdentity:identity certificates:(__bridge NSArray*)certArray persistence:NSURLCredentialPersistencePermanent];

    [challenge.sender useCredential:credential forAuthenticationChallenge:challenge];

    }

    并添加下面的方法

    - (OSStatus)extractIdentity:(CFDataRef)inP12Data toIdentity:(SecIdentityRef*)identity {

    OSStatus securityError = errSecSuccess;

    CFStringRef password = CFSTR("证书密码");

    const void *keys[] = { kSecImportExportPassphrase };

    const void *values[] = { password };

    CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);

    CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);

    securityError = SecPKCS12Import(inP12Data, options, &items);

    if (securityError == 0)

    {

    CFDictionaryRef ident = CFArrayGetValueAtIndex(items,0);

    const void *tempIdentity = NULL;

    tempIdentity = CFDictionaryGetValue(ident, kSecImportItemIdentity);

    *identity = (SecIdentityRef)tempIdentity;

    }

    if (options) {

    CFRelease(options);

    }

    return securityError;

    }


    就好了  大功搞成,好长啊。。。

    展开全文
  • 关于iOS的ATS适配和ssl双向验证,在我的项目中都实现了,但是对于苹果的审核,一定要详细的说明在plist文件你配置的键值是做什么的.

    一,说明

    在WWDC 2016开发者大会上,苹果宣布了一个最后期限:到2017年1月1日 App Store中的所有应用都必须启用 App Transport Security安全功能。App Transport Security(ATS)是苹果在iOS 9中引入的一项隐私保护功能,屏蔽明文HTTP资源加载,连接必须经过更安全的HTTPS。苹果目前允许开发者暂时关闭ATS,可以继续使用HTTP连接,但到年底所有官方商店的应用都必须强制性使用ATS。

    补充:强制ATS的日期为2017年1月1日现已延期. 具体日期还没出来,今年可以过个好年了.链接:https://developer.apple.com/news/?id=12212016b


    项目说明: 一开始听到这个消息,我对ATS的概念一知半解. 经过5天的时间,终于搞好了,在此记录一下.  我的项目中用到AFNetwork3.x,ASIHttpRequest,UIWebView.   在文章后面会一一讲解. 

    我们后台开发的同学,用了nginx服务器,直接将http转为HTTPS.开发只用了一周.

    首先后台会给你提供很多的证书,但是客户端只要几个证书,下面根据你的需求,选择证书.


    (1),如果你的项目只支持ATS,单向验证,不需要ssl双向验证,你只需要金色的ca.cer证书(注:可将ca.crt转为ca.cer);
    用openssl 将crt转为cer. 下面是2条命令:

    (2),如果你的项目支持ATS,和ssl双向认证,那么你需要ca.cer证书,server.cer证书,client.p12证书(注:client.p12是client导入到钥匙串,在导出来的.注意导出时要记住密码"xxx"),这3个证书.
    (3,)如果你的项目除了用到AFNetworking,还用到ASIHttpRequest, 那么你还需要client.pfx证书.(注:client.pfx是后台给的);

    在项目开始前,在下图将plist里面的字段整个删掉:



    1,如果你的项目只要支持ATS,不需要ssl双向验证. 那么你叫后台代码注释ssl双向验证功能(如果后台开启双向验证),只支持单向验证就可以.
    我的所有接口用的是AFN3.x 所以我不会说AFN2.x(因为2.x不支持ipv6).  
    下面是代码说明:
    //只支持ATS,单向验证
    _manager=[SHHttpRequestClient sharedSessionManager];
        _manager.responseSerializer=[AFHTTPResponseSerializer serializer];
        _manager.requestSerializer.timeoutInterval=12;
        SHAFSecurityPolicy *securitypolicy=[SHAFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
        _manager.securityPolicy=securitypolicy;
        _manager.securityPolicy.allowInvalidCertificates=YES;//是否允许CA不信任的证书通过
        [_manager.securityPolicy setValidatesDomainName:NO];//是否验证主机名
        __weak typeof(self) weakSelf = self;
        [_manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing *_credential) {
            
            SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];
            /**
             *  导入多张CA证书(Certification Authority,支持SSL证书以及自签名的CA),请替换掉你的证书名称
             */
            NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"ca" ofType:@"cer"];//自签名证书
            NSData* caCert = [NSData dataWithContentsOfFile:cerPath];
            weakSelf.manager.securityPolicy.pinnedCertificates = [NSSet setWithObjects:caCert,nil];
            SecCertificateRef caRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)caCert);
            NSCAssert(caRef != nil, @"caRef is nil");
            NSArray *caArray = @[(__bridge id)(caRef)];
            NSCAssert(caArray != nil, @"caArray is nil");
            OSStatus status = SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)caArray);
            SecTrustSetAnchorCertificatesOnly(serverTrust,NO);
            //NSCAssert(errSecSuccess == status, @"SecTrustSetAnchorCertificates failed");
            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 {
                disposition = NSURLSessionAuthChallengePerformDefaultHandling;
            }
            *_credential=credential;
            return disposition;
        }];
        [_manager GET:path parameters:params progress:^(NSProgress * _Nonnull downloadProgress) {
        } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        }];
    
    将上面的代码拷贝到你的项目中,如果发现你的https接口请求时崩溃,那么是校验服务端证书个数和客户端信任个数不一致,那么此时要修改源码.

    由于服务端使用.jks是一个证书库,客户端获取到的证书可能不止一本,具体获取到基本可通过SecTrustGetCertificateCount方法获取证书个数,AFNetworking在evaluateServerTrust:forDomain:方法中,AFSSLPinningMode的类型为AFSSLPinningModeCertificate和AFSSLPinningModePublicKey的时候都有校验服务端的证书个数与客户端信任的证书数量是否一样,如果不一样的话无法请求成功,所以这边我就修改他的源码,当有一个校验成功时即算成功。
    在AFSecurityPolicy.m的-(BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain修改代码:
    case AFSSLPinningModeCertificate: {
                NSMutableArray *pinnedCertificates = [NSMutableArray array];
                for (NSData *certificateData in self.pinnedCertificates) {
                    [pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
                }
                SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
    
                if (!AFServerTrustIsValid(serverTrust)) {
                    return NO;
                }
    
                // obtain the chain after being validated, which *should* contain the pinned certificate in the last position (if it's the Root CA)
                NSUInteger trustedCertificateCount = 0;//这个地方修改
                NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
                
                for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
                    if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
                        trustedCertificateCount++;//这个地方修改
                        //return YES;
                    }
                }
                
                return trustedCertificateCount == [serverCertificates count] - 1;//这个地方修改
                //return NO;
            }
            case AFSSLPinningModePublicKey: {
                NSUInteger trustedPublicKeyCount = 0;
                NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);
    
                for (id trustChainPublicKey in publicKeys) {
                    for (id pinnedPublicKey in self.pinnedPublicKeys) {
                        if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
                            trustedPublicKeyCount += 1;
                        }
                    }
                }
                return trustedPublicKeyCount > 0 && ( trustedPublicKeyCount >= 1);//这个地方修改
                //return trustedPublicKeyCount > 0;
            }
    

    同时,在AFHTTPSessionManager.m的

    - (void)setSecurityPolicy:(SHAFSecurityPolicy *)securityPolicy

    方法中将

    @throw [NSException exceptionWithName:@"Invalid Security Policy" reason:reason userInfo:nil];注释掉如下图:



    完成,运行,发现所有的https的接口请求都有数据,但是加载的https的H5页面和https的网络图片加载不上,怎么解决? 在下面会讲解. 但是先将ssl双向验证讲完.


    2,如果你的项目既要支持ATS,还需要ssl双向验证. 那么你叫后台开启ssl双向验证功能.
    这个时候还需要server.cer证书和client.p12及client.p12对于的密码@"xxx".
    下面是代码讲解:
    - (void)startGetRequest:(NSDictionary *)params withPath:(NSString *)path{
        _manager=[SHHttpRequestClient sharedSessionManager];
        _manager.responseSerializer=[AFHTTPResponseSerializer serializer];
        _manager.requestSerializer.timeoutInterval=12;
        NSString *cerpth=[[NSBundle mainBundle]pathForResource:@"server" ofType:@"cer"];
        NSData *cerdat=[NSData dataWithContentsOfFile:cerpth];
        SHAFSecurityPolicy *securitypolicy=[SHAFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate withPinnedCertificates:[NSSet setWithObjects:cerdat,nil]];
        _manager.securityPolicy=securitypolicy;
        _manager.securityPolicy.allowInvalidCertificates=YES;//是否允许CA不信任的证书通过
        [_manager.securityPolicy setValidatesDomainName:NO];//是否验证主机名
        __weak typeof(self) weakSelf = self;
        [_manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing *_credential) {
            SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];
            /**
             *  导入多张CA证书(Certification Authority,支持SSL证书以及自签名的CA),请替换掉你的证书名称
             */
            NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"ca" ofType:@"cer"];//自签名证书
            NSData* caCert = [NSData dataWithContentsOfFile:cerPath];
            weakSelf.manager.securityPolicy.pinnedCertificates = [NSSet setWithObjects:caCert,nil];
            SecCertificateRef caRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)caCert);
            NSCAssert(caRef != nil, @"caRef is nil");
            NSArray *caArray = @[(__bridge id)(caRef)];
            NSCAssert(caArray != nil, @"caArray is nil");
            OSStatus status = SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)caArray);
            SecTrustSetAnchorCertificatesOnly(serverTrust,NO);
            //NSCAssert(errSecSuccess == status, @"SecTrustSetAnchorCertificates failed");
            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 {//自签证书验证, 是对服务端返回的证书与客户端本地证书验证是否一致
                //disposition = NSURLSessionAuthChallengePerformDefaultHandling;
                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 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;
        }];
        [_manager GET:path parameters:params progress:^(NSProgress * _Nonnull downloadProgress) {
        } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        }];
    }
    - (BOOL)extractIdentity:(SecIdentityRef *)outIdentity andTrust:(SecTrustRef*)outTrust fromPKCS12Data:(NSData *)inPKCS12Data
    {
        OSStatus securityError = errSecSuccess;
        
        NSDictionary *optionsDictionary = [NSDictionary dictionaryWithObject:@"xxx" forKey:(id)kSecImportExportPassphrase];
        
        CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
        securityError = SecPKCS12Import((CFDataRef)inPKCS12Data,(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(@"--------证书错误------- %d",(int)securityError);
            return NO;
        }
        return YES;
    }
    
    完成,如果你上传的server.cer证书是没错的,那么求https的接口,是有返回数据的. 
    3,关于webView加载H5页面的问题, 由于项目支持iOS7,所以这里只讲UIWebView. 
    (1)如果你的服务端是单向验证,那么Https的H5是可以加载的.  
    (2)如果你的H5的页面全都是https.那么需要ssl双向认证. 由于NSURLConnection被废了.所以这里只讲NSURLsession的双向验证.
    - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
        if ([[[NSURL URLWithString:_baseUrl] scheme] isEqualToString:@"https"]) {
            if (!isOnceTrue) {
                isOnceTrue=NO;//全局定义一个BOOL类型的属性,只作为每当打开webview时一次性ssl双向验证
                //开启同步的请求去双向认证
                NSURLSessionConfiguration *conf = [NSURLSessionConfiguration defaultSessionConfiguration];
                NSOperationQueue *queue = [[NSOperationQueue alloc] init];
                queue.name = @"Mutual Author";
                //NSURLSession *session = [NSURLSession sharedSession];
                NSURLSession *session = [NSURLSession sessionWithConfiguration:conf delegate:self delegateQueue:queue];
                NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
                    if ([error localizedDescription].length==0) {
                        [self loadHtmlorData];//证书验证正确,则会求H5页面
                    }
                }];
                [task resume];
                [webView stopLoading];
                return NO;
            }
        }
        return YES;
    

    #pragma mark --SSL
    #pragma mark -- NSURLSession Delegate --
    
    - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
        NSString *method = challenge.protectionSpace.authenticationMethod;
        NSLog(@"challenge auth method:%@",method);
        if ([method isEqualToString:NSURLAuthenticationMethodServerTrust]) {
            NSString *host = challenge.protectionSpace.host;
            NSLog(@"host:%@",host);
            
            SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
            BOOL validDomain = false;
            NSMutableArray *polices = [NSMutableArray array];
            if (validDomain) {
                [polices addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)host)];
            }else{
                [polices addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
            }
            SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)polices);
            //pin mode for certificate
            NSString *path = [[NSBundle mainBundle] pathForResource:@"server" ofType:@"cer"];
            NSData *certData = [NSData dataWithContentsOfFile:path];
            NSMutableArray *pinnedCerts = [NSMutableArray arrayWithObjects:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certData), nil];
            SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCerts);
            
            NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
            completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
            return;
        }
        
        //client authentication
        NSString *thePath = [[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"];
        NSData *pkcs12Data = [NSData dataWithContentsOfFile:thePath];
        CFDataRef inPKCS12Data = (CFDataRef)CFBridgingRetain(pkcs12Data);
        SecIdentityRef identity;
        
        OSStatus ret = [self extractP12Data:inPKCS12Data toIdentity:&identity];
        if (ret != errSecSuccess) {
            completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge,nil);
            return;
        }
        isOnceTrue=YES;
        SecCertificateRef certificate = NULL;
        SecIdentityCopyCertificate(identity, &certificate);
        const void *certs[] = {certificate};
        CFArrayRef certArray = CFArrayCreate(kCFAllocatorDefault, certs, 1, NULL);
        NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identity certificates:(NSArray *)CFBridgingRelease(certArray) persistence:NSURLCredentialPersistencePermanent];
        completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
    }
    
    - (OSStatus)extractP12Data:(CFDataRef)inP12Data toIdentity:(SecIdentityRef *)identity {
        OSStatus securityErr = errSecSuccess;
        
        CFStringRef pwd = CFSTR("xxx");
        const void *keys[] = {kSecImportExportPassphrase};
        const void *values[] = {pwd};
        
        CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
        CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
        securityErr = SecPKCS12Import(inP12Data, options, &items);
        
        if (securityErr == errSecSuccess) {
            CFDictionaryRef ident = CFArrayGetValueAtIndex(items, 0);
            const void *tmpIdent = NULL;
            tmpIdent = CFDictionaryGetValue(ident, kSecImportItemIdentity);
            *identity = (SecIdentityRef)tmpIdent;
        }
        
        if (options) {
            CFRelease(options);
        }
        
        return securityErr;
    }
    
    完成,UIWebview的ssl双向验证成功.  但是加载https的H5页面会出现布局混乱,页面显示不全. 这是因为H5相关联的请求都是http请求, 我暂时没发现客户端可以解决这个问题.在本博客更新部分已解决这个问题.请查看.
    上面的UIwebview加载https的H5页面,同时要双向验证的问题没解决,这是我想,将所有的H5页面的https都换为http,这样就不存在页面混乱,显示不全的问题.   要写UIWebView成功加载http的H5页面同时又不影响ATS. 有2种方法:
    第一:如下图所示;在plist文件中添加例外,(例外最好都有以下3个字段):



    NSIncludesSubdomains 设为YES表示:该域名的ATS配置适配其子域名
    NSExceptionRequiresForwardSecrecy 设为NO表示访问该域名时TLS可以不支持完全正向保密
    NSExceptionAllowsInsecureHTTPLoads 设为YES表示该域名可以通过http请求,但是TLS版本要求还是1.2.

    如果加载的http的H5页面的域名和你的服务器域名一样,那么你就不能通过添加例外解决该问题.只能通过第二种方法
    第二:在iOS10以上可以通过设置Allow Arbitrary Loads in Web Content 来允许请求http,iOS9那么可以设置Allow Arbitrary Loads.如下图:


    【注意】NSAllowsArbitraryLoadsNSAllowsArbitraryLoadsInMediaNSAllowsArbitraryLoadsInWebContentNSExceptionAllowsInsecureHTTPLoads任一属性置为YES或设置NSExceptionMinimumTLSVersion,都会触发App Store的额外审核并需要提交对应说明.  但是对于以上设置,我不能保证审核一定能够通过,我选的是第二种方法.
     
    此时通过AFN3.x请求接口可以成功,UIWebview加载http的H5页面也是正常显示的.那么对于记载https的网络图片如果不能正常显示,那么SDWebImage就要换一种方法,如下图:


    如果https的网络图片还是不能显示,要么添加例外,要么将https改为http.提交时进行说明.
    运行成功了,报了一个错:友盟的第三方不支持https

    解决方法:添加例外:

    4关于ASIHttpRequest的ssl双向验证的问题,那么需要client.pfx证书
    以下是代码讲解:
    SecIdentityRef identity = NULL;
        SecTrustRef trust = NULL;
        NSData *PKCS12Data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"client" ofType:@"pfx"]];
        [self extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data];
        request1 = [ASIHTTPRequest requestWithURL:url];
        [request1 setClientCertificateIdentity:identity];
        [request1 setValidatesSecureCertificate:YES];
        [request1 setDelegate:self];
        [request1 startAsynchronous];
    - (BOOL)extractIdentity:(SecIdentityRef *)outIdentity andTrust:(SecTrustRef*)outTrust fromPKCS12Data:(NSData *)inPKCS12Data
    {
        OSStatus securityError = errSecSuccess;
        
        NSDictionary *optionsDictionary = [NSDictionary dictionaryWithObject:@"xxx" forKey:(id)kSecImportExportPassphrase];
        
        CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
        securityError = SecPKCS12Import((CFDataRef)inPKCS12Data,(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(@"Failed with error code %d",(int)securityError);
            return NO;
        }
        return YES;
    }
    
    OK,此时运行,报错:
    原因是我们客户端证书链的顶层是自签的证书,不受信任.
    解决方法:还是修改源码:在ASIHTTPRequest.m的-(void)startRequest方法中将
    NSMutableDictionary *sslProperties = [NSMutableDictionary dictionaryWithCapacity:1];//替换为:
    NSDictionary *sslProperties = [[NSDictionary alloc] initWithObjectsAndKeys:
                                          [NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredCertificates,
                                          [NSNumber numberWithBool:YES], kCFStreamSSLAllowsAnyRoot,
                                          [NSNumber numberWithBool:NO],  kCFStreamSSLValidatesCertificateChain,
                                          kCFNull,kCFStreamSSLPeerName,
                                          nil];


    
    保存,再次运行,请求成功.
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    

    注意一点,不要将你自已的服务器域名,添加到例外中.否则有伪装第三方服务器的嫌疑,苹果有办法检测的,后果是不能承受. 目前我的做法是所有的接口请求都是https,所有的UIWebView加载的H5页面都是http,图片也是http.因为我在info.plist中添加Allow Aribitrary Loads为YES.所以我会在提交审核时会详细解释,力求能审核通过.  例外澄清一点,文章开头说的16年底,所有app事强制ATS.是一位参加wwdc2016的人发的推特. 苹果并不是说17之后就不能用http的接口了. 只是希望开发者不要无脑的关闭ATS.并且要详细的给个解释.链接:http://www.jianshu.com/p/6d5e0f8b722b 和苹果官方文档.

    ----------------------------------------------------------------------------更新----------------------------------------------------------------------------------------------------------------

    二,更新(重要)

    1,现在项目使用AFN3.x  所有接口支持HTTPS.对此更新. :
    证书;只要一个client.p12证书及对应的密码如:lovely_girl
    现在以一个AFN3.x请求为例,代码如下

    - (void)startRequest:(NSDictionary*)params path:(NSString*)path{
        
       urlString=[NSStringstringWithFormat:@"%@%@",Httpsource,path];
       _manager=[AFHTTPSessionManagermanager];
       _manager.responseSerializer=[AFHTTPResponseSerializerserializer];
       _manager.requestSerializer.timeoutInterval=12;
       _manager.securityPolicy.allowInvalidCertificates=YES;
        [_manager.securityPolicysetValidatesDomainName:NO];
        [_manager.requestSerializersetValue:@"gzip"forHTTPHeaderField:@"Accept-Encoding"];
        
       __weak typeof(self) weakSelf =self;
        [_managersetSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession *session,NSURLAuthenticationChallenge *challenge,NSURLCredential *__autoreleasing *_credential) {
           
           NSURLSessionAuthChallengeDisposition disposition =NSURLSessionAuthChallengePerformDefaultHandling;
           __autoreleasing NSURLCredential *credential =nil;
           if ([challenge.protectionSpace.authenticationMethodisEqualToString:NSURLAuthenticationMethodServerTrust]) {
               if ([weakSelf.manager.securityPolicyevaluateServerTrust:challenge.protectionSpace.serverTrustforDomain:challenge.protectionSpace.host]) {
                    credential = [NSURLCredentialcredentialForTrust:challenge.protectionSpace.serverTrust];
                   if (credential) {
                        disposition =NSURLSessionAuthChallengeUseCredential;
                    }else {
                        disposition =NSURLSessionAuthChallengePerformDefaultHandling;
                    }
                }else {
                    disposition =NSURLSessionAuthChallengeCancelAuthenticationChallenge;
                }
            }else {
               SecIdentityRef identity = NULL;
               SecTrustRef trust = NULL;
               NSString *p12 = [[NSBundlemainBundle] pathForResource:@"client"ofType:@"p12"];
               NSFileManager *fileManager =[NSFileManagerdefaultManager];
                
               if(![fileManager fileExistsAtPath:p12])
                {
                   NSLog(@"client.p12:not exist");
                }
               else
                {
                   NSData *PKCS12Data = [NSDatadataWithContentsOfFile:p12];
                   if ([HttpRequestextractIdentity:&identity andTrust:&trustfromPKCS12Data:PKCS12Data])
                    {
                       SecCertificateRef certificate = NULL;
                       SecIdentityCopyCertificate(identity, &certificate);
                       const void*certs[] = {certificate};
                       CFArrayRef certArray =CFArrayCreate(kCFAllocatorDefault, certs,1,NULL);
                        credential =[NSURLCredentialcredentialWithIdentity:identity certificates:(__bridge NSArray*)certArray persistence:NSURLCredentialPersistencePermanent];
                        disposition =NSURLSessionAuthChallengeUseCredential;
                    }
                }
            }
            *_credential=credential;
           return disposition;
        }];
    
        [_managerPOST:urlStringparameters:params progress:^(NSProgress *_Nonnull uploadProgress) {
        }success:^(NSURLSessionDataTask *_Nonnull task, id _Nullable responseObject) {
           NSDictionary *dic=[NSJSONSerializationJSONObjectWithData:responseObject options:NSJSONReadingAllowFragmentserror:nil];
           if ([_delegaterespondsToSelector:@selector(request:didReceiveData:)]) {
                [_delegaterequest:selfdidReceiveData:dic];
            }
        }failure:^(NSURLSessionDataTask *_Nullable task, NSError *_Nonnull error) {
           if ([_delegaterespondsToSelector:@selector(requestFailed:)]) {
                [_delegaterequestFailed:self];
            }
        }];
       if ([_delegaterespondsToSelector:@selector(requestStarted:)]) {
            [_delegaterequestStarted:self];
        }
    }
    
    +(BOOL)extractIdentity:(SecIdentityRef *)outIdentity andTrust:(SecTrustRef*)outTrust fromPKCS12Data:(NSData *)inPKCS12Data
    {
        OSStatus securityError =errSecSuccess;
        NSDictionary *optionsDictionary = [NSDictionarydictionaryWithObject:@"lovely_girl"forKey:(id)kSecImportExportPassphrase];
        
        CFArrayRef items =CFArrayCreate(NULL,0, 0,NULL);
        securityError = SecPKCS12Import((CFDataRef)inPKCS12Data,(CFDictionaryRef)optionsDictionary,&items);
        
        if (securityError ==0) {
            CFDictionaryRef myIdentityAndTrust =CFArrayGetValueAtIndex (items, 0);
            constvoid *tempIdentity = NULL;
            tempIdentity = CFDictionaryGetValue (myIdentityAndTrust,kSecImportItemIdentity);
            *outIdentity = (SecIdentityRef)tempIdentity;
            constvoid *tempTrust = NULL;
            tempTrust = CFDictionaryGetValue (myIdentityAndTrust,kSecImportItemTrust);
            *outTrust = (SecTrustRef)tempTrust;
        } else {
            NSLog(@"--------证书错误------- %d",(int)securityError);
            returnNO;
        }
        returnYES;
    }
    


    只要写这2个方法,就可以完整的以一个自签证书,进行https接口请求.

    2,对于上述使用NSURLsession双向认证记载HTTPS网页,导致页面不全.是因为图片,css,js等外部资源没有加载上.  我看到我安卓同时能够完整的加载一个自签的https的网页,是因为安卓里面有个回调方法,网页每加载一个资源,都在这个回调方法里验证证书.  所以上面的NSURLsession只验证了一次,是不行的.所以显示不全.下面是解决方法.  (该解决方法是在3年前的一个帖子上发现的).


    新建HTTPSURLProtocol继承NSURLProtocol.


    2-1,在appdelegate.m中注册,添加以下代码:


    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
            [NSURLProtocol registerClass:[HTTPSURLProtocol class]];
    }

    2-2,HTTPSURLProtocol.h

    #import <Foundation/Foundation.h>
    
    /**
    	该类用于处理https类型的ajax请求
     */
    @interface HTTPSURLProtocol : NSURLProtocol
    {
        NSMutableData* _data;
        NSURLConnection *theConncetion;
    }
    
    @end

    2-3,HTTPSURLProtocol.m

    #import "HTTPSURLProtocol.h"
    
    static NSString * const URLProtocolHandledKey = @"这里任意字符串";
    @implementation HTTPSURLProtocol
    
    + (BOOL)canInitWithRequest:(NSURLRequest *)theRequest
    {
        //FIXME: 后来的https类型的reqeust应该也要被处理
        NSString *scheme = [[theRequest URL] scheme];
        if ( ([scheme caseInsensitiveCompare:@"http"] == NSOrderedSame ||
              [scheme caseInsensitiveCompare:@"https"] == NSOrderedSame))
        {
            //看看是否已经处理过了,防止无限循环
            if ([NSURLProtocol propertyForKey:URLProtocolHandledKey inRequest:theRequest]) {
                return NO;
            }
    //此处做个判断,app.xxxx.com是我们项目的域名.任何不属于我们域名的不做处理,这样可以加载https://weibo.com等第三方https网页
            if (![[[theRequest URL] host] isEqualToString:@"app.xxxx.com"]) {
                return NO;
            }
            return YES;
        }
        return NO;
    }
    
    + (NSURLRequest*)canonicalRequestForRequest:(NSURLRequest*)request
    {
        NSMutableURLRequest *mutableReqeust = [request mutableCopy];
        mutableReqeust = [self redirectHostInRequset:mutableReqeust];
        return mutableReqeust;
    }
    
    - (void)startLoading
    {
        
        
        //theConncetion = [[NSURLConnection alloc] initWithRequest:self.request delegate:self];
        
        NSMutableURLRequest *mutableReqeust = [[self request] mutableCopy];
        //标示改request已经处理过了,防止无限循环
        [NSURLProtocol setProperty:@YES forKey:URLProtocolHandledKey inRequest:mutableReqeust];
        theConncetion = [NSURLConnection connectionWithRequest:mutableReqeust delegate:self];
        if (theConncetion) {
            _data = [NSMutableData data];
        }
    }
    
    - (void)stopLoading
    {
        // NOTE:如有清理工作,可以在此处添加
        [theConncetion cancel];
    }
    
    + (BOOL)requestIsCacheEquivalent:(NSURLRequest*)requestA toRequest:(NSURLRequest*)requestB
    {
        return [super requestIsCacheEquivalent:requestA toRequest:requestB];
    }
    
    #pragma mark - NSURLConnectionDelegate
    
    - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
    {
        //响应服务器证书认证请求和客户端证书认证请求
        return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust] ||
        [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate];
    }
    
    - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
    {
        NSURLCredential* credential;
        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
        {
            //服务器证书认证
            credential= [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
        }
        else if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate])
        {
            //客户端证书认证
            //TODO:设置客户端证书认证
           
            NSString *thePath = [[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"];
            NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath];
            CFDataRef inPKCS12Data = (CFDataRef)CFBridgingRetain(PKCS12Data);
            SecIdentityRef identity;
            // 读取p12证书中的内容
            OSStatus result = [self extractP12Data:inPKCS12Data toIdentity:&identity];
            
    //        if(result != errSecSuccess){
    //            completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
    //            return;
    //        }
            
            SecCertificateRef certificate = NULL;
            SecIdentityCopyCertificate(identity, &certificate);
            
            const void *certs[] = {certificate};
            CFArrayRef certArray = CFArrayCreate(kCFAllocatorDefault, certs, 1, NULL);
            
            credential = [NSURLCredential credentialWithIdentity:identity certificates:(NSArray*)CFBridgingRelease(certArray) persistence:NSURLCredentialPersistencePermanent];
        }
    
        if (credential != nil)
        {
            [challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
        }
        else
        {
            [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
        }
    }
    
    - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
    {
        [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
        
    }
    
    - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
    {
        [_data appendData:data];
    }
    
    - (void)connectionDidFinishLoading:(NSURLConnection *)connection
    {
        [[self client] URLProtocol:self didLoadData:_data];
        [[self client] URLProtocolDidFinishLoading:self];
        
    }
    
    -(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
    {
        [[self client] URLProtocol:self didFailWithError:error];
        
    }
    - (OSStatus)extractP12Data:(CFDataRef)inP12Data toIdentity:(SecIdentityRef *)identity {
        OSStatus securityErr = errSecSuccess;
        
        CFStringRef pwd = CFSTR("此处为证书密码");
        const void *keys[] = {kSecImportExportPassphrase};
        const void *values[] = {pwd};
        
        CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
        CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
        securityErr = SecPKCS12Import(inP12Data, options, &items);
        
        if (securityErr == errSecSuccess) {
            CFDictionaryRef ident = CFArrayGetValueAtIndex(items, 0);
            const void *tmpIdent = NULL;
            tmpIdent = CFDictionaryGetValue(ident, kSecImportItemIdentity);
            *identity = (SecIdentityRef)tmpIdent;
        }
        
        if (options) {
            CFRelease(options);
        }
        
        return securityErr;
    }
    +(NSMutableURLRequest*)redirectHostInRequset:(NSMutableURLRequest*)request
    {
        if ([request.URL host].length == 0) {
            return request;
        }
        
        NSString *originUrlString = [request.URL absoluteString];
        NSLog(@"-----链接导向---------->:%@",originUrlString);
        
        NSString *originHostString = [request.URL host];
        NSRange hostRange = [originUrlString rangeOfString:originHostString];
        if (hostRange.location == NSNotFound) {
            return request;
        }
    //此处可以将网页中链接的域名替换为自己的域名,防止劫持.
    //    NSString *ip = @"app.xxxx.com";
    //     // 替换域名
    //    NSString *urlString;
    //    if ([originUrlString isEqualToString:@"apis.map.qq.com"]) {
    //        urlString=originUrlString;
    //    }else if ([originUrlString isEqualToString:@"weibo.com"]){
    //        urlString=originUrlString;
    //    
    //    }else{
    //        urlString = [originUrlString stringByReplacingCharactersInRange:hostRange withString:ip];
    //    }
        
    //    NSURL *url = [NSURL URLWithString:originUrlString];
    //    request.URL = url;
        
        return request;
    }
    @end
    

    OK,完成,解决了uiwebview加载不了图片,css,js等外部资源,同时可以访问微博等第三方https页面.并且对每个https的请求做处理,提升加载速度在5秒以内(安卓的超过10秒).

    三,结束语:

    如果对文章有什么不懂的,欢迎评论,也欢迎顶一个.
    参考:http://bewithme.iteye.com/blog/1999031
    http://www.cocoachina.com/ios/20160928/17663.html
    http://www.cocoachina.com/bbs/read.php?tid=1689632
    http://www.cocoachina.com/ios/20151021/13722.html
    http://www.jianshu.com/p/7c89b8c5482a

    展开全文
  • 苹果从IOS9.0以后就要求使用https,今年发布说的是在2017年1月1日后,所有上架的APP必须使用HTTPS(貌似目前推迟了)。不论怎么说,使用https时迟早的事情,之前通过在info.plist文件中设置 NSAppTransportSecurity...

    一、背景:

    苹果从IOS9.0以后就要求使用https,今年发布说的是在2017年1月1日后,所有上架的APP必须使用HTTPS(貌似目前推迟了)。不论怎么说,使用https时迟早的事情,之前通过在info.plist文件中设置 NSAppTransportSecurity为NSAllowsArbitraryLoads的方式不起作用了。这篇文章主要就是介绍在IOS中如何将http改造成https。

    二、关于https:

    可以简单的理解https就是http的安全版本,https实质是http+SSL;就在http请求的基础上加上一层安全措施,这样抓包工具抓到的数据显示的就是乱码,而不是明文方式了。更详细的关于https和SSL可以查看下面文章:

    SSL/TLS协议运行机制的概述

    图解SSL/TLS协议


    三、IOS中的AFNetworking使用https:

    1.需要服务端提供认证证书.crt文件,然后自己导出成.cer文件

    2.将导出的cer证书加入到项目中,注意勾选相应的target不然可能获取证书路径为nil

    3.通过cer证书生成证书校验的安全策略

    4.在AFNetworking的网络请求中设置安全策略:[_operationManager setSecurityPolicy:[CertificatehttpsTools customSecurityPolicy]];

    5.通过抓包工具Charles检验请求和返回的内容是否加密


    证书校验安全策略的工具类:

    #import <Foundation/Foundation.h>
    #import "AFNetworking.h"
    
    @interface HttpsCertificateTools : NSObject
    
    
    /**
     自定义证书安全策略
    
     @return 返回自定义的证书安全策略
     */
    + (AFSecurityPolicy*)customSecurityPolicy;
    
    @end


    #import "HttpsCertificateTools.h"
    
    @implementation HttpsCertificateTools
    
    + (AFSecurityPolicy*)customSecurityPolicy{
        // /先导入证书
        NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"服务端证书名称" ofType:@"cer"];//证书的路径
        NSData *certData = [NSData dataWithContentsOfFile:cerPath];
        //    NSString *aString = [[NSString alloc] initWithData:certData encoding:NSUTF8StringEncoding];
        
        // AFSSLPinningModeCertificate 使用证书验证模式
        //    AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
        AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
        // allowInvalidCertificates 是否允许无效证书(也就是自建的证书),默认为NO
        // 如果是需要验证自建证书,需要设置为YES
        securityPolicy.allowInvalidCertificates = YES;
        
        //validatesDomainName 是否需要验证域名,默认为YES;
        //假如证书的域名与你请求的域名不一致,需把该项设置为NO;如设成NO的话,即服务器使用其他可信任机构颁发的证书,也可以建立连接,这个非常危险,建议打开。
        //置为NO,主要用于这种情况:客户端请求的是子域名,而证书上的是另外一个域名。因为SSL证书上的域名是独立的,假如证书上注册的域名是www.google.com,那么mail.google.com是无法验证通过的;当然,有钱可以注册通配符的域名*.google.com,但这个还是比较贵的。
        //如置为NO,建议自己添加对应域名的校验逻辑。
        securityPolicy.validatesDomainName = NO;
        
        //    securityPolicy.validatesCertificateChain = NO;
        
        //    requestOperationManager.securityPolicy = securityPolicy;
        
        securityPolicy.pinnedCertificates = @[certData];
        
        return securityPolicy;
    }
    
    @end

    AFNetworking在网络请求的时候添加安全策略:

    _operationManager = [AFHTTPRequestOperationManager manager];
    //https协议
    [_operationManager setSecurityPolicy:[HttpsCertificateTools customSecurityPolicy]];

    下面方法是双向认证需要实现的,如果你只做单向认证的话不用管:

    在AFURLConnectionOperation.m中修改willSendRequestForAuthenticationChallenge方法:

    #pragma mark - NSURLConnectionDelegate
    
    - (void)connection:(NSURLConnection *)connection
    willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
    {
    //    if (self.authenticationChallenge) {
    //        self.authenticationChallenge(connection, challenge);
    //        return;
    //    }
    //
    //    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
    //        if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
    //            NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
    //            [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
    //        } else {
    //            [[challenge sender] cancelAuthenticationChallenge:challenge];
    //        }
    //    } else {
    //        if ([challenge previousFailureCount] == 0) {
    //            if (self.credential) {
    //                [[challenge sender] useCredential:self.credential forAuthenticationChallenge:challenge];
    //            } else {
    //                [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
    //            }
    //        } else {
    //            [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
    //        }
    //    }
        
        NSString *thePath = [[NSBundle mainBundle] pathForResource:@"服务返回的客户端证书" ofType:@"p12"];
        
        NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath];
        
        CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data;
        SecIdentityRef identity = NULL;
        
        // extract the ideneity from the certificate
        
        [self extractIdentity :inPKCS12Data :&identity];
        
        
        
        SecCertificateRef certificate = NULL;
        
        SecIdentityCopyCertificate (identity, &certificate);
        
        
        
        const void *certs[] = {certificate};
        
                                CFArrayRef certArray = CFArrayCreate(kCFAllocatorDefault, certs, 1, NULL);
        
        // create a credential from the certificate and ideneity, then reply to the challenge with the credential
        
        //NSLog(@"identity=========%@",identity);
        
        NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identity certificates:nil persistence:NSURLCredentialPersistencePermanent];
        
        
        
                   credential = [NSURLCredential credentialWithIdentity:identity certificates:(__bridge NSArray*)certArray persistence:NSURLCredentialPersistencePermanent];
        
        
        
        [challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
    }

    将方法:

    - (OSStatus)extractIdentity:(CFDataRef)inP12Data :(SecIdentityRef*)identity {
        OSStatus securityError = errSecSuccess;
        CFStringRef password = CFSTR("showbaby123456");
        
        const void *keys[] = { kSecImportExportPassphrase };
        
        const void *values[] = { password };
        
        CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
        
        CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
        
        securityError = SecPKCS12Import(inP12Data, options, &items);
        
        if (securityError == 0){
            
            CFDictionaryRef ident = CFArrayGetValueAtIndex(items,0);
            
            const void *tempIdentity = NULL;
            
            tempIdentity = CFDictionaryGetValue(ident, kSecImportItemIdentity);
            
            *identity = (SecIdentityRef)tempIdentity;
        }
        
        if (options) {
            
            CFRelease(options);
            
        }
        return securityError;
    }

    添加到类中。

    下载抓包工具Charles,设置手机的代理和请求地址,请求数据然后,查看请求和返回的数据是否加密。






    展开全文
  • 作为前端开发,应该或多或少的都会熟悉https,特别是邻居家IOS,早就必须使用HTTPS了,Android也在9.0的时候增加了这一要求,当我们的targetSdkVersion指到9.0以上时,必须使用HTTPSHTTPS 简单来讲,HTTPS可以...
  • Https iOS需要做的事

    2016-08-20 09:43:13
    Https iOS需要做的事情 HTTPS就是将HTTP协议数据包放到SSL/TSL层加密后,在TCP/IP层组成IP数据报去传输,以此保证传输数据的安全;而对于接收端,在SSL/TSL将接收的数据包解密之后,将数据传给HTTP协议层,就是普通...
  • iOS配置HTTPS

    2016-12-15 22:48:05
    iOS配置HTTPS的详解
  • 由于苹果的开发者大会召开后,对HTTPS进行了严格的规定,以前还可以通过在info.plist中添加Key值继续使用HTTP,不过在2017年1月1日就不可以了,所以我这也紧锣密鼓的开始张罗HTTPS了,其实一整也没什么,下面是个人...
  • HTTP and HTTPS Requests》有详细的说明,而文档《HTTPS Server Trust Evaluation》则详细讲解了HTTPS验证相关知识,这里就不多说了。本文主要讲解我们最常用的NSURLConnection支持HTTPS的实现(NSUR
  • iOS 上对证书链的验证HTTPS 概要 HTTPS 是运行在 TLS/SSL 之上的 HTTP,与普通的 HTTP 相比,在数据传输的安全性上有很大的提升。 要了解它安全性的巧妙之处,需要先简单地了解对称加密和非对称加密的区别...
  • iOS9中https网络适配

    2015-09-21 12:25:39
    1. iOS9网络适配_ATS:改用更安全的HTTPS [摘要]为了强制增强数据访问安全, iOS9 默认会把 所有的http请求 所有从NSURLConnection 、 CFURL、 NSURLSession发出的 HTTP 请求,都改为 HTTPS 请求:iOS9.x-SDK...
  • ios 9 出来以后,苹果推荐使用https协议,来提高数据传输之间的安全性苹果在2016年6月底发布说要在2017年强制开启ATS,但在24号说为了给用户时间去更新,延期了,但是无论如何早晚搜要使用HTTPS。下面将介绍,如何在工程...
  • 如果最近发现iOS APP打开h5非常慢,达到5秒以上,而安卓又正常,如果刚好你又是使用了Let's Encrypt 的免费SSL服务,恭喜你,你可能是一位运营商干扰的受害者。 原因: 近期由于众所周知的原因,国内无法直接访问 ...
  • iOS9推出的时候,苹果希望大家使用https协议,来提高数据传输之间的安全性。下面我就从最简单的代码介绍,如何在工程中设置,来支持https的请求。一、证书准备篇1.证书转换在服务器人员,给你发送的crt证书后,进到...
  • 在移动App开发领域,开发工程师对于安全方面的考虑普遍比较欠缺,而由于iOS平台的封闭性,遭遇到的安全问题相比于Android来说要少得多,这就导致了许多iOS开发人员对于安全性方面没有太多的深入,但对于一个合格的...
  • 为什么要使用HTTPS代替HTTP HTTPS和HTTP的区别 https协议需要到CA申请证书,一般免费证书很少,需要交费。 http是超文本传输协议,信息是明文传输,https则是具有安全性的SSL加密传输协议。 http和https使用的...
  • iOS9推出的时候,苹果希望大家使用https协议,来提高数据传输之间的安全性。下面我就从最简单的代码介绍,如何在工程中设置,来支持https的请求。 一、证书准备篇 1.证书转换 在服务器人员,给你发送的crt证书后...
  • iOS开发 - 用AFNetworking实现https单向验证,双向验证 http://blog.csdn.net/codingfire/article/details/53419521本文信息来源于http://www.hangge.com/blog/cache/detail_1052.html,不错的一个网址。实现了OC的...
1 2 3 4 5 ... 20
收藏数 6,192
精华内容 2,476
关键字:

https验证域名 ios