證書類型
- 從權(quán)威認證機構(gòu)購買的證書
- 優(yōu)點:服務(wù)端如果使用的是這類證書的話,那么客戶端一般不需要做什么,直接使用HTTPS請求就行了,蘋果內(nèi)置了那些受信任的根證書
- 缺點:需要花錢
- 自制證書
- 優(yōu)點:無需花錢
- 缺點:這類證書是不受信任的,因此需要我們在代碼中將自制證書設(shè)置為可信任證書
自己實現(xiàn)自制證書驗證
- 這里以
NSURLSession請求為例演示下如何驗證自制證書
- 實現(xiàn)
NSURLSessionDelegate的代理方法-URLSession:didReceiveChallenge:completionHandler:
- 只要訪問的是
HTTPS請求就會調(diào)用此代理方法
- 此代理方法的作用:對自制證書的驗證
- 代理方法的各個參數(shù)介紹
- challenge:挑戰(zhàn)、質(zhì)問 (包含了受保護的空間)
- completionHandler:處理自制證書,該block的兩個參數(shù)的含義:
disposition表示證書處理方式,credential表示需要處理的證書
- disposition類型介紹
NSURLSessionAuthChallengeUseCredential = 0, 使用該證書,安裝該證書
NSURLSessionAuthChallengePerformDefaultHandling = 1, 默認方式,證書被忽略
NSURLSessionAuthChallengeCancelAuthenticationChallenge = 2, 取消請求,證書被忽略
NSURLSessionAuthChallengeRejectProtectionSpace = 3, 拒絕本次請求
- 證書認證結(jié)果
SecTrustResultType類型介紹
-------以下認證結(jié)果表示可信證書---------
// 證書通過驗證,是由于用戶有操作設(shè)置了證書被信任,如:在彈出的是否信任的alert框中選擇always trust
// 一般是自制證書,非權(quán)威機構(gòu)頒發(fā)的,如:Charles的代理證書
kSecTrustResultProceed = 1,
// 證書通過驗證,用戶沒有設(shè)置這些證書是否被信任
// 一般驗證權(quán)威機構(gòu)頒發(fā)的CA證書會返回這個結(jié)果
kSecTrustResultUnspecified = 4,
------------------------------------
-------以下認證結(jié)果表示不可信證書-------
// 無效證書
kSecTrustResultInvalid = 0,
// 待確認證書
kSecTrustResultConfirm = 2,
// 被拒證書
kSecTrustResultDeny = 3,
// 不可信證書
kSecTrustResultRecoverableTrustFailure = 5,
// 嚴重不可信證書
kSecTrustResultFatalTrustFailure = 6,
// 其他錯誤
kSecTrustResultOtherError = 7
// 認證方式
challenge.protectionSpace.authenticationMethod
// 返回的服務(wù)端證書
// 如果authenticationMethod不是NSURLAuthenticationMethodServerTrust,那么serverTrust為nil
challenge.protectionSpace.serverTrust
- 自制證書認證步驟
- 首先判斷認證方式,是否為
NSURLAuthenticationMethodServerTrust
- 獲取服務(wù)端返回的自制證書
challenge.protectionSpace.serverTrust
- 從app的資源文件中獲取
內(nèi)置的本地證書(可以是多個),并設(shè)置為服務(wù)端證書的根證書,設(shè)置后會屏蔽掉系統(tǒng)的證書列表,當然可以通過SecTrustSetAnchorCertificatesOnly方法(第二個參數(shù)設(shè)置為NO)來使系統(tǒng)的證書列表繼續(xù)起作用
- 在設(shè)置的證書列表中驗證服務(wù)端自制證書是否可信
- 完成驗證后通過回調(diào)告知系統(tǒng)如何處理自制證書
- 代碼實現(xiàn):這里使用的
do-while編程思想不錯,避免了太多的if-else
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
// 證書的處理方式
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
// 被處理的證書
NSURLCredential *credential = nil;
do
{
// 1、校驗認證方式是否為NSURLAuthenticationMethodServerTrust
if (![challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
break; /* failed */
// 2、獲取服務(wù)端返回的證書
SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
// 3、從app的資源文件中獲取內(nèi)置的本地證書(可以是多個,這里只演示只有一個內(nèi)置證書)
// custom是你證書的名稱,記得替換
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"custom" ofType:@"cer"];
NSData *caCert = [NSData dataWithContentsOfFile:cerPath];
if(nil != caCert) {
// 創(chuàng)建證書
SecCertificateRef caRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)caCert);
// NSCAssert(caRef != nil, @"caRef is nil");
if(nil == caRef)
break; /* failed */
// 添加自制證書,這里表明了可以添加多張自制證書
NSArray *caArray = @[(__bridge id)(caRef)];
// NSCAssert(caArray != nil, @"caArray is nil");
if(nil == caArray)
break; /* failed */
// 將內(nèi)置的本地證書列表設(shè)置為服務(wù)端證書的根證書
// 設(shè)置可信任證書列表,設(shè)置后就只會在設(shè)置的證書列表中進行驗證,屏蔽掉系統(tǒng)的證書列表
OSStatus status = SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)caArray);
// 要使系統(tǒng)的證書列表繼續(xù)起作用可以調(diào)用此方法,第二個參數(shù)設(shè)置成NO即可
SecTrustSetAnchorCertificatesOnly(serverTrust, NO);
// NSCAssert(errSecSuccess == status, @"SecTrustSetAnchorCertificates failed");
if(errSecSuccess != status)// errSecSuccess表示沒有錯誤
break; /* failed */
}
// 4、使用本地導入的證書列表驗證服務(wù)器的證書是否可信
SecTrustResultType result = -1;
OSStatus status = SecTrustEvaluate(serverTrust, &result);
if(errSecSuccess != status)
break; /* failed */
// kSecTrustResultUnspecified and kSecTrustResultProceed are success
BOOL allowConnect = (result == kSecTrustResultUnspecified)// 證書通過驗證,是由于用戶沒有設(shè)置這些證書是否被信任
|| (result == kSecTrustResultProceed);// 證書通過驗證,用戶有操作設(shè)置了證書被信任
if (!allowConnect)
break; /* failed */
// 5、設(shè)置證書的處理方式
credential = [NSURLCredential credentialForTrust:serverTrust];
disposition = NSURLSessionAuthChallengeUseCredential;
} while(0);
// 6、告知系統(tǒng)如何處理自制證書
if(completionHandler) completionHandler(disposition, credential);
}
通過AFN實現(xiàn)自制證書驗證
-
AFSecurityPolicy的三種驗證模式
-
AFSSLPinningModeNone只驗證證書是否在信任列表中
-
AFSSLPinningModePublicKey先驗證證書是否在信任列表中,然后僅驗證服務(wù)端證書與客戶端證書的公鑰是否一致
-
AFSSLPinningModeCertificate先驗證證書是否在信任列表中,然后對比服務(wù)端證書和客戶端證書是否一致(不僅限于公鑰是否一致)
- 通過給
AFHTTPSessionManager設(shè)置回調(diào)setSessionDidReceiveAuthenticationChallengeBlock來驗證自制證書,然后將內(nèi)置的本地證書加入到可信任的證書列表中,即可通過證書的校驗
- 代碼實現(xiàn)
// 記得設(shè)置個AFHTTPSessionManager屬性
@property (nonatomic, strong) AFHTTPSessionManager *sessionManager;
- (void)validateCerByAFN {
_sessionManager = [AFHTTPSessionManager manager];
// 創(chuàng)建安全策略
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
// 是否允許使用自制證書
securityPolicy.allowInvalidCertificates = YES;
// 是否需要驗證域名,默認YES
securityPolicy.validatesDomainName = YES;
_sessionManager.securityPolicy = securityPolicy;
// 響應(yīng)數(shù)據(jù)序列化格式
_sessionManager.responseSerializer = [AFJSONResponseSerializer serializer];
// 設(shè)置超時
_sessionManager.requestSerializer.timeoutInterval = 30.f;
// 緩存策略
_sessionManager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringCacheData;
// 接受的返回數(shù)據(jù)類型
_sessionManager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/html", @"text/json", @"text/plain", @"text/javascript", @"text/xml", @"image/*", nil];
// 打開狀態(tài)欄的等待菊花
// [AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
__weak typeof(self) weakSelf = self;
[_sessionManager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing *_credential)
{
// 1、校驗認證方式是否為NSURLAuthenticationMethodServerTrust
if (![challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
return NSURLSessionAuthChallengePerformDefaultHandling;
}
// 2、設(shè)置自制證書,可以導入多張自制證書(也就是個循環(huán),這里就不演示了)
// custom是你證書的名稱,記得替換
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"custom" ofType:@"cer"];
if(cerPath != nil) {
NSData *caCert = [NSData dataWithContentsOfFile:cerPath];
// 將內(nèi)置的本地證書列表賦值給AFN,便于后期基于此證書列表進行驗證
NSSet *cerArray = [[NSSet alloc] initWithObjects:caCert, nil];
weakSelf.sessionManager.securityPolicy.pinnedCertificates = cerArray;
// 創(chuàng)建證書
SecCertificateRef caRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)caCert);
// 從這個數(shù)組可以看出是可以有多張本地自制證書
NSArray *caArray = @[(__bridge id)(caRef)];
// 獲取服務(wù)端返回的證書
SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
// 將內(nèi)置的本地證書列表設(shè)置為服務(wù)端證書的根證書
// 設(shè)置可信任證書列表,設(shè)置后就只會在設(shè)置的證書列表中進行驗證,屏蔽掉系統(tǒng)的證書列表
OSStatus status = SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)caArray);
if(status == errSecSuccess) {// 表名設(shè)置成功
// 要使系統(tǒng)的證書列表繼續(xù)起作用可以調(diào)用此方法,第二個參數(shù)設(shè)置成NO即可
SecTrustSetAnchorCertificatesOnly(serverTrust, NO);
}
}
// 證書驗證,獲取證書的處理方式
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
if ([weakSelf.sessionManager.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
disposition = NSURLSessionAuthChallengeUseCredential;
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
return disposition;
}];
}
參考文章
最后編輯于 :
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。