当前位置: 首页 > 工具软件 > iOS HTTPS > 使用案例 >

IOS之https请求认证及双向认证

马煌
2023-12-01

一、背景:

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

二、关于https:

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

三、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;
        
                                      更多免费的Python学习资料
    
                                           进QQ群  688244617
   
                                     群里还有小伙伴跟你一起交流学习


        //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;
                                        
                                         更多免费的Python学习资料
    
                                          
                                           进QQ群  688244617
   
                                    
                                     群里还有小伙伴跟你一起交流学习
        
        // 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://blog.csdn.net/guobing19871024/article/details/53841373

 类似资料: