1. 程式人生 > >iOS基於AFNetworking使用自簽名證書實現HTTPS請求

iOS基於AFNetworking使用自簽名證書實現HTTPS請求

二:HTTPS加密方式

對稱加密只有一個金鑰,加密和解密都用這個金鑰;

非對稱加密有公鑰和私鑰,私鑰加密後的內容只有公鑰才能解密,公鑰加密的內容只有私鑰才能解密。公鑰加密的另一用途是身份驗證:用私鑰加密的資訊,可以用公鑰對其解密,接收者由此可知這條資訊確實來自於擁有私鑰的某人。私鑰加密的過程即數字簽名

為了提高安全性,我們常用的做法是使用對稱加密的手段加密資料。可是隻使用對稱加密的話,雙方通訊的開始總會以明文的方式傳輸金鑰。那麼從一開始這個金鑰就洩露了,談不上什麼安全。所以 TLS/SSL 在握手的階段,結合非對稱加密的手段,保證只有通訊雙方才知道對稱加密的金鑰。大概的流程如下:下圖同時也是單向認證的原理圖。

所以,HTTPS 實現傳輸安全的關鍵是:在 TLS/SSL 握手階段保證僅有通訊雙方得到 Session Key!

單向認證:認證伺服器-伺服器給客戶端公鑰,私鑰只有伺服器有。只有伺服器可以解密。

三、單向認證

Https在建立Socket連線之前,需要進行握手,具體過程如下:


客戶端向服務端傳送SSL協議版本號、加密演算法種類、隨機數等資訊。

服務端給客戶端返回SSL協議版本號、加密演算法種類、隨機數等資訊,同時也返回伺服器端的證書,

客戶端使用服務端返回的資訊驗證伺服器的合法性,包括:

◦          證書是否過期

◦          髮型伺服器證書的CA

是否可靠

◦          返回的公鑰是否能正確解開返回證書中的數字簽名-也就是使用返回的公鑰解開證書中用私鑰-可知這條資訊確實來自於擁有私鑰的某人。私鑰加密的過程即數字簽名

◦          伺服器證書上的域名是否和伺服器的實際域名相匹配

驗證通過後,將繼續進行通訊,否則,終止通訊

客戶端向服務端傳送自己所能支援的對稱加密方案,供伺服器端進行選擇

伺服器端在客戶端提供的加密方案中選擇加密程度最高的加密方式。

伺服器將選擇好的加密方案通過明文方式返回給客戶端

客戶端接收到服務端返回的加密方式後,使用該加密方式生成產生隨機碼,用作通訊過程中對稱加密的金鑰,使用服務端返回的公鑰進行加密,將加密後的隨機碼傳送至伺服器

伺服器收到客戶端返回的加密資訊後,使用自己的私鑰進行解密,獲取對稱加密金鑰。

10 在接下來的會話中,伺服器和客戶端將會使用該密碼進行對稱加密,保證通訊過程中資訊的安全。

四、雙向認證

雙向認證和單向認證原理基本差不多,只是除了客戶端需要認證服務端以外,增加了服務端對客戶端的認證,具體過程如下:


客戶端向服務端傳送SSL協議版本號、加密演算法種類、隨機數等資訊。

服務端給客戶端返回SSL協議版本號、加密演算法種類、隨機數等資訊,同時也返回伺服器端的證書,即公鑰證書

客戶端使用服務端返回的資訊驗證伺服器的合法性,包括:

◦          證書是否過期

◦          髮型伺服器證書的CA是否可靠

◦          返回的公鑰是否能正確解開返回證書中的數字簽名

◦          伺服器證書上的域名是否和伺服器的實際域名相匹配

驗證通過後,將繼續進行通訊,否則,終止通訊

服務端要求客戶端傳送客戶端的證書,客戶端會將自己的證書傳送至服務端

驗證客戶端的證書,通過驗證後,會獲得客戶端的公鑰

客戶端向服務端傳送自己所能支援的對稱加密方案,供伺服器端進行選擇

伺服器端在客戶端提供的加密方案中選擇加密程度最高的加密方式

將加密方案通過使用之前獲取到的公鑰進行加密,返回給客戶端

10 客戶端收到服務端返回的加密方案密文後,使用自己的私鑰進行解密,獲取具體加密方式,而後,產生該加密方式的隨機碼,用作加密過程中的金鑰,使用之前從服務端證書中獲取到的公鑰進行加密後,傳送給服務端

服務端收到客戶端傳送的訊息後,使用自己的私鑰進行解密,獲取對稱加密的金鑰,在接下來的會話中,伺服器和客戶端將會使用該密碼進行對稱加密,保證通訊過程中資訊的安全。

