臨時(shí)中轉(zhuǎn)

/*
 思路:Time: O(logN),最壞O(N)
 二分排除法
 mid比右邊大,排除mid本身和mid左邊的部分
 mid比右邊小,排除mid右邊的部分
 最后返回mid
 */
class Solution {
    func findMin(_ nums: [Int]) -> Int {
        let numsCount = nums.count
        var left = 0
        var right = numsCount - 1
        while left <= right
        {
            if (left == right) {
                return nums[left]
            }
            
            let mid = left + (right - left) / 2
            if nums[mid] > nums[right] {
                left = mid + 1
            }
            else {
                right = mid
            }
        }
        
        return nums[left]
    }
}








fds

在 WWDC 16 中,Apple 表示, 從 2017年1月1日起(最新消息, 實(shí)施時(shí)間已延期),所有新提交的 App 使用系統(tǒng)組件進(jìn)行的 HTTP 網(wǎng)絡(luò)請(qǐng)求都需要是 HTTPS 加密的,否則會(huì)導(dǎo)致請(qǐng)求失敗而無法通過審核。
HTTPS 的驗(yàn)證過程有很多資料可以查詢到,但對(duì)于在iOS中如何實(shí)現(xiàn) HTTPS 驗(yàn)證卻不是很清楚,偶爾看到的這篇文章,閱讀后收獲不小,分享給大家。

正文

本文的介紹分為三部分:

  • 1,簡要分析下對(duì)服務(wù)器身份驗(yàn)證的完整握手過程。
  • 2,是證書鏈的驗(yàn)證。
  • 3,探索下iOS中原生庫NSURLConnection或NSURLSession如何支持實(shí)現(xiàn)https。
一、關(guān)于HTTPS

HTTPS是承載在TLS/SSL之上的HTTP,相較于HTTP明文數(shù)據(jù)傳輸方面所暴露出的缺點(diǎn),HTTPS具有防止信息被竊聽、篡改、劫持,提供信息加密,完整性校驗(yàn)及身份驗(yàn)證等優(yōu)勢(shì)。TLS/SSL是安全傳輸層協(xié)議,介于TCP和HTTP之間。TLS1.0是建立在SSL3.0規(guī)范之上的,可以理解為SSL3.0的升級(jí)版本。目前推薦使用的版本是TLS1.2。

TLS/SSL協(xié)議通常分為兩層:TLS記錄協(xié)議(TLS Record Protocol)和TLS握手協(xié)議(TLS Handshake Protocol)。

  • TLS記錄協(xié)議建立在可靠的傳輸協(xié)議(如TCP)之上,為高層協(xié)議提供數(shù)據(jù)封裝、壓縮、加密等基本功能的支持。
  • TLS握手協(xié)議建立在記錄協(xié)議之上,用于在實(shí)際的數(shù)據(jù)傳輸開始前,通訊雙方進(jìn)行身份認(rèn)證、協(xié)商加密算法、交換加密密鑰等。
  • 除了這倆協(xié)議以外,還存在其它三種輔助協(xié)議: Changecipher spec 協(xié)議用來通知對(duì)端從handshake切換到record協(xié)議(有點(diǎn)冗余,在TLS1.3里面已經(jīng)被刪掉了)。alert協(xié)議,用來通知各種返回碼。application data協(xié)議,就是把http,smtp等的數(shù)據(jù)流傳入record層做處理并傳輸。

場景分析
訪問HTTPS://xxx的網(wǎng)站,對(duì)于HTTPS而言,在整個(gè)發(fā)送請(qǐng)求返回?cái)?shù)據(jù)過程中,除了發(fā)送請(qǐng)求-服務(wù)器響應(yīng)請(qǐng)求-結(jié)果返回并顯示外,還涉及到通訊雙方證書驗(yàn)證、數(shù)據(jù)加密、數(shù)據(jù)完整性校驗(yàn)等。
下面以登錄qq郵箱為例,通過Wireshark抓包可以看到如下圖:


