本文主要是爲了afnetworking下配置https證書驗證。html
最近公司另外一個APP要作SSL自定義驗證,參考了另外一個APP已由其餘同事寫好的代碼,順便總結一下相關知識。ios
參考1:http://blog.csdn.net/dr19901106/article/details/50294393算法
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 哈希獲得證書內容的摘要,再用本身的私鑰給這份摘要加密,獲得數字簽名。綜合已有的信息,生成分別包含公鑰和私鑰的兩個證書。
- (void)setSessionDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block;
數值 | 中文說明 |
---|---|
NSURLSessionAuthChallengeUseCredential = 0, | 使用證書 |
NSURLSessionAuthChallengePerformDefaultHandling = 1, | 忽略證書(默認的處理方式) |
NSURLSessionAuthChallengeCancelAuthenticationChallenge = 2, | 忽略書證, 並取消此次請求 |
NSURLSessionAuthChallengeRejectProtectionSpace = 3, | 拒絕當前這一次, 下一次再詢問 |
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/