HTTPS鏈接過程及證書自定義認證

前言

原理

HTTPS 簡述

  • HTTPS是運行在 TLS/SSL 之上的 HTTP,與普通的 HTTP 相比,在數據傳輸的安全性上有很大的提高。要了解它安全性的巧妙之處,須要先簡單地瞭解對稱加密和非對稱加密的區別!服務器

  • 對稱加密只有一個密鑰,加密和解密都用這個密鑰;session

  • 非對稱加密有公鑰和私鑰,私鑰加密後的內容只有公鑰才能解密,公鑰加密的內容只有私鑰才能解密。app

  • 爲了提升安全性,咱們經常使用的作法是使用對稱加密的手段加密數據。但是隻使用對稱加密的話,雙方通訊的開始總會以明文的方式傳輸密鑰。那麼從一開始這個密鑰就泄露了,談不上什麼安全。因此 TLS/SSL 在握手的階段,結合非對稱加密的手段,保證只有通訊雙方纔知道對稱加密的密鑰。大概的流程以下:加密

數字證書的內容

X.509 應該是比較流行的 SSL 數字證書標準,包含(但不限於)如下的字段:lua

字段 值說明
對象名稱(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。url

  • 在獲得證書申請者的一些必要信息(對象名稱,公鑰私鑰)以後,證書頒發者經過 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/

相關文章
相關標籤/搜索