在瀏覽器與服務(wù)器進(jìn)行Application Data傳輸之前,還經(jīng)歷了Client Hello-Server Hello-Client Key Exchange-Change Cipher Spec等過程。而這些過程正是TLS/SSL提供的服務(wù)所決定的:

  • 認(rèn)證服務(wù)器身份,確保數(shù)據(jù)發(fā)送到正確的服務(wù)器;
  • 加密數(shù)據(jù)以防止數(shù)據(jù)中途被竊?。?/li>
  • 維護(hù)數(shù)據(jù)的完整性,確保數(shù)據(jù)在傳輸過程中不被改變。

上述單向驗(yàn)證的完整握手過程,總結(jié)如下:

  • 第一階段:ClientHello
    客戶端發(fā)起請(qǐng)求,以明文傳輸請(qǐng)求信息,包含版本信息,加密套件候選列表,壓縮算法候選列表,隨機(jī)數(shù)random_C,擴(kuò)展字段等信息。
  • 第二階段:ServerHello-ServerHelloDone
    如上圖可以看出這個(gè)階段包含4個(gè)過程( 有的服務(wù)器是單條發(fā)送,有的是合并一起發(fā)送)。服務(wù)端返回協(xié)商的信息結(jié)果,包括選擇使用的協(xié)議版本,選擇的加密套件,選擇的壓縮算法、隨機(jī)數(shù)random_S等,其中隨機(jī)數(shù)用于后續(xù)的密鑰協(xié)商。服務(wù)器也會(huì)配置并返回對(duì)應(yīng)的證書鏈Certificate,用于身份驗(yàn)證與密鑰交換。然后會(huì)發(fā)送ServerHelloDone信息用于通知服務(wù)器信息發(fā)送結(jié)束。
  • 第三階段:證書校驗(yàn)
    在上圖中的5-6之間,客戶端這邊還需要對(duì)服務(wù)器返回的證書進(jìn)行校驗(yàn)。只有證書驗(yàn)證通過后,才能進(jìn)行后續(xù)的通信。(具體分析可參看后續(xù)的證書驗(yàn)證過程)
  • 第四階段:ClientKeyExchange-Finished
    服務(wù)器返回的證書驗(yàn)證合法后, 客戶端計(jì)算產(chǎn)生隨機(jī)數(shù)字Pre-master,并用server證書中公鑰加密,發(fā)送給服務(wù)器。同時(shí)客戶端會(huì)根據(jù)已有的三個(gè)隨機(jī)數(shù)使用相應(yīng)的算法生成協(xié)商密鑰??蛻舳藭?huì)通知服務(wù)器后續(xù)的通信都采用協(xié)商的通信密鑰和加密算法進(jìn)行加密通信。然后客戶端發(fā)送Finished消息用于通知客戶端信息發(fā)送結(jié)束。
  • 第五階段:服務(wù)器端生成協(xié)商密鑰
    服務(wù)器也會(huì)根據(jù)已有的三個(gè)隨機(jī)數(shù)使用相應(yīng)的算法生成協(xié)商密鑰,會(huì)通知客戶端后續(xù)的通信都采用協(xié)商的通信密鑰和加密算法進(jìn)行加密通信。然后發(fā)送Finished消息用于通知服務(wù)器信息發(fā)送結(jié)束。
  • 第六階段:握手結(jié)束
    在握手階段結(jié)束后,客戶端和服務(wù)器數(shù)據(jù)傳輸開始使用協(xié)商密鑰進(jìn)行加密通信。

總結(jié)
簡單來說,HTTPS請(qǐng)求整個(gè)過程主要分為兩部分:

  • 一是握手過程:用于客戶端和服務(wù)器驗(yàn)證雙方身份,協(xié)商后續(xù)數(shù)據(jù)傳輸時(shí)使用到的密鑰等。
  • 二是數(shù)據(jù)傳輸過程:身份驗(yàn)證通過并協(xié)商好密鑰后,通信雙方使用協(xié)商好的密鑰加密數(shù)據(jù)并進(jìn)行通信。