若干重要驗證說明:

1.        客戶端驗證伺服器的Certificate訊息。主要驗證內容為:

(1).    伺服器證書使用日期是否有效。

(2).    發行伺服器證書的CA是否可靠。

(3).    發行者的公鑰能否解開伺服器證書上的“發行者數字簽名”。

(4).    伺服器證書上的名稱(如域名)是否和伺服器實際名稱匹配等(PHP中可以選擇是否驗證該選項)。

(Tenfy: 這裡可以看出,客戶端驗證服務的時候,只需要驗證對應伺服器證書的有效性即可,無需驗證對應伺服器是否擁有跟該證書一致的私鑰)

2.        伺服器驗證客戶端的CertificateVerify訊息。主要驗證內容為:

(1).    用客戶端公鑰能否解開客戶端私鑰加密的訊息。

(Tenfy:伺服器驗證客戶端時候,還需要驗證是否擁有跟證書對應的私鑰)

3.        伺服器驗證客戶端的ClientCertificate訊息。主要驗證內容為:

(1).    客戶的證書使用日期是否有效。

(2).    為客戶提供證書的CA 是否可靠。

(3).    發行CA 的公鑰能否正確解開客戶證書的發行CA的數字簽名。

(4).    檢查客戶的證書是否在證書廢止列表(CRL)中。

五.CA證書

CA證書是經過認證的數字證書是一個用於網際網路通訊中認證身份的工具,由權威機構——CA機構(Certificate Authority)發行。CA 是一些非常權威的專門用於認證一個網站合法性的組織。服務商可以向他們申請一個證書,使得他們建立安全連線時可以帶上 CA 的簽名。而 CA 的安全性由作業系統或瀏覽器來認證。你的 Windows、Mac、Linux、Chrome、Safari 等會在安裝時帶上一個他們認為安全的 CA 證書列表。如果和你建立安全連線的人帶著這些人的簽名,那麼認為這個安全連線是安全的,沒有遭到中間人攻擊。

所以通過 對稱加密 + 非對稱加密 + CA認證 這三個技術混合在一起,才使得 HTTP 的後面加上了一個 S —— Security。實際上 HTTPS 的協議比我這裡描述的更復雜一些,我這裡說的主要是基本的實現原理。因為其中任何一環稍有閃失,就會使得整個加密都將變得不安全。這也是為什麼 HTTPS 的加密協議從SSL 1.0 升級到 SSL 3.0 再被 TLS 1.0 現在被 TLS 1.2 取代,其背後都是一環環細節上的修改,以防任何地方的閃失。

六.在AFNetworking設定HTTPS單向認證。

NSString *myUrl=@"https://192.168.18.83:9443/testweb/test?action=3";

AFHTTPSessionManager*manager = [AFHTTPSessionManager manager];

    //    設定超時時間

    manager.requestSerializer.timeoutInterval = 30.f;

    [manager.requestSerializer didChangeValueForKey:@"timeoutInterval"];

    [manager.requestSerializer setValue:@"Content-Type" forHTTPHeaderField:@"application/json;charset=utf-8"];

//設定安全政策

//AFSecurityPolicy:=將固定的SSL證書新增到您的應用程式可以幫助防止中間人攻擊和其他漏洞。或財務資訊處理敏感的客戶資料的應用程式被強烈鼓勵所有通訊路由在一個HTTPSSSL連線固定配置和啟用

    //校驗證書的方式

    AFSecurityPolicy*securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];

    /**AFSSLPinningModeCertificate=除了公鑰外,其他內容也要一致才能通過驗證---必須有伺服器的證書才可以通過認證

     **AFSSLPinningModePublicKey=只驗證公鑰部分,只要公鑰部分一致就驗證通過---必須有伺服器的證書才可以通過認證;也就是即使伺服器證書有所變動,只要公鑰不變,就能通過認證。

     **AFSSLPinningModeNone=不做任何驗證,只要伺服器返回了證書就通過-,也就是你不必將認證證書跟你的APP一起打包;

     */

    //tomcat1(1)-伺服器的證書

    NSString * cerPath = [[NSBundle mainBundle] pathForResource:@"server" ofType:@"cer"];

    NSData *certData = [NSData dataWithContentsOfFile:cerPath];

    NSSet  *dataSet = [NSSet setWithArray:@[certData]];

    //是否信任伺服器無效或過期的SSL證書。預設為”---這個引數是校驗證書過期+是否是CA認證有效的()-20170427

    //經測試:設定為“NO”自簽名的證書認證失敗,百度等第三方的證書認證成功!-系統在驗證到根證書時,發現它是自簽名、不可信的。

    [securityPolicy setAllowInvalidCertificates:YES];

    [securityPolicy setPinnedCertificates:dataSet];

    //是否驗證域名證書的CN欄位。預設為”----------------------測試這裡改為NO

    [securityPolicy setValidatesDomainName:YES];

