iOS https
目前iOS大都是用的AFN來進行數(shù)據(jù)請求
正常請求
配置
AFHTTPSessionManager * mgr = [AFHTTPSessionManager manager];
mgr.responseSerializer = [AFHTTPResponseSerializer serializer];
mgr.requestSerializer = [AFHTTPRequestSerializer serializer];
NSString * URL = @"https://www.baidu.com";
//中間穿插一層 SSL配置
//typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
// AFSSLPinningModeNone, 什么也不操作
// AFSSLPinningModePublicKey, 只驗證公鑰
// AFSSLPinningModeCertificate, 驗證證書
//};
//此方法會默認(rèn)加載本地的.cer文件
+ (NSSet *)certificatesInBundle:(NSBundle *)bundle {
NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."];
NSMutableSet *certificates = [NSMutableSet setWithCapacity:[paths count]];
for (NSString *path in paths) {
NSData *certificateData = [NSData dataWithContentsOfFile:path];
[certificates addObject:certificateData];
}
return [NSSet setWithSet:certificates];
}
//并存到pinnedPublicKeys字段 公鑰集合
- (void)setPinnedCertificates:(NSSet *)pinnedCertificates {
_pinnedCertificates = pinnedCertificates;
if (self.pinnedCertificates) {
NSMutableSet *mutablePinnedPublicKeys = [NSMutableSet setWithCapacity:[self.pinnedCertificates count]];
for (NSData *certificate in self.pinnedCertificates) {
id publicKey = AFPublicKeyForCertificate(certificate);
if (!publicKey) {
continue;
}
[mutablePinnedPublicKeys addObject:publicKey];
}
self.pinnedPublicKeys = [NSSet setWithSet:mutablePinnedPublicKeys];
} else {
self.pinnedPublicKeys = nil;
}
}
AFSecurityPolicy * p =
[AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
//驗證證書中的域名
p.validatesDomainName = YES;
//是否允許過期的證書使用
p.allowInvalidCertificates = YES;
mgr.securityPolicy = p;
[mgr GET:URL parameters:@{} progress:^(NSProgress * _Nonnull uploadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {}];
邏輯處理
主要使用是在 AFURLSessionManager.m 文件
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler;
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler;
{
//證書處理方式
//1 NSURLSessionAuthChallengeUseCredential //使用指定證書 可能為空
//2 NSURLSessionAuthChallengePerformDefaultHandling //如果沒有實現(xiàn)代理方法默認(rèn)處理, credential參數(shù)忽略
//3 NSURLSessionAuthChallengeCancelAuthenticationChallenge //所有的請求都會被取消, credential參數(shù)忽略
//4 NSURLSessionAuthChallengeRejectProtectionSpace //拒絕當(dāng)前這一次, 下一次再詢問
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
if (self.sessionDidReceiveAuthenticationChallenge) {
disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
} else {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
// 如果驗證方法為NSURLAuthenticationMethodServerTrust,說明服務(wù)器端需要客戶端返回一個根據(jù)認(rèn)證挑戰(zhàn)的保護空間提供的信任(即challenge.protectionSpace.serverTrust)產(chǎn)生的挑憑據(jù),用+credentialForTrust方法創(chuàng)建
// 如果驗證方法不是NSURLAuthenticationMethodServerTrust,則challenge.protectionSpace.serverTrust為nil
// 一般情況下,服務(wù)器的驗證方式都是ServerTrust,如果不是,就用其它方法創(chuàng)建憑據(jù)(參考那個官方文檔鏈接)
//參數(shù)的主要邏輯判斷
if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
if (credential) {
//使用證書
disposition = NSURLSessionAuthChallengeUseCredential;
} else {
//默認(rèn)處理
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
//不符合驗證條件的 但不符合的直接拒絕請求
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
//默認(rèn)處理
//驗證方法不是NSURLAuthenticationMethodServerTrust
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}
if (completionHandler) {
completionHandler(disposition, credential);
}
}
evaluateServerTrust邏輯
//no 默認(rèn)處理
//yes 根據(jù)空間域創(chuàng)建證書 有證書就驗證,, 沒有也是默認(rèn)處理
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
forDomain:(NSString *)domain
{
//有域名 允許無效證書 校驗域名 (不校驗或者本地沒證書)
if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) {
// https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html
// According to the docs, you should only trust your provided certs for evaluation.
// Pinned certificates are added to the trust. Without pinned certificates,
// there is nothing to evaluate against.
//
// From Apple Docs:
// "Do not implicitly trust self-signed certificates as anchors (kSecTrustOptionImplicitAnchors).
// Instead, add your own (self-signed) CA certificate to the list of trusted anchors."
NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning.");
return NO;//無法訪問
}
NSMutableArray *policies = [NSMutableArray array];
//校驗域名
if (self.validatesDomainName) {
[policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
} else {
//不校驗域名
[policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
}
//設(shè)置SSL檢查策略 插入域名校驗或常規(guī)
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
//如果模式設(shè)置為None
if (self.SSLPinningMode == AFSSLPinningModeNone) {
//當(dāng) 允許無效的證書(自建證書) 直接不校驗通過
//證書直接通過
return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
} else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
//校驗沒通過 并且只能校驗證書有效 不通過
return NO;
}
switch (self.SSLPinningMode) {
case AFSSLPinningModeNone:
default:
return NO;//不通過
case AFSSLPinningModeCertificate: {
NSMutableArray *pinnedCertificates = [NSMutableArray array];
for (NSData *certificateData in self.pinnedCertificates) {
[pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
}
////將讀取的證書設(shè)置為serverTrust的根證書
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
if (!AFServerTrustIsValid(serverTrust)) {
return NO;//先驗證不通過
}
// obtain the chain after being validated, which *should* contain the pinned certificate in the last position (if it's the Root CA)
NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
//證書base64校驗
for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
return YES;
}
}
return NO;//不通過
}
case AFSSLPinningModePublicKey: {
NSUInteger trustedPublicKeyCount = 0;
NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);
for (id trustChainPublicKey in publicKeys) {
for (id pinnedPublicKey in self.pinnedPublicKeys) {
if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
trustedPublicKeyCount += 1;
}
}
}
return trustedPublicKeyCount > 0;
}
}
return NO;
}
AFPublicKeyForCertificate 本地證書提取公鑰
static id AFPublicKeyForCertificate(NSData *certificate) {
id allowedPublicKey = nil;
SecCertificateRef allowedCertificate;
SecPolicyRef policy = nil;
SecTrustRef allowedTrust = nil;
SecTrustResultType result;
allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate);
__Require_Quiet(allowedCertificate != NULL, _out);
policy = SecPolicyCreateBasicX509();
__Require_noErr_Quiet(SecTrustCreateWithCertificates(allowedCertificate, policy, &allowedTrust), _out);
__Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out);
allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust);
_out:
if (allowedTrust) {
CFRelease(allowedTrust);
}
if (policy) {
CFRelease(policy);
}
if (allowedCertificate) {
CFRelease(allowedCertificate);
}
return allowedPublicKey;
}
AFServerTrustIsValid 策略
static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) {
BOOL isValid = NO;
SecTrustResultType result;
//類似goto語句
__Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out);
//指示評估成功,證書隱式可信,但未顯式指定用戶意圖
//Indicates the evaluation succeeded and the certificate is implicitly trusted, but user intent was not explicitly specified. This value may be returned by the SecTrustEvaluate function or stored as part of the user trust settings.
//表示您可以繼續(xù)
//Indicates you may proceed. This value may be returned by the SecTrustEvaluate function or stored as part of the user trust settings.
isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);
_out:
return isValid;
}
AFCertificateTrustChainForServerTrust 策略轉(zhuǎn)化
static NSArray * AFCertificateTrustChainForServerTrust(SecTrustRef serverTrust) {
CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];
for (CFIndex i = 0; i < certificateCount; i++) {
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
[trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)];
}
return [NSArray arrayWithArray:trustChain];
}
AFPublicKeyTrustChainForServerTrust 公鑰
static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
SecPolicyRef policy = SecPolicyCreateBasicX509();
CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];
for (CFIndex i = 0; i < certificateCount; i++) {
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
SecCertificateRef someCertificates[] = {certificate};
CFArrayRef certificates = CFArrayCreate(NULL, (const void **)someCertificates, 1, NULL);
SecTrustRef trust;
__Require_noErr_Quiet(SecTrustCreateWithCertificates(certificates, policy, &trust), _out);
SecTrustResultType result;
__Require_noErr_Quiet(SecTrustEvaluate(trust, &result), _out);
[trustChain addObject:(__bridge_transfer id)SecTrustCopyPublicKey(trust)];
_out:
if (trust) {
CFRelease(trust);
}
if (certificates) {
CFRelease(certificates);
}
continue;
}
CFRelease(policy);
return [NSArray arrayWithArray:trustChain];
}