在握手過程協(xié)商密鑰時(shí),使用的是非對(duì)稱密鑰交換算法, 密鑰交換算法本身非常復(fù)雜,密鑰交換過程涉及到隨機(jī)數(shù)生成,模指數(shù)運(yùn)算,空白補(bǔ)齊,加密,簽名等操作。在數(shù)據(jù)傳輸過程中,客戶端和服務(wù)器端使用協(xié)商好的密鑰進(jìn)行對(duì)稱加密解密。

二、證書

PKI (Public Key Infrastructure),公開密鑰基礎(chǔ)設(shè)施。它是一個(gè)標(biāo)準(zhǔn),在這個(gè)標(biāo)準(zhǔn)之下發(fā)展出的為了實(shí)現(xiàn)安全基礎(chǔ)服務(wù)目的的技術(shù)統(tǒng)稱為PKI。 權(quán)威的第三方機(jī)構(gòu)CA(認(rèn)證中心)是PKI的核心, CA負(fù)責(zé)核實(shí)公鑰的擁有者的信息,并頒發(fā)認(rèn)證“證書”,同時(shí)能夠?yàn)槭褂谜咛峁┳C書驗(yàn)證服務(wù)。 x.509是PKI中最重要的標(biāo)準(zhǔn),它定義了公鑰證書的基本結(jié)構(gòu)。

證書申請(qǐng)過程

  • 證書申請(qǐng)者向頒發(fā)證書的可信第三方CA提交申請(qǐng)證書相關(guān)信息,包括:申請(qǐng)者域名、申請(qǐng)者生成的公鑰(私鑰自己保存)及證書請(qǐng)求文件.cer等
  • CA通過線上、線下等多種手段驗(yàn)證證書申請(qǐng)者提供的信息合法和真實(shí)性。
  • 當(dāng)證書申請(qǐng)者提供的信息審核通過后,CA向證書申請(qǐng)者頒發(fā)證書,證書內(nèi)容包括明文信息和簽名信息。其中明文信息包括證書頒發(fā)機(jī)構(gòu)、證書有效期、域名、申請(qǐng)者相關(guān)信息及申請(qǐng)者公鑰等,簽名信息是使用CA私鑰進(jìn)行加密的明文信息。當(dāng)證書申請(qǐng)者獲取到證書后,可以通過安裝的CA證書中的公鑰對(duì)簽名信息進(jìn)行解密并與明文信息進(jìn)行對(duì)比來驗(yàn)證簽名的完整性。

證書驗(yàn)證過程

  • 驗(yàn)證證書本身的合法性(驗(yàn)證簽名完整性,驗(yàn)證證書有效期等)
  • 驗(yàn)證證書頒發(fā)者的合法性(查找頒發(fā)者的證書并檢查其合法性,這個(gè)過程是遞歸的)
    證書驗(yàn)證的遞歸過程最終會(huì)成功終止,而成功終止的條件是:證書驗(yàn)證過程中遇到了錨點(diǎn)證書,錨點(diǎn)證書通常指:嵌入到操作系統(tǒng)中的根證書(權(quán)威證書頒發(fā)機(jī)構(gòu)頒發(fā)的自簽名證書)。

證書驗(yàn)證失敗的原因

  • 無法找到證書的頒發(fā)者
  • 證書過期
  • 驗(yàn)證過程中遇到了自簽名證書,但該證書不是錨點(diǎn)證書。
  • 無法找到錨點(diǎn)證書(即在證書鏈的頂端沒有找到合法的根證書)
  • 訪問的server的dns地址和證書中的地址不同
三、iOS實(shí)現(xiàn)支持HTTPS