[manager setSecurityPolicy: securityPolicy];

[manager POST: myUrl parameters:nil progress:nil success:success failure:failure];

七.單向認證總結:

1.iOS中證書是否有效的標準:信任鏈中如果只含有 有效證書並且以“可信錨點(trusted auchor)”結尾,那麼這個證書就被稱為是有效的。

2.可信任錨點:指系統隱式信任的證書,也就是系統中CA根證書。蘋果文件:不要信任隱士的自簽名證書,如果要信任需要自己的簽名證書新增到證書信任列表中。不過我們可以在驗證證書鏈時,可以設定自定義的證書作為可信任的錨點。也可通過認證。[securityPolicy setAllowInvalidCertificates:NO];設定為NO時:檢查證書是否有效,自簽名證書會是無效的。百度等CA認證的是有效的證書。

3.如果要驗證自簽名證書的域名,驗證證書不能使用“AFSSLPinningModeNone”:Inorder to validate a domain name for self signed certificates, you MUST usepinning.

4.在AFNetworking嵌入開發,CA證書:系統會自動查詢需要匹配的伺服器證書。也就是[securityPolicy setPinnedCertificates:dataSet];這個方法可以不用新增

八.設定自定義的證書作為可信任的錨點:

[manager setSessionDidBecomeInvalidBlock:^(NSURLSession * _Nonnull session, NSError * _Nonnull error) {

        NSLog(@"setSessionDidBecomeInvalidBlock");

    }];

    __weak typeof(manager)weakManager = manager;

    // 該方法的作用就是處理伺服器返回的證書, 需要在該方法中告訴系統是否需要安裝伺服器返回的證書

    // NSURLAuthenticationChallenge : 授權質問- challenge,也就是質詢

    //+ 受保護空間

    //+ 伺服器返回的證書型別

    //驗證證書

    [manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession*session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing*_credential) {

        //第一個引數:challenge 代表如何處理證書

        //第二個引數: NSURLCredential 代表需要處理哪個證書

        //// 如果使用預設的處置方式,那麼 credential(憑證) 就會被忽略

        NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;

        __autoreleasing NSURLCredential *credential =nil;

        NSLog(@"authenticationMethod=%@",challenge.protectionSpace.authenticationMethod);

        //判斷伺服器返回的證書是否是伺服器信任的

        // 1.從伺服器返回的受保護空間中拿到證書的型別

        // 2.判斷伺服器返回的證書是否是伺服器信任的

        //如果這個值是 NSURLAuthenticationMethodServerTrust 的話,我們就可以插手 TLS 握手中驗證數字證書有效性這一步。

        if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {

            //呼叫自定義的驗證過程

            if ([selfmyCustomValidation:challenge]) {

                    credential =[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];

                    if (credential){

                        disposition= NSURLSessionAuthChallengeUseCredential;

                    }

            } else {

                    //無效的話,取消

                disposition =NSURLSessionAuthChallengeCancelAuthenticationChallenge;

            }

        *_credential = credential;

        return disposition;

    }];

九:在AFNetworking設定HTTPS雙向認證。

AFHTTPSessionManager*manager = [AFHTTPSessionManager manager];

    //    設定超時時間

    [manager.requestSerializer willChangeValueForKey:@"timeoutInterval"];

    manager.requestSerializer.timeoutInterval = 30.f;

    [manager.requestSerializer didChangeValueForKey:@"timeoutInterval"];

    [manager.requestSerializer setValue:@"Content-Type" forHTTPHeaderField:@"application/json; charset=utf-8"];

    //設定安全政策

    [manager setSecurityPolicy:[self customSecurityPolicy]];

    //校驗證書=驗證證書;

    [self checkCredential:manager];

NSData *data = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:nil];

    NSString *jsonString = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];

    [manager POST:urlString parameters:jsonString progress:uploadProgress success:success failure:failure];

//校驗伺服器-證書;

