美文网首页iOS技术
iOS HTTPS双向认证

iOS HTTPS双向认证

作者: 程序渣渣猿 | 来源:发表于2019-02-16 17:43 被阅读20次

    一般情况下对于HTTPS,如果公司申请认证之后,客户端是不需要做什么操作。但是如果是自建证书,没有通过CA认证的话,就需要我们代码里自己做认证。就如很早之前的12306,打开就是不受信任的证书,那个就是自建证书。

    如果服务端已经配置好了各种证书,那么步骤2不需要看,直接略过。验证之前需步骤5进行设置。

    1. HTTPS双向认证过程

    HTTPS双向认证过程

    2. 创建CA证书

    这里的CA证书是不经过认证的,自己创建的一个证书

    2.1 生成证书

    2.1.1 创建私钥

    openssl genrsa -out root/root-key.pem 1024 
    

    2.1.2 创建证书请求

    openssl req -new -out root/root-req.csr -key root/root-key.pem
    

    2.1.3 自签署证书

    openssl x509 -req -in root/root-req.csr -out root/root-cert.pem -signkey root/root-key.pem -days 3650 
    

    2.1.4 将证书导出成浏览器支持的.p12格式

    openssl pkcs12 -export -clcerts -in root/root-cert.pem -inkey root/root-key.pem -out root/root.p12
    

    2.2 生成sever证书

    2.2.1 创建私钥

    openssl genrsa -out server/server-key.pem 1024 
    

    2.2.2 创建证书请求

    openssl req -new -out server/server-req.csr -key server/server-key.pem 
    

    2.2.3 自签署证书

    openssl x509 -req -in server/server-req.csr -out server/server-cert.pem -signkey server/server-key.pem -CA root/root-cert.pem -CAkey root/root-key.pem -CAcreateserial -days 3650 
    

    2.2.4 将证书导出成浏览器支持的.p12格式

    openssl pkcs12 -export -clcerts -in server/server-cert.pem -inkey server/server-key.pem -out server/server.p12
    

    2.3 生成client证书

    2.3.1 创建私钥

    openssl genrsa -out client/client-key.pem 1024 
    

    2.3.2 创建证书请求

    openssl req -new -out client/client-req.csr -key client/client-key.pem 
    

    2.3.3 自签署证书

    openssl x509 -req -in client/client-req.csr -out client/client-cert.pem -signkey client/client-key.pem -CA root/root-cert.pem -CAkey root/root-key.pem -CAcreateserial -days 3650 
    

    2.3.4 将证书导出成浏览器支持的.p12格式

    openssl pkcs12 -export -clcerts -in client/client-cert.pem -inkey client/client-key.pem -out client/client.p12
    

    3. iOS客户端设置双向认证

    用的是AFNetworking进行双向验证

    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface BJAFNetWorking : NSObject
    +(AFHTTPSessionManager *) createCredentialsClient;
    @end
    
    NS_ASSUME_NONNULL_END
    
    
    #import "BJAFNetWorking.h"
    
    @implementation BJAFNetWorking
    + (AFHTTPSessionManager *)manager
    {
        static AFHTTPSessionManager *shareInstance = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            
            NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
            shareInstance = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:BJBASEURL_KEY] sessionConfiguration:configuration];
            //设置请求参数的类型:JSON
            shareInstance.requestSerializer = [AFJSONRequestSerializer serializer];
            //设置服务器返回结果的类型:JSON (AFJSONResponseSerializer,AFHTTPResponseSerializer)
            shareInstance.responseSerializer = [AFJSONResponseSerializer serializer];
            //设置请求的超时时间
            shareInstance.requestSerializer.timeoutInterval = 20.0f;
            //设置ContentType
            shareInstance.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/html", @"text/json", @"text/plain", @"text/javascript", @"text/xml", @"image/jpeg",@"image/png", nil];
            
            // https配置
            NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"Your sever cer(服务端公钥)" ofType:@"cer"];
            NSData *certData = [NSData dataWithContentsOfFile:cerPath];
            AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate withPinnedCertificates:[[NSSet alloc] initWithObjects:certData, nil]];
            
            NSSet *dataSet = [[NSSet alloc] initWithObjects:certData, nil]; //这里可以添加多个server的证书
            
            // setPinnedCertificates 设置证书文件(可能不止一个证书)
            [securityPolicy setPinnedCertificates:dataSet];
            // allowInvalidCertificates 是否允许无效证书
            [securityPolicy setAllowInvalidCertificates:NO];
            // validatesDomainName 是否需要验证域名
            [securityPolicy setValidatesDomainName:YES];
            
            shareInstance.securityPolicy = securityPolicy;
        });
        return shareInstance;
    }
    
    /*
     *
     **
     * 创建服务器信任客户端的认证条件
     **
     */
    +(AFHTTPSessionManager *) createCredentialsClient
    {
        __block AFHTTPSessionManager * manager = [BJAFNetWorking manager];
        [manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession*session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing*_credential) {
            NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
            __autoreleasing NSURLCredential *credential =nil;
            if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
                if([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:@"Your client p12"ofType:@"p12"];
                NSFileManager *fileManager =[NSFileManager defaultManager];
                
                if(![fileManager fileExistsAtPath:p12])
                {
                    NSLog(@"client.p12:not exist");
                }
                else
                {
                    NSData *PKCS12Data = [NSData dataWithContentsOfFile:p12];
                    if ([BJAFNetWorking 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;
        }];
        
        return manager;
    }
    
    /**
     **加载PKCS12证书,pfx或p12
     **
     **/
    +(BOOL)extractIdentity:(SecIdentityRef*)outIdentity andTrust:(SecTrustRef *)outTrust fromPKCS12Data:(NSData *)inPKCS12Data {
        OSStatus securityError = errSecSuccess;
        //client certificate password
        NSDictionary*optionsDictionary = [NSDictionary dictionaryWithObject:@"Your client p12 password(密码)"
                                                                     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;
    }
    @end
    
    

    4. 使用

    在需要初始化AFHTTPSessionManager的地方使用如下代码进行初始化

    [BJAFNetWorking createCredentialsClient]
    

    5. 补充说明

    5.1 对于cer证书

    cer证书

    cer证书,常用名称为域名形式

    5.2 info.plist文件配置

     <key>NSAppTransportSecurity</key>
        <dict>
            <key>NSExceptionDomains</key>
            <dict>
                <key>xxxx.com</key>
                <dict>
                    <key>NSExceptionAllowsInsecureHTTPLoads</key>
                    <true/>
                    <key>NSExceptionMinimumTLSVersion</key>
                    <string>TLSv1.0</string>
                    <key>NSExceptionRequiresForwardSecrecy</key>
                    <false/>
                    <key>NSIncludesSubdomains</key>
                    <true/>
                </dict>
            </dict>
        </dict>
    

    相关文章

      网友评论

        本文标题:iOS HTTPS双向认证

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