iOSHTTP与HTTPS

国际标准化组织(ISO)在1978年将计算机网络体系结构的通信协议划分为七层,自下而上依次为:物理层数据链路层网络层传输层会话层表示层应用层。其中每一层都规定有明确的服务及接口标准;把用户的应用程序作为最高层;除了最高层外,中间的每一层都向上一层提供服务,同时又是下一层的用户。

http(超文本传输协议)

HTTP是客户端浏览器或其他程序与Web服务器之间的应用层通信协议,HTTP通信的基本单位是报文。在Http工作之前,客户端通过网络和服务器建立链连接,该连接是通过TCP来完成的,一般TCP接口的端口号是80(传输层主要有两个协议,分别是 TCP 和 UDP)。

常见状态码

image

HTTP请求方法包括GET、POST、PUT、DELETE、PATCH、HEAD、

OPTIONS、TRACE。其中GET和POST最为常用。

GET和POST的区别

从语义角度来说,GET是从指定的资源请求数据,POST是向指定的资源提交要被处理的数据。通俗点将,GET是获取数据,POST是更新数据。

具体区别可以大致总结为一下几点:

(1) GET的URL可见,POST的URL不可见;

(2) GET请求可以被缓存,POST不会被缓存;

(3) GET请求可以保留在浏览器历史记录中,POST不会;

(4) GET请求有长度限制,2048个字节,POST长度没有限制;

(5) GET请求是幂等性的,POST是非幂等性的;

(6) GET请求的安全性没有POST高。

连接的建立需要通过三次握手

image

首先,介绍一下几个概念:

ACK:响应标识,1表示响应,连接建立成功之后,所有报文段ACK的值都为1

SYN:连接标识,1表示建立连接,连接请求和连接接受报文段SYN=1,其他情况都是0

FIN:关闭连接标识,1标识关闭连接,关闭请求和关闭接受报文段FIN=1,其他情况都是0,跟SYN类似

seq number:序号,一个随机数X,请求报文段中会有该字段,响应报文段没有

ack number:应答号,值为请求seq+1,即X+1,除了连接请求和连接接受响应报文段没有该字段,其他的报文段都有该字段

知道了上面几个概念后,看一下三次握手的具体流程:

第一次握手:建立连接请求。客户端发送连接请求报文段,将SYN置为1,seq为随机数x。然后,客户端进入SYN_SEND状态,等待服务器确认。

第二次握手:确认连接请求。服务器收到客户端的SYN报文段,需要对该请求进行确认,设置应答号ack=x+1(即客户端seq+1),同时设置连接标识SYN=1,响应标识ACK=1,序号seq=y。服务器一并发送给客户端,服务器进入SYN_RECV状态。

第三次握手:客户端收到SYN+ACK报文段,将ack设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕,客户端和服务券进入ESTABLISHED状态,完成Tcp三次握手。

断开连接需要四次挥手

第一次挥手:客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。

第二次挥手:服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。

第三次挥手:服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。

第四次挥手:客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。

HTTPS(:Hyper Text Transfer Protocol over Secure Socket Layer)

HTTPS的建立流程

Https在建立连接之前,需要进行握手

1.客户端向服务端发送SSL协议版本号、加密算法种类、随机数等信息。

2.服务端给客户端返回SSL协议版本号、加密算法种类、随机数等信息,同时也返回服务器端的证书,即公钥证书

3.客户端使用服务端返回的信息验证服务器的合法性,包括:

证书是否过期

发型服务器证书的CA是否可靠

返回的公钥是否能正确解开返回证书中的数字签名

服务器证书上的域名是否和服务器的实际域名相匹配

验证通过后,将继续进行通信,否则,终止通信

4.客户端向服务端发送自己所能支持的对称加密方案,供服务器端进行选择

5.服务器端在客户端提供的加密方案中选择加密程度最高的加密方式。

6.服务器将选择好的加密方案通过明文方式返回给客户端

7.客户端接收到服务端返回的加密方式后,使用该加密方式生成产生随机码,用作通信过程中对称加密的密钥,使用服务端返回的公钥进行加密,将加密后的随机码发送至服务器

8.服务器收到客户端返回的加密信息后,使用自己的私钥进行解密,获取对称加密密钥。

9.在接下来的会话中,服务器和客户端将会使用该密码进行对称加密,保证通信过程中信息的安全。

单向:客户端验证服务端的CA证书
双向:客户端验证服务器的CA证书,服务器验证客户端的p12证书

下面是AFNetworking的实现代码

manager.securityPolicy= [self customSecurityPolicy];
[self checkCredential:manager];

//启用服务器证书验证 (单向认证只需要实现此方法)
- (AFSecurityPolicy*)customSecurityPolicy

{

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

    NSData  *certData = [NSData dataWithContentsOfFile:cerPath];

    AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate withPinnedCertificates:[[NSSet alloc] initWithObjects:certData, nil]];

    securityPolicy.allowInvalidCertificates = YES;

    securityPolicy.validatesDomainName=NO;

    return securityPolicy;

}

客户端请求验证 重写 setSessionDidReceiveAuthenticationChallengeBlock 方法

- (void)checkCredential:(AFURLSessionManager *)manager
{
  [manager setSessionDidBecomeInvalidBlock:^(NSURLSession * _Nonnull session, NSError * _Nonnull error) {
  }];
  __weak typeof(manager)weakManager = manager;
  [manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession*session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing*_credential) {
    NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    __autoreleasing NSURLCredential *credential =nil;
    NSLog(@"authenticationMethod=%@",challenge.protectionSpace.authenticationMethod);
    //判断是核验客户端证书还是服务器证书
    if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        // 基于客户端的安全策略来决定是否信任该服务器,不信任的话,也就没必要响应挑战
        if([weakManager.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
            // 创建挑战证书(注:挑战方式为UseCredential和PerformDefaultHandling都需要新建挑战证书)
            NSLog(@"serverTrust=%@",challenge.protectionSpace.serverTrust);
            credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
            // 确定挑战的方式
            if (credential) {
                //证书挑战  设计policy,none,则跑到这里
                disposition = NSURLSessionAuthChallengeUseCredential;
            } else {
                disposition = NSURLSessionAuthChallengePerformDefaultHandling;
            }
        } else {
            disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
        }
    } else {
        // client authentication
        SecIdentityRef identity = NULL;
        SecTrustRef trust = NULL;
        NSString *p12 = [[NSBundle mainBundle] pathForResource:@"app"ofType:@"p12"];
        NSFileManager *fileManager =[NSFileManager defaultManager];
 
        if(![fileManager fileExistsAtPath:p12])
        {
            NSLog(@"client.p12:not exist");
        }
        else
        {
            NSData *PKCS12Data = [NSData dataWithContentsOfFile:p12];
 
            if ([self extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data])
            {
                SecCertificateRef certificate = 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;
  }];
}

验证客户端证书

+(BOOL)extractIdentity:(SecIdentityRef*)outIdentity andTrust:(SecTrustRef *)outTrust fromPKCS12Data:(NSData *)inPKCS12Data {
    OSStatus securityError = errSecSuccess;
    //客户端证书密码
    NSDictionary*optionsDictionary = [NSDictionary dictionaryWithObject:@"p12证书密码"
                                                                 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(@"Failedwith error code %d",(int)securityError);
        return NO;
    }
    return YES;
}

HTTPS和HTTP的区别主要如下:

1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。

2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。

3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。

4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

你可能感兴趣的:(iOSHTTP与HTTPS)