- (AFSecurityPolicy*)customSecurityPolicy {

    AFSecurityPolicy*securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];//AFSSLPinningModeCertificate-校驗證書的方式;

    /**AFSSLPinningModeCertificate=除了公鑰外,其他內容也要一致才能通過驗證---必須有伺服器的證書才可以通過認證

     **AFSSLPinningModePublicKey=只驗證公鑰部分,只要公鑰部分一致就驗證通過---必須有伺服器的證書才可以通過認證;也就是即使伺服器證書有所變動,只要公鑰不變,就能通過認證。

     **AFSSLPinningModeNone=不做任何驗證,只要伺服器返回了證書就通過-,也就是你不必將認證證書跟你的APP一起打包;

     */

    //tomcat1(1)-伺服器的證書

    NSString * cerPath = [[NSBundle mainBundle] pathForResource:@"server" ofType:@"cer"];

    NSData *certData = [NSData dataWithContentsOfFile:cerPath];

    NSSet  *dataSet = [NSSet setWithArray:@[certData]];

    //是否信任伺服器無效或過期的SSL證書。預設為”---這個引數是校驗證書過期+是否是CA認證有效的()-20170427

    //經測試:設定為“NO”自簽名的證書認證失敗,百度等第三方的證書認證成功!-系統在驗證到根證書時,發現它是自簽名、不可信的。

    [securityPolicy setAllowInvalidCertificates:YES];

    [securityPolicy setPinnedCertificates:dataSet];

    //是否驗證域名證書的CN欄位。預設為

    [securityPolicy setValidatesDomainName:YES];

    return securityPolicy;

}

//校驗證書=驗證證書;

- (void)checkCredential:(AFURLSessionManager *)manager

{

    [manager setSessionDidBecomeInvalidBlock:^(NSURLSession * _Nonnull session, NSError * _Nonnull error) {

        NSLog(@"setSessionDidBecomeInvalidBlock");

    }];

    __weak typeof(manager)weakManager = manager;

    // 該方法的作用就是處理伺服器返回的證書, 需要在該方法中告訴系統是否需要安裝伺服器返回的證書

    // NSURLAuthenticationChallenge : 授權質問

    //+ 受保護空間

    //+ 伺服器返回的證書型別

    //驗證證書

    [manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession*session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing*_credential) {

        //第一個引數:challenge 代表如何處理證書

        //第二個引數: NSURLCredential 代表需要處理哪個證書

        NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;

        __autoreleasing NSURLCredential *credential =nil;

        NSLog(@"authenticationMethod=%@",challenge.protectionSpace.authenticationMethod);

        //判斷是核驗客戶端證書還是伺服器證書--------//判斷伺服器返回的證書是否是伺服器信任的

        // 1.從伺服器返回的受保護空間中拿到證書的型別

        // 2.判斷伺服器返回的證書是否是伺服器信任的

        if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {//--客戶端驗證方法伺服器信任

            // 基於客戶端的安全策略來決定是否信任該伺服器,不信任的話,也就沒必要響應挑戰

            if([weakManager.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {

                NSLog(@"是伺服器信任的證書");

                // 建立挑戰證書(注:挑戰方式為UseCredentialPerformDefaultHandling都需要新建挑戰證書)

                NSLog(@"serverTrust=%@",challenge.protectionSpace.serverTrust);

                //建立質詢證書-------//建立證書---------

                credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];

                // 確定挑戰的方式---//確認質詢方式

                if (credential) {

                    //證書挑戰設計policy,none,則跑到這裡

                    disposition = NSURLSessionAuthChallengeUseCredential;//使用指定的憑據,這可能是零

                } else {

                    disposition = NSURLSessionAuthChallengePerformDefaultHandling;//預設處理挑戰——如果沒有這個委託實施;憑證引數被忽略

                }

            } else {

                NSLog(@"不是伺服器信任的證書-沒有挑戰的必要了");

                disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;//整個請求將被取消,憑證引數被忽略

            }

        }

        else {

            // client authentication-客戶端身份驗證

            SecIdentityRef identity = NULL;//p12資料中提取identitytrustobjects(可信任物件),並評估其可信度。

            SecTrustRef trust = NULL;//評估證書。這裡的信任物件(trustobject),包括信任策略和其他用於判斷證書是否可信的資訊,都已經含在了PKCS資料中。要單獨評估一個證書是否可信

            NSString *p12 = [[NSBundle mainBundle] pathForResource:@"client"ofType:@"p12"];

            NSFileManager *fileManager =[NSFileManager defaultManager];

            if(![fileManager fileExistsAtPath:p12])

            {

                NSLog(@"client.p12:notexist");

            }

            else

            {

                NSData *PKCS12Data = [NSData dataWithContentsOfFile:p12];

                if ([self extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data])

                {

                    SecCertificateRefcertificate = NULL;

                    SecIdentityCopyCertificate(identity,&certificate);

                    const void*certs[] = {certificate};

                    CFArrayRef certArray =CFArrayCreate(kCFAllocatorDefault, certs,1,NULL);

                    credential =[NSURLCredential credentialWithIdentity:identity certificates:(__bridge  NSArray*)certArray persistence:NSURLCredentialPersistencePermanent];

                    disposition =NSURLSessionAuthChallengeUseCredential;

                }

            }

        }

        *_credential = credential;

        return disposition;

    }];

}