在OC中當(dāng)使用NSURLConnection或NSURLSession建立URL并向服務(wù)器發(fā)送https請(qǐng)求獲取資源時(shí),服務(wù)器會(huì)使用HTTP狀態(tài)碼401進(jìn)行響應(yīng)(即訪問拒絕)。此時(shí)NSURLConnection或NSURLSession會(huì)接收到服務(wù)器需要授權(quán)的響應(yīng),當(dāng)客戶端授權(quán)通過后,才能繼續(xù)從服務(wù)器獲取數(shù)據(jù)。如下圖所示:


非自建證書驗(yàn)證實(shí)現(xiàn)
在接收到服務(wù)器返回的狀態(tài)碼為401的響應(yīng)后,
對(duì)于NSURLSession而言,需要代理對(duì)象實(shí)現(xiàn)URLSession:task:didReceiveChallenge:completionHandler:方法。
對(duì)于NSURLConnection而言,需要代理對(duì)象實(shí)現(xiàn)connection:willSendRequestForAuthenticationChallenge: 方法(OS X v10.7和iOS5及以上)。
對(duì)于早期的版本代理對(duì)象需要實(shí)現(xiàn)代理對(duì)象要實(shí)現(xiàn)connection:canAuthenticateAgainstProtectionSpace:和connection:didReceiveAuthenticationChallenge:方法。
代碼如下:

@property (nonatomic, copy) void  (^ myblock)(NSInteger i);
__weak typeof (self) weakSelf = self;
self.myblock = ^(NSInteger i){
    [weakSelf view];
};

在其中,我們需要在block中引用self,如果直接引用,也是循環(huán)引用了,采用先定義一個(gè)weak變量,然后在block中引用weak對(duì)象,避免循環(huán)引用 你會(huì)直接想到如下的方式

__weak typeof (self) wself = self;
self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:1
                                                       target:wself
                                                     selector:@selector(animationTimerDidFired:)
                                                     userInfo:nil
                                                      repeats:YES];

是不是瞬間覺得完美了,呵呵,我只能說少年,你沒理解兩者之間的區(qū)別。在block中,block是對(duì)變量進(jìn)行捕獲,意思是對(duì)使用到的變量進(jìn)行拷貝操作,注意是拷貝的不是對(duì)象,而是變量自身。拿上面的來說,block中只是對(duì)變量wself拷貝了一份,也就是說,block中也定義了一個(gè)weak對(duì)象,相當(dāng)于,在block的內(nèi)存區(qū)域中,定義了一個(gè)__weak blockWeak對(duì)象,然后執(zhí)行了blockWeak = wself;注意到了沒,這里并沒有引起對(duì)象的持有量的變化,所以沒有問題,再看timer的方式,雖然你是將wself傳入了timer的構(gòu)造方法中,我們可以查看NSTimer的

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti
                                     target:(id)aTarget
                                   selector:(SEL)aSelector
                                   userInfo:(nullable id)userInfo
                                    repeats:(BOOL)yesOrNo;

定義,其target的說明The object to which to send the message specified by aSelector when the timer fires. The timer maintains a strong reference to this object until it (the timer) is invalidated,是要強(qiáng)應(yīng)用這個(gè)變量的 也就是說,大概是這樣的,__strong strongSelf = wself 強(qiáng)引用了一個(gè)弱應(yīng)用的變量,結(jié)果還是強(qiáng)引用,也就是說strongSelf持有了wself所指向的對(duì)象(也即是self所只有的對(duì)象),這和你直接傳self進(jìn)來是一樣的效果,并不能達(dá)到解除強(qiáng)引用的作用!看來只能換個(gè)思路了,我直接生成一個(gè)臨時(shí)對(duì)象,讓Timer強(qiáng)用用這個(gè)臨時(shí)對(duì)象,在這個(gè)臨時(shí)對(duì)象中弱引用self

