# AFNetworking框架下的SSL服務器證書的自定義驗證服務器
## 如何使用本地證書進行SSL驗證session
#### 開啓SSL驗證app
須要設置 `AFHTTPSessionManager` 的 `setSecurityPolicy` ,使用 `AFSSLPinningModeCertificate` 證書驗證模式框架
```網站
[AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate]lua
```code
而後進行 `AFSecurityPolicy` 的一系列設置,以下orm
```server
+ (AFSecurityPolicy *)customSecurityPolicy {接口
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate]; // 設置證書驗證模式
[securityPolicy setAllowInvalidCertificates:NO]; // 是否容許無效證書(也就是自建的證書),默認爲NO.若是是須要驗證自建證書,須要設置爲YES
[securityPolicy setValidatesDomainName:YES]; // 驗證域名
// 設置證書
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"itouchtv_app" ofType:@"cer"];
NSData *certData = [NSData dataWithContentsOfFile:cerPath];
[securityPolicy setPinnedCertificates:[NSSet setWithObject:certData]];
return securityPolicy;
}
```
#### 關閉SSL驗證
只須要使用 `AFSSLPinningModeNone` 模式,便可關閉SSL驗證
```
[AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone]
```
## 自定義實現SSL證書的驗證邏輯
#### 使用 NSURLSession 走 HTTPS 通道訪問網站或接口
NSURLSession的 ` -URLSession:didReceiveChallenge:completionHandler: ` 回調中會收到一個類型爲 `NSURLAuthenticationChallenge` 的質詢 challenge,在此回調中,進行證書信息的認證,以決定是否繼續鏈接服務器,或者斷開鏈接。
經過 challenge.protectionSpace.authenticationMethod 獲取保護空間要求認證的方式,若是值是 `NSURLAuthenticationMethodServerTrust` ,就能夠走數字證書的自定義驗證邏輯了。
#### AFNetworking 下的具體實現方式
跟 `NSURLSession` 是相同的原理,質詢的回調是 `AFHTTPSessionManager` 的 `setSessionDidReceiveAuthenticationChallengeBlock` 。
#### SSL證書的層級和自定義驗證邏輯
SSL證書,通常有三層,根證書和二級證書是申請證書時的權威可信的頒發機構的證書,在換證書時,是保持不變的,因此能夠使用字符串常量來直接驗證是否相等,便可完成根證書和二級證書的驗證。
而最後一層,纔是每一個證書申請者本身的獨特的信息,也是須要自定義驗證的部分。
##### 具體代碼
關鍵在於 `isServerTrust` 變量,表示自定義驗證經過。
```
// 設置驗證模式
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) {//第三級:要部分驗證的本地證書
//獲取本地證書
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"app" ofType:@"cer"];
NSData *certData = [NSData dataWithContentsOfFile:cerPath];
SecCertificateRef certificateLocal = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certData);
//域名
NSString *subjectSummaryLocal = (__bridge_transfer NSString *)SecCertificateCopySubjectSummary(certificateLocal);
//驗證
if ([subjectSummary isEqualToString:subjectSummaryLocal])
{
isCertClientTrust = YES;
}
}
}
NSLog(@"一級根證書是否驗證經過:%@,第二級證書是否驗證經過:%@,第三級本地證書是否驗證經過:%@", @(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];
});
```