//讀取p12檔案中的密碼

- (BOOL)extractIdentity:(SecIdentityRef*)outIdentity andTrust:(SecTrustRef *)outTrust fromPKCS12Data:(NSData *)inPKCS12Data {

    OSStatus securityError = errSecSuccess;

    //client certificate password

    //構造包含了密碼的dictionary,用於傳遞給SecPKCS12Import函式。注意這裡使用的是core foundation中的CFDictionaryRef,與NSDictionary完全等價

    NSDictionary *optionsDictionary = [NSDictionary dictionaryWithObject:@"123456"

                                                                 forKey:(__bridge id)kSecImportExportPassphrase];

    CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);

    securityError = SecPKCS12Import((__bridge CFDataRef)inPKCS12Data,(__bridge CFDictionaryRef)optionsDictionary,&items);

    if(securityError == 0) {

        CFDictionaryRef myIdentityAndTrust =CFArrayGetValueAtIndex(items,0);

        const void*tempIdentity =NULL;

        tempIdentity= CFDictionaryGetValue(myIdentityAndTrust,kSecImportItemIdentity);

        *outIdentity = (SecIdentityRef)tempIdentity;

        const void*tempTrust =NULL;

        tempTrust = CFDictionaryGetValue(myIdentityAndTrust,kSecImportItemTrust);

        *outTrust = (SecTrustRef)tempTrust;

    } else {

        NSLog(@"Failedwitherror code %d",(int)securityError);

        return NO;

    }

    return YES;

}

十:AFNewworking判斷伺服器證書是否有效

- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust

                  forDomain:(NSString *)domain

{

/*

self.allowInvalidCertificates:是否信任伺服器無效或過期的SSL證書。預設為“不”allowInvalidCertificates定義了客戶端是否信任非法證書。一般來說,每個版本的iOS裝置中,都會包含一些既有的CA根證書。如果接收到的證書是iOS信任的CA根證書籤名的,那麼則為合法證書;否則則為“非法”證書。

self.validatesDomainName是否驗證域名證書的CN欄位。預設為“是”。validatesDomainName 是指是否校驗在證書中的domain這一個欄位。每個證書都會包含一個DomainName, 它可以是一個IP地址,一個域名或者一端帶有萬用字元的域名。如*.google.com, www.google.com 都可以成為這個證書的DomainName。設定validatesDomainName=YES將嚴格地保證其安全性

self.SSLPinningMode: 驗證證書的方式;

self.pinnedCertificates count:匹配的證書;pinnedCertificates 就是用來校驗伺服器返回證書的證書。通常都儲存在mainBundle下。通常預設情況下,AFNetworking會自動尋找在mainBundle的根目錄下所有的.cer檔案並儲存在pinnedCertificates數組裡,以校驗伺服器返回的證書。

*/

    if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) {

        //  According to the docs, youshould only trust your provided certs for evaluation.

        //  Pinned certificates are added tothe trust. Without pinned certificates,

        //  there is nothing to evaluateagainst.

        //

        //  From Apple Docs:

        //          "Do not implicitlytrust self-signed certificates as anchors (kSecTrustOptionImplicitAnchors).

        //           Instead, add your own(self-signed) CA certificate to the list of trusted anchors."

        NSLog(@"Inorder to validate a domain name for self signed certificates, you MUST usepinning.");

        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()];

    }

    SecTrustSetPolicies(serverTrust,(__bridge CFArrayRef)policies);

    if (self.SSLPinningMode == AFSSLPinningModeNone) {//不驗證證書內容

        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)];

            }

            SecTrustSetAnchorCertificates(serverTrust,(__bridge CFArrayRef)pinnedCertificates);

            if (!AFServerTrustIsValid(serverTrust)) {

                return NO;

            }

            // obtain the chain after being validated, which *should* contain thepinned certificate in the last position (if it's the Root CA)

            NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);

            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;

}

十一:在測試AFNetworking設定HTTPS描述。

1.  單向認證-只驗證伺服器證書的公鑰-驗證域名-信任無效證書。

測試結果:連結成功。

2.雙向認證-驗證伺服器證書全部內容-驗證域名-信任無效證書。

    測試結果:連結成功

3. 單向認證-只驗證伺服器證書的公鑰-驗證域名-信任有效證書。

測試結果:連結失敗。

4. 百度單向認證-只驗