@interface YYWeakProxy : NSProxy
@property (nonatomic, weak, readonly) id target;
- (instancetype)initWithTarget:(id)target;
+ (instancetype)proxyWithTarget:(id)target;
@end
@implementation YYWeakProxy
- (instancetype)initWithTarget:(id)target {
_target = target;
return self;
}
+ (instancetype)proxyWithTarget:(id)target {
return [[YYWeakProxy alloc] initWithTarget:target];
}
//當(dāng)不能識(shí)別方法時(shí)候,就會(huì)調(diào)用這個(gè)方法,在這個(gè)方法中,我們可以將不能識(shí)別的傳遞給其它對(duì)象處理
//由于這里對(duì)所有的不能處理的都傳遞給_target了,所以methodSignatureForSelector和forwardInvocation不可能被執(zhí)行的,所以不用再重載了吧
//其實(shí)還是需要重載methodSignatureForSelector和forwardInvocation的,為什么呢?因?yàn)開target是弱引用的,所以當(dāng)_target可能釋放了,當(dāng)它被釋放了的情況下,那么forwardingTargetForSelector就是返回nil了.然后methodSignatureForSelector和forwardInvocation沒實(shí)現(xiàn)的話,就直接crash了!!!
//這也是為什么這兩個(gè)方法中隨便寫的!!!
- (id)forwardingTargetForSelector:(SEL)selector {
return _target;
}
- (void)forwardInvocation:(NSInvocation *)invocation {
void *null = NULL;
[invocation setReturnValue:&null];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
return [NSObject instanceMethodSignatureForSelector:@selector(init)];
}
- (BOOL)respondsToSelector:(SEL)aSelector {
return [_target respondsToSelector:aSelector];
}
- (BOOL)isEqual:(id)object {
return [_target isEqual:object];
}
- (NSUInteger)hash {
return [_target hash];
}
- (Class)superclass {
return [_target superclass];
}
- (Class)class {
return [_target class];
}
- (BOOL)isKindOfClass:(Class)aClass {
return [_target isKindOfClass:aClass];
}
- (BOOL)isMemberOfClass:(Class)aClass {
return [_target isMemberOfClass:aClass];
}
- (BOOL)conformsToProtocol:(Protocol *)aProtocol {
return [_target conformsToProtocol:aProtocol];
}
- (BOOL)isProxy {
return YES;
}
- (NSString *)description {
return [_target description];
}
- (NSString *)debugDescription {
return [_target debugDescription];
}
@end

使用的時(shí)候,將原來的替換為:

self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:1
                                                       target:[YYWeakProxy proxyWithTarget:self ]
                                                     selector:@selector(animationTimerDidFired:)
                                                     userInfo:nil
                                                      repeats:YES];

block方式來解決循環(huán)引用

@interface NSTimer (JQUsingBlock)
+ (NSTimer *)jq_scheduledTimerWithTimeInterval:(NSTimeInterval)timeInterval
                                         block:(void(^)())block
                                       repeats:(BOOL)repeats;
@end

@implementation NSTimer (JQUsingBlock)

+ (NSTimer *)jq_scheduledTimerWithTimeInterval:(NSTimeInterval)timeInterval
                                         block:(void(^)())block
                                       repeats:(BOOL)repeats{
    
    return [self scheduledTimerWithTimeInterval:timeInterval
                                         target:self
                                       selector:@selector(jq_blockInvoke:)
                                       userInfo:[block copy]
                                        repeats:repeats];
}

+ (void)jq_blockInvoke:(NSTimer *)timer{
    
    void(^block)() = timer.userInfo;
    if (block) {
        block();
    }
}

@end

__weak typeof(self)weakSelf = self;
_timer = [NSTimer jq_scheduledTimerWithTimeInterval:self.autoScrollInterval
                                              block:^{
                                                  __strong typeof(self)strongSelf = weakSelf;
                                                  [strongSelf timerAutoScroll:nil];
                                              }
                                            repeats:YES];


參考:
http://www.cocoachina.com/ios/20160204/15226.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容