临时中转

/*
 思路: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日起(最新消息, 实施时间已延期),所有新提交的 App 使用系统组件进行的 HTTP 网络请求都需要是 HTTPS 加密的,否则会导致请求失败而无法通过审核。
HTTPS 的验证过程有很多资料可以查询到,但对于在iOS中如何实现 HTTPS 验证却不是很清楚,偶尔看到的这篇文章,阅读后收获不小,分享给大家。

正文

本文的介绍分为三部分:

  • 1,简要分析下对服务器身份验证的完整握手过程。
  • 2,是证书链的验证。
  • 3,探索下iOS中原生库NSURLConnection或NSURLSession如何支持实现https。
一、关于HTTPS

HTTPS是承载在TLS/SSL之上的HTTP,相较于HTTP明文数据传输方面所暴露出的缺点,HTTPS具有防止信息被窃听、篡改、劫持,提供信息加密,完整性校验及身份验证等优势。TLS/SSL是安全传输层协议,介于TCP和HTTP之间。TLS1.0是建立在SSL3.0规范之上的,可以理解为SSL3.0的升级版本。目前推荐使用的版本是TLS1.2。

TLS/SSL协议通常分为两层:TLS记录协议(TLS Record Protocol)和TLS握手协议(TLS Handshake Protocol)。

  • TLS记录协议建立在可靠的传输协议(如TCP)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持。
  • TLS握手协议建立在记录协议之上,用于在实际的数据传输开始前,通讯双方进行身份认证、协商加密算法、交换加密密钥等。
  • 除了这俩协议以外,还存在其它三种辅助协议: Changecipher spec 协议用来通知对端从handshake切换到record协议(有点冗余,在TLS1.3里面已经被删掉了)。alert协议,用来通知各种返回码。application data协议,就是把http,smtp等的数据流传入record层做处理并传输。

场景分析
访问HTTPS://xxx的网站,对于HTTPS而言,在整个发送请求返回数据过程中,除了发送请求-服务器响应请求-结果返回并显示外,还涉及到通讯双方证书验证、数据加密、数据完整性校验等。
下面以登录qq邮箱为例,通过Wireshark抓包可以看到如下图:


在浏览器与服务器进行Application Data传输之前,还经历了Client Hello-Server Hello-Client Key Exchange-Change Cipher Spec等过程。而这些过程正是TLS/SSL提供的服务所决定的:

  • 认证服务器身份,确保数据发送到正确的服务器;
  • 加密数据以防止数据中途被窃取;
  • 维护数据的完整性,确保数据在传输过程中不被改变。

上述单向验证的完整握手过程,总结如下:

  • 第一阶段:ClientHello
    客户端发起请求,以明文传输请求信息,包含版本信息,加密套件候选列表,压缩算法候选列表,随机数random_C,扩展字段等信息。
  • 第二阶段:ServerHello-ServerHelloDone
    如上图可以看出这个阶段包含4个过程( 有的服务器是单条发送,有的是合并一起发送)。服务端返回协商的信息结果,包括选择使用的协议版本,选择的加密套件,选择的压缩算法、随机数random_S等,其中随机数用于后续的密钥协商。服务器也会配置并返回对应的证书链Certificate,用于身份验证与密钥交换。然后会发送ServerHelloDone信息用于通知服务器信息发送结束。
  • 第三阶段:证书校验
    在上图中的5-6之间,客户端这边还需要对服务器返回的证书进行校验。只有证书验证通过后,才能进行后续的通信。(具体分析可参看后续的证书验证过程)
  • 第四阶段:ClientKeyExchange-Finished
    服务器返回的证书验证合法后, 客户端计算产生随机数字Pre-master,并用server证书中公钥加密,发送给服务器。同时客户端会根据已有的三个随机数使用相应的算法生成协商密钥。客户端会通知服务器后续的通信都采用协商的通信密钥和加密算法进行加密通信。然后客户端发送Finished消息用于通知客户端信息发送结束。
  • 第五阶段:服务器端生成协商密钥
    服务器也会根据已有的三个随机数使用相应的算法生成协商密钥,会通知客户端后续的通信都采用协商的通信密钥和加密算法进行加密通信。然后发送Finished消息用于通知服务器信息发送结束。
  • 第六阶段:握手结束
    在握手阶段结束后,客户端和服务器数据传输开始使用协商密钥进行加密通信。

总结
简单来说,HTTPS请求整个过程主要分为两部分:

  • 一是握手过程:用于客户端和服务器验证双方身份,协商后续数据传输时使用到的密钥等。
  • 二是数据传输过程:身份验证通过并协商好密钥后,通信双方使用协商好的密钥加密数据并进行通信。

在握手过程协商密钥时,使用的是非对称密钥交换算法, 密钥交换算法本身非常复杂,密钥交换过程涉及到随机数生成,模指数运算,空白补齐,加密,签名等操作。在数据传输过程中,客户端和服务器端使用协商好的密钥进行对称加密解密。

二、证书

PKI (Public Key Infrastructure),公开密钥基础设施。它是一个标准,在这个标准之下发展出的为了实现安全基础服务目的的技术统称为PKI。 权威的第三方机构CA(认证中心)是PKI的核心, CA负责核实公钥的拥有者的信息,并颁发认证“证书”,同时能够为使用者提供证书验证服务。 x.509是PKI中最重要的标准,它定义了公钥证书的基本结构。

证书申请过程

  • 证书申请者向颁发证书的可信第三方CA提交申请证书相关信息,包括:申请者域名、申请者生成的公钥(私钥自己保存)及证书请求文件.cer等
  • CA通过线上、线下等多种手段验证证书申请者提供的信息合法和真实性。
  • 当证书申请者提供的信息审核通过后,CA向证书申请者颁发证书,证书内容包括明文信息和签名信息。其中明文信息包括证书颁发机构、证书有效期、域名、申请者相关信息及申请者公钥等,签名信息是使用CA私钥进行加密的明文信息。当证书申请者获取到证书后,可以通过安装的CA证书中的公钥对签名信息进行解密并与明文信息进行对比来验证签名的完整性。

证书验证过程

  • 验证证书本身的合法性(验证签名完整性,验证证书有效期等)
  • 验证证书颁发者的合法性(查找颁发者的证书并检查其合法性,这个过程是递归的)
    证书验证的递归过程最终会成功终止,而成功终止的条件是:证书验证过程中遇到了锚点证书,锚点证书通常指:嵌入到操作系统中的根证书(权威证书颁发机构颁发的自签名证书)。

证书验证失败的原因

  • 无法找到证书的颁发者
  • 证书过期
  • 验证过程中遇到了自签名证书,但该证书不是锚点证书。
  • 无法找到锚点证书(即在证书链的顶端没有找到合法的根证书)
  • 访问的server的dns地址和证书中的地址不同
三、iOS实现支持HTTPS

在OC中当使用NSURLConnection或NSURLSession建立URL并向服务器发送https请求获取资源时,服务器会使用HTTP状态码401进行响应(即访问拒绝)。此时NSURLConnection或NSURLSession会接收到服务器需要授权的响应,当客户端授权通过后,才能继续从服务器获取数据。如下图所示:


非自建证书验证实现
在接收到服务器返回的状态码为401的响应后,
对于NSURLSession而言,需要代理对象实现URLSession:task:didReceiveChallenge:completionHandler:方法。
对于NSURLConnection而言,需要代理对象实现connection:willSendRequestForAuthenticationChallenge: 方法(OS X v10.7和iOS5及以上)。
对于早期的版本代理对象需要实现代理对象要实现connection:canAuthenticateAgainstProtectionSpace:和connection:didReceiveAuthenticationChallenge:方法。
代码如下:

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

在其中,我们需要在block中引用self,如果直接引用,也是循环引用了,采用先定义一个weak变量,然后在block中引用weak对象,避免循环引用 你会直接想到如下的方式

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

是不是瞬间觉得完美了,呵呵,我只能说少年,你没理解两者之间的区别。在block中,block是对变量进行捕获,意思是对使用到的变量进行拷贝操作,注意是拷贝的不是对象,而是变量自身。拿上面的来说,block中只是对变量wself拷贝了一份,也就是说,block中也定义了一个weak对象,相当于,在block的内存区域中,定义了一个__weak blockWeak对象,然后执行了blockWeak = wself;注意到了没,这里并没有引起对象的持有量的变化,所以没有问题,再看timer的方式,虽然你是将wself传入了timer的构造方法中,我们可以查看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,是要强应用这个变量的 也就是说,大概是这样的,__strong strongSelf = wself 强引用了一个弱应用的变量,结果还是强引用,也就是说strongSelf持有了wself所指向的对象(也即是self所只有的对象),这和你直接传self进来是一样的效果,并不能达到解除强引用的作用!看来只能换个思路了,我直接生成一个临时对象,让Timer强用用这个临时对象,在这个临时对象中弱引用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];
}
//当不能识别方法时候,就会调用这个方法,在这个方法中,我们可以将不能识别的传递给其它对象处理
//由于这里对所有的不能处理的都传递给_target了,所以methodSignatureForSelector和forwardInvocation不可能被执行的,所以不用再重载了吧
//其实还是需要重载methodSignatureForSelector和forwardInvocation的,为什么呢?因为_target是弱引用的,所以当_target可能释放了,当它被释放了的情况下,那么forwardingTargetForSelector就是返回nil了.然后methodSignatureForSelector和forwardInvocation没实现的话,就直接crash了!!!
//这也是为什么这两个方法中随便写的!!!
- (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

使用的时候,将原来的替换为:

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

block方式来解决循环引用

@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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容