HTTPS连接过程及证书自定义认证

前言

原理

HTTPS 简述

  • HTTPS是运行在 TLS/SSL 之上的 HTTP,与普通的 HTTP 相比,在数据传输的安全性上有很大的提升。要了解它安全性的巧妙之处,需要先简单地了解对称加密和非对称加密的区别!

  • 对称加密只有一个密钥,加密和解密都用这个密钥;

  • 非对称加密有公钥和私钥,私钥加密后的内容只有公钥才能解密,公钥加密的内容只有私钥才能解密。

  • 为了提高安全性,我们常用的做法是使用对称加密的手段加密数据。可是只使用对称加密的话,双方通信的开始总会以明文的方式传输密钥。那么从一开始这个密钥就泄露了,谈不上什么安全。所以 TLS/SSL 在握手的阶段,结合非对称加密的手段,保证只有通信双方才知道对称加密的密钥。大概的流程如下:

数字证书的内容

X.509 应该是比较流行的 SSL 数字证书标准,包含(但不限于)以下的字段:

字段 值说明
对象名称(Subject Name) 用于识别该数字证书的信息
共有名称(Common Name) 对于客户证书,通常是相应的域名
证书颁发者(Issuer Name) 发布并签署该证书的实体的信息
签名算法(Signature Algorithm) 签名所使用的算法
序列号(Serial Number) 数字证书机构(Certificate Authority, CA)给证书的唯一整数,一个数字证书一个序列号
生效期(Not Valid Before) (`・ω・´)
失效期(Not Valid After) (╯°口°)╯(┴—┴
公钥(Public Key) 可公开的密钥
签名(Signature) 通过签名算法计算证书内容后得到的数据,用于验证证书是否被篡改

数字证书的生成及验证

  • 数字证书的生成是分层级的,下一级的证书需要其上一级证书的私钥签名。 所以后者是前者的证书颁发者,也就是说上一级证书的 Subject Name 是其下一级证书的 Issuer Name。

  • 在得到证书申请者的一些必要信息(对象名称,公钥私钥)之后,证书颁发者通过 SHA-256 哈希得到证书内容的摘要,再用自己的私钥给这份摘要加密,得到数字签名。综合已有的信息,生成分别包含公钥和私钥的两个证书。

使用

使用原理简述

  • 先从服务器获取证书,然后分级验证证书,字符串内容是否相等。最后通过afnetworking的AFHTTPSessionManager的block方法校验后,返回是否通过验证。
- (void)setSessionDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block;
  • 最终返回状态
数值 中文说明
NSURLSessionAuthChallengeUseCredential = 0, 使用证书
NSURLSessionAuthChallengePerformDefaultHandling = 1, 忽略证书(默认的处理方式)
NSURLSessionAuthChallengeCancelAuthenticationChallenge = 2, 忽略书证, 并取消这次请求
NSURLSessionAuthChallengeRejectProtectionSpace = 3, 拒绝当前这一次, 下一次再询问
  • 注意,对于afnetworking库,要使用自认证,必须要设置如下选项。
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
securityPolicy.allowInvalidCertificates = NO;//是否允许使用自签名证书
securityPolicy.validatesDomainName = YES;//是否需要验证域名,默认YES
[manager setSecurityPolicy:securityPolicy];

代码

+ (AFHTTPSessionManager *)addHeaderForSignWithRequestURL:(NSString *)requestURL mathod:(NSString *)method needToDecrypt:(BOOL)needToDecrypt {
    static AFHTTPSessionManager *manager = nil;
    static dispatch_once_t oneToken;
    dispatch_once(&oneToken, ^{
        
        manager = [[AFHTTPSessionManager manager] initWithBaseURL:[NSURL URLWithString:OnLineURL]];
        
        [manager.responseSerializer setAcceptableContentTypes:[NSSet setWithObjects:
                                                               TTVNetWork_AcceptableContentType_ApplicationJson,TTVNetWork_AcceptableContentType_TextHtml,
                                                               TTVNetWork_AcceptableContentType_TextJvascript,TTVNetWork_AcceptableContentType_TextJson,
                                                               TTVNetWork_AcceptableContentType_TextPlain,TTVNetWork_AcceptableContentType_urlencoded,nil]];
        
        manager.requestSerializer = [AFJSONRequestSerializer serializer];
        manager.responseSerializer = [AFJSONResponseSerializer serializer];
        // 设置超时时间
        [manager.requestSerializer willChangeValueForKey:TTVNewWork_timeoutInterValKey];
        manager.requestSerializer.timeoutInterval = TTVNetWork_TimeoutInterval;
        [manager.requestSerializer didChangeValueForKey:TTVNewWork_timeoutInterValKey];
        
        // DeviceID 0.
        [manager.requestSerializer setValue:NetworkOptionShareIntance.prefixDeviceID forHTTPHeaderField:TTVNetWork_DeviceIDKey];
        
//        // SSL Pinning
//        [manager setSecurityPolicy:[self customSecurityPolicy]];
        
        AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
        securityPolicy.allowInvalidCertificates = NO;//是否允许使用自签名证书
        securityPolicy.validatesDomainName = YES;//是否需要验证域名,默认YES
        [manager setSecurityPolicy:securityPolicy];

        // 自定义验证证书
        [manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing *_credential)
        {
            BOOL isServerTrust = NO;//所有证书验证通过
            
            BOOL isCertRootTrust = NO;//根证书验证通过
            BOOL isCertSecondTrust = NO;//二级证书验证通过
            BOOL isCertClientTrust = NO;//最后一级,本地证书验证通过
            
            //取得服务器返回的三级证书
            SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];
            
            //遍历服务器返回的证书
            CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
            for (CFIndex i = certificateCount - 1; i >= 0; i--) {
                SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
                NSString *subjectSummary = (__bridge_transfer NSString *)SecCertificateCopySubjectSummary(certificate);
                
                //证书NSData转为NSString
                CFDataRef certData = SecCertificateCopyData(certificate);
                NSString *certificateBase64String = [(__bridge_transfer NSData *)certData base64EncodedStringWithOptions:0];
                
                //比较本地保存的一级和二级证书NSString
                if (i == certificateCount - 1) {//一级根证书
                    if ([certificateBase64String isEqualToString:certStringForRoot]) {
                        isCertRootTrust = YES;
                    }
                } else if (i == certificateCount - 2) {//第二级证书
                    if ([certificateBase64String isEqualToString:certStringForSecondLevel]) {
                        isCertSecondTrust = YES;
                    }
                } else if (i == 0) {//第三级:要部分验证的本地证书
  
//                    //服务器证书的颁发机构序列号
//                    CFDataRef issuerSequenceData = SecCertificateCopyNormalizedIssuerSequence(certificate);//iOS 10.3 后,才能使用此方法
//                    NSString *issuerSequenceBase64String = [(__bridge_transfer NSData *)issuerSequenceData base64EncodedStringWithOptions:0];
                    
                    //获取本地证书
                    NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"itouchtv_app" ofType:@"cer"];
                    NSData *certData = [NSData dataWithContentsOfFile:cerPath];
                    SecCertificateRef certificateLocal = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certData);
                    
                    //域名
                    NSString *subjectSummaryLocal = (__bridge_transfer NSString *)SecCertificateCopySubjectSummary(certificateLocal);
                    
//                    //颁发机构序列号
//                    CFDataRef issuerSequenceDataLocal = SecCertificateCopyNormalizedIssuerSequence(certificateLocal);
//                    NSString *issuerSequenceBase64StringLocal = [(__bridge_transfer NSData *)issuerSequenceDataLocal base64EncodedStringWithOptions:0];
                    
                    //验证
//                    if ([subjectSummary isEqualToString:subjectSummaryLocal]
//                        && [issuerSequenceBase64String isEqualToString:issuerSequenceBase64StringLocal])
//                    {
//                        isCertClientTrust = YES;
//                    }
                    
                    if ([subjectSummary isEqualToString:subjectSummaryLocal])
                    {
                        isCertClientTrust = YES;
                    }
                }
            }
            
            DLog(@"一级根证书是否验证通过:%@,第二级证书是否验证通过:%@,第三级本地证书是否验证通过:%@", @(isCertRootTrust), @(isCertSecondTrust), @(isCertClientTrust));
            
            //判断所有证书是否验证通过
            if (isCertRootTrust && isCertSecondTrust && isCertClientTrust) {
                isServerTrust = YES;
            }

            NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
            __autoreleasing NSURLCredential *credential = nil;
            if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
//                if ([manager.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
                if (isServerTrust) {//使用自己的判断逻辑
                    credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
                    if (credential) {
                        disposition = NSURLSessionAuthChallengeUseCredential;
                    } else {
                        disposition = NSURLSessionAuthChallengePerformDefaultHandling;
                    }
                } else {
                    disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
                }
            } else {
                disposition = NSURLSessionAuthChallengePerformDefaultHandling;
            }
            return disposition;
        }];
        
        [TTVPlayerMoniter sharedMointerManager];

    });
    
    // USERID & Authorization
    if ([TTVUserInfo sharedTTVUserInfo].isLogin) {//1.
        [manager.requestSerializer setValue:[TTVUserInfo sharedTTVUserInfo].currentUser.userId forHTTPHeaderField:TTVNetWork_UserIDKey];
        NSString *authorization = [NSString stringWithFormat:@"Bearer %@",[TTVUserInfo sharedTTVUserInfo].currentUser.jwt];//2.
        [manager.requestSerializer setValue:authorization forHTTPHeaderField:TTVNetWork_AuthorizationKey];
    }else {
        [manager.requestSerializer setValue:NetworkOptionShareIntance.prefixDeviceID forHTTPHeaderField:TTVNetWork_DeviceIDKey];
        
        [manager.requestSerializer setValue:nil forHTTPHeaderField:TTVNetWork_UserIDKey];
        
        [manager.requestSerializer setValue:nil forHTTPHeaderField:TTVNetWork_AuthorizationKey];
    }
    
    return manager;
}

原文:http://raychow.linkfun.top/2017/12/15/archives/1_ios/HTTPS_Certificate_Certification/index/

猜你喜欢

转载自my.oschina.net/u/3729367/blog/1590724