為了防止中間人攻擊并蝗,需要在客戶端中內(nèi)置SSL證書戏仓,來驗證服務(wù)端證書的有效性凛驮。
- AFNetworking 定義了AFSecurityPolicy類實現(xiàn)對服務(wù)端的配置管理,其中
AFSSLPinningMode
中定義的AFSSLPinningModePublicKey
郭宝、AFSSLPinningModeCertificate
均需要客戶端中內(nèi)置服務(wù)器的SSL證書荠割。
typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
AFSSLPinningModeNone,//不需要任何驗證
AFSSLPinningModePublicKey,//驗證公鑰
AFSSLPinningModeCertificate,//驗證證書
};
另外瞻惋,可通過如下命令獲取服務(wù)端的證書小染。
openssl s_client -connect ***.***.com.cn:443 </dev/null 2>/dev/null | openssl x509 -outform DER > https.cer
- 類中主要定義了如下方法進行驗證,注釋中已經(jīng)很明確箱舞。
// 客戶端通過本地證書或公鑰對服務(wù)端進行驗證
- (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) {//如果需要驗證domain
//設(shè)置驗證策略 那么就使用SecPolicyCreateSSL函數(shù)創(chuàng)建驗證策略
[policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
} else {
//只驗證x509標準的證書
[policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
}
// 設(shè)置驗證serverTrust 的策略遍坟, 告訴系統(tǒng)在驗證serverTrust的時候考慮域名的正確性
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
if (self.SSLPinningMode == AFSSLPinningModeNone) {
//如果為AFSSLPinningModeNone,則要么不驗證晴股,要么只使用系統(tǒng)對證書進行驗證
return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
} else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
//如果不允許無效證書政鼠,且系統(tǒng)沒有驗證通過serverTrust
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)];
}
//給serverTrust設(shè)置錨點證書,即如果以后再次去驗證serverTrust队魏,會從錨點證書去找是否匹配公般。
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
//用系統(tǒng)CA證書 驗證serverTrust的有效性
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)
//獲取服務(wù)端證書鏈
NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
//如果本地證書 包含 在服務(wù)端證書鏈中
if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
return YES;
}
}
return NO;
}
case AFSSLPinningModePublicKey: {
NSUInteger trustedPublicKeyCount = 0;
//獲取服務(wù)端公鑰鏈
NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);
for (id trustChainPublicKey in publicKeys) {
for (id pinnedPublicKey in self.pinnedPublicKeys) {
if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
//驗證公鑰匹配的個數(shù)
trustedPublicKeyCount += 1;
}
}
}
return trustedPublicKeyCount > 0;
}
}
return NO;
}