IOS 使用自签名证书开发HTTPS文件传输

@[TOC](IOS 使用自签名证书开发HTTPS文件传输)

1. HTTPS文件传输简介

最近由于项目需要实现IOS数据通过https实现与机器人端的文件传输功能。参考了很多资料,最终实现了一个文件传输功能,其中在https证书配置方面遇到了很多坑,写这篇文章一是对这段工作的总结,方便自己以后查阅,同时也希望帮助到更多有同样需求的哥们,少走一点弯路,节省时间。

2. HTTPS 对比HTTP简介

2.1 HTTP的一些基本概念

  • 什么是HTTP

HTTP:是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),用于从WWW服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效,使网络传输减少。
HTTP有称为:超文本传输协议(HTTP,HyperText Transfer Protocol)所有的WWW文件都必须遵守这个标准。设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法。1960年美国人Ted Nelson构思了一种通过计算机处理文本信息的方法,并称之为超文本(hypertext),这成为了HTTP超文本传输协议标准架构的发展根基。Ted Nelson组织协调万维网协会(World Wide Web Consortium)和互联网工程工作小组(Internet Engineering Task Force )共同合作研究,最终发布了一系列的RFC,其中著名的RFC 2616定义了HTTP 1.1。

  • HTTP有什么作用?

主要功能:

  1. HTTP协议(HyperText Transfer Protocol,超文本传输协议)是用于从WWW服务器传输超文本到本地浏览器的传输协议。它可以使浏览器更加高效,使网络传输减少。它不仅保证计算机正确快速地传输超文本文档,还确定传输文档中的哪一部分,以及哪部分内容首先显示(如文本先于图形)等。
  2. HTTP是客户端浏览器或其他程序与Web服务器之间的应用层通信协议。在Internet上的Web服务器上存放的都是超文本信息,客户机需要通过HTTP协议传输所要访问的超文本信息。HTTP包含命令和传输信息,不仅可用于Web访问,也可以用于其他因特网/内联网应用系统之间的通信,从而实现各类应用资源超媒体访问的集成。
  3. 我们在浏览器的地址栏里输入的网站地址叫做URL (Uniform Resource Locator,统一资源定位符)。就像每家每户都有一个门牌地址一样,每个网页也都有一个Internet地址。当你在浏览器的地址框中输入一个URL或是单击一个超级链接时,URL就确定了要浏览的地址。浏览器通过超文本传输协议(HTTP),将Web服务器上站点的网页代码提取出来,并翻译成漂亮的网页。

HTTP是一个客户端和服务器端请求和应答的标准(TCP)。客户端是终端用户,服务器端是网站。通过使用Web浏览器、网络爬虫或者其它的工具,客户端发起一个到服务器上指定端口(默认端口为80)的HTTP请求。(我们称这个客户端)叫用户代理(user agent)。应答的服务器上存储着(一些)资源,比如HTML文件和图像。(我们称)这个应答服务器为源服务器(origin server)。在用户代理和源服务器中间可能存在多个中间层,比如代理,网关,或者隧道(tunnels)。尽管TCP/IP协议是互联网上最流行的应用,HTTP协议并没有规定必须使用它和(基于)它支持的层。 事实上,HTTP可以在任何其他互联网协议上,或者在其他网络上实现。HTTP只假定(其下层协议提供)可靠的传输,任何能够提供这种保证的协议都可以被其使用。
通常,由HTTP客户端发起一个请求,建立一个到服务器指定端口(默认是80端口)的TCP连接。HTTP服务器则在那个端口监听客户端发送过来的请求。一旦收到请求,服务器(向客户端)发回一个状态行,比如"HTTP/1.1 200 OK",和(响应的)消息,消息的消息体可能是请求的文件、错误消息、或者其它一些信息。HTTP使用TCP而不是UDP的原因在于(打开)一个网页必须传送很多数据,而TCP协议提供传输控制,按顺序组织数据,和错误纠正。
通过HTTP或者HTTPS协议请求的资源由统一资源标示符(Uniform Resource Identifiers)(或者,更准确一些,URLs)来标识。

  • HTTP 的主要特点?
  1. 基于请求/响应模型的协议。请求和响应必须成对,先有请求后有响应
  2. http协议默认端口:80
  3. 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
  4. 灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
    5 .无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
  5. 无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
  • HTTP协议有哪些主要版本?
版本 产生时间 内容 发展现状
HTTP/0.9 1991年 不涉及数据包传输,规定客户端和服务器之间通信格式,只能GET请求 没有作为正式的标准
HTTP/1.0 1996年 传输内容格式不限制,增加PUT、PATCH、HEAD、 OPTIONS、DELETE命令 正式作为标准
HTTP/1.1 1997年 持久连接(长连接)、节约带宽、HOST域、管道机制、分块传输编码 2015年前使用最广泛
HTTP/2 2015年 多路复用、服务器推送、头信息压缩、二进制协议等 逐渐覆盖市场

HTTP/0.9 :只接受GET一种请求方法,没有在通信中指定版本号,且不支持请求头。由于该版本不支持POST方法,因此客户端无法向服务器传递太多信息。
HTTP/1.0 :第一个在通信中指定的版本号,至今被广泛采用,特别是在代理服务器中。
HTTP/1.1 :当前版本号,持久连接被默认采用,并能很好地配合代理服务器工作。还支持以管道方式在同时发送多个请求,以便降低线路负载,提高传输速度。
HTTP/2.0 正在开发中······

  • HTTP/1.1与HTTP/1.0的区别?

主要区别:
在同一个tcp的连接中可以传送多个HTTP请求和响应.
多个请求和响应可以重叠,多个请求和响应可以同时进行.
更加多的请求头和响应头(比如HTTP1.0没有host的字段).
总之在HTTP/1.0中,大多实现为每个请求/响应交换使用新的连接。在HTTP/1.1中,一个连接可用于一次或多次请求/响应交换,尽管连接可能由于各种原因被关闭。

  1. persistent connection(持久连接)
    HTTP/1.0中,每对请求/ 响应都使用一个新的连接。
    HTTP/1.1则支持持久连接(默认)。
  2. Host域
    HTTP/1.1在请求消息头多一个Host域;HTTP/1.0 则没有这个域,建立TCP连接的时候已经指定了IP地址,而且默认一个IP地址只对应一个主机名,IP地址上只有一个host。
  3. 带宽优化
    HTTP/1.1中在请求消息中引入了range头域,它允许只请求资源的某个部分。在响应消息中Content-Range头域声明了返回的这部分对象 的偏移值和长度。如果服务器相应地返回了对象所请求范围的内容,则响应码为206(Partial Content),它可以防止Cache将响应误以为是完整的一个对象。请求消息中如果包含比较大的实体内容,但不确定服务器是否能够接收该请求(如是否有权限),此时若贸然发出带实体的请求,如果被拒绝也会浪费带宽。 HTTP/1.1加入了一个新的状态码100(Continue)。客户端事先发送一个只带头域的请求,如果服务器因为权限拒绝了请求,就回送响应码 401(Unauthorized);如果服务器接收此请求就回送响应码100,客户端就可以继续发送带实体的完整请求了。注意,HTTP/1.0的客户 端不支持100响应码。
    节省带宽资源的一个非常有效的做法就是压缩要传送的数据。Content-Encoding是对消息进行端到端(end-to-end)的编码,它可能是 资源在服务器上保存的固有格式(如jpeg图片格式);在请求消息中加入Accept-Encoding头域,它可以告诉服务器客户端能够解码的编码方 式。而Transfer-Encoding是逐段式(hop-by-hop)的编码,如Chunked编码。在请求消息中加入TE头 域用来告诉服务器能够接收的transfer-coding方式。
  4. 请求方法和状态码
    HTTP1.1增加了OPTIONS, PUT, DELETE, TRACE, CONNECT这些Request方法
    HTTP/1.0中只定义了16个状态响应码,对错误或警告的提示不够具体。HTTP/1.1引入了一个Warning头域,增加对错误或警告信息的描述。
    在HTTP/1.1中新增了24个状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。
  5. 内容协商
    为 了满足互联网使用不同母语和字符集的用户,一些网络资源有不同的语言版本(如中文版、英文版)。HTTP/1.0定义了内容协商 (content negotiation)的概念,也就是说客户端可以告诉服务器自己可以接收以何种语言(或字符集)表示的资源。例如如果服务器不能明确 客户端需要何种类型的资源,会返回300(Multiple Choices),并包含一个列表,用来声明该资源的不同可用版本,然后客户端在请求消息中包含Accept-Language和Accept- Charset头域指定需要的版本。
  6. 状态码
    100~199:信息状态码,表示成功接收请求,要求客户端继续提交下一次请求才能完成整个处理过程
    100(continue)继续发送
    200~299:成功状态码,表示成功接收请求并已完成整个处理过程,常用200(OK)成功接收
    300~399:重定向状态码,例如,请求的资源已经移动一个新地址,常用302、307和304
    400~499:客户端的请求有错误,常用404(Not Found),403(Fobidden)
    500~599:服务器端出现错误,常用 500
  • HTTP1.1 解决了HTTP1.0的相关痛点?
  1. 一个WEB站点每天可能要接收到上百万的用户请求,为了提高系统的效率,HTTP 1.0规定浏览器与服务器只保持短暂的连接,浏览器的每次请求都需要与服务器建立一个TCP连接,服务器完成请求处理后立即断开TCP连接,服务器不跟踪每个客户也不记录过去的请求。
  2. 这也造成了一些性能上的缺陷,例如,一个包含有许多图像的网页文件中并没有包含真正的图像数据内容,而只是指明了这些图像的URL地址,当WEB浏览器访问这个网页文件时,浏览器首先要发出针对该网页文件的请求,当浏览器解析WEB服务器返回的该网页文档中的HTML内容时,发现其中的img图像标签后,浏览器将根据img标签中的src属性所指定的URL地址再次向服务器发出下载图像数据的请求。
  3. 显然,访问一个包含有许多图像的网页文件的整个过程包含了多次请求和响应,每次请求和响应都需要建立一个单独的连接,每次连接只是传输一个文档和图像,上一次和下一次请求完全分离。即使图像文件都很小,但是客户端和服务器端每次建立和关闭连接却是一个相对比较费时的过程,并且会严重影响客户机和服务器的性能。当一个网页文件中包含Applet,JavaScript文件,CSS文件等内容时,也会出现类似上述的情况。
  4. 为了克服HTTP 1.0的这个缺陷,HTTP 1.1支持持久连接,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟。一个包含有许多图像的网页文件的多个请求和应答可以在一个连接中传输,但每个单独的网页文件的请求和应答仍然需要使用各自的连接。HTTP 1.1还允许客户端不用等待上一次请求结果返回,就可以发出下一次请求,但服务器端必须按照接收到客户端请求的先后顺序依次回送响应结果,以保证客户端能够区分出每次请求的响应内容,这样也显著地减少了整个下载过程所需要的时间。基于HTTP 1.1协议的客户机与服务器的信息交换过程。
  5. 可见HTTP 1.1在继承了HTTP 1.0优点的基础上,也克服了HTTP 1.0的性能问题。不仅如此,HTTP 1.1还通过增加更多的请求头和响应头来改进和扩充HTTP 1.0的功能。例如,由于HTTP 1.0不支持Host请求头字段,WEB浏览器无法使用主机头名来明确表示要访问服务器上的哪个WEB站点,这样就无法使用WEB服务器在同一个IP地址和端口号上配置多个虚拟WEB站点。在HTTP 1.1中增加Host请求头字段后,WEB浏览器可以使用主机头名来明确表示要访问服务器上的哪个WEB站点,这才实现了在一台WEB服务器上可以在同一个IP地址和端口号上使用不同的主机名来创建多个虚拟WEB站点。HTTP 1.1的持续连接,也需要增加新的请求头来帮助实现,例如,Connection请求头的值为Keep-Alive时,客户端通知服务器返回本次请求结果后保持连接;Connection请求头的值为close时,客户端通知服务器返回本次请求结果后关闭连接。HTTP 1.1还提供了与身份认证、状态管理和Cache缓存等机制相关的请求头和响应头。
  • HTTP报文格式
HTTP报文格式
  • HTTP请求报文
  1. HTTP请求报文组成:请求行+请求头+请求体


    HTTP请求报文组成
  2. 请求行(HTTP请求报文的第一行)
    请求行由方法字段、URL字段和HTTP协议版本字段。其中,方法字段严格区分大小写,当前HTTP协议中的方法都是大写,方法字段如下介绍如下:
  3. 方法字段
    ①GET:请求获取Request-URI(URI:通用资源标识符,URL是其子集,URI注重的是标识,而URL强调的是位置,可以将URL看成原始的URI),所标识的资源
    ②POST:在Request-URI所标识的资源后附加新的数据;支持HTML表单提交,表单中有用户添入的数据,这些数据会发送到服务器端,由服务器存储至某位置(例如发送处理程序)
    ③HEAD:请求Request-URI所标识的资源响应消息报头,HEAD方法可以在响应时不返回消息体。
    ④PUT:与GET相反,请求服务器存储一个资源,并用Request-URI做为其标识;例如发布系统。
    ⑤DELETE:请求删除URL指向的资源
    ⑥OPTIONS:请求查询服务器的性能,或者查询与资源相关的选项
    ⑦TRACE:跟踪请求要经过的防火墙、代理或网关等,主要用于测试或诊断
    ⑧CONNECT保留将来使用
  4. URL
    一个完整的包括类型、主机名和可选路径名的统一资源引用名,如:http://www.example.com/path/to/file.html
  5. 请求头部:位于请求行的下面
    请求报文中常见的标头有:
    Connetion标头(连接管理)、Host标头(指定请求资源的主机)、Range标头(请求实体的字节范围)、User-Agent标头(包含发出请求的用户信息)、Accept标头(首选的媒体类型)、Accept-Language(首选的自然语言)
  6. HTTP首部:
    6.1 通用首部:请求和响应都可以使用的;
    Connection:定义C/S之间关于请求/响应的有关选项
    对于http/1.0, Connection: keep-alive
    Via: 显示了报文经过的中间节点
    Cache-Control: 缓存指示
    6.2 实体首部:用于指定实体属性
    实体主体用于POST方法中。用户向Web服务器提交表单数据的时候,需要使用POST方法,此时主体中包含用户添写在表单的各个属性字段的值,当Web服务器收到POST方法的HTTP请求报文后,可以从实体中取出需要的属性字段的值。
    也就是说,当用户通过Web浏览器向Web服务器发送请求时,Web浏览器会根据用户的具体请求来选择不同的HTTP请求方法,再将相应的URL和HTTP协议版本及相关的标头填入头部行中,若是POST方法,还会将相关的表单数据填入实体主体中,产生一个HTTP请求报文,然后将这个报文发送给Web服务器。
  1. 请求报文分析:


    请求报文分析
  2. 常见请求头属性:
    Location: 资源的新位置
    Allow: 允许对此资源使用的请求方法
    1、内容首部:
    Content-Encoding:支持的编码
    Content-Language:支持的自然语言
    Content-Length:文本长度
    Content-Location:资源所在位置
    Content-Range:在整个资源中此实体表示的字节范围
    Content-Type:主体的对象类型
    2、缓存首部:
    ETag: 实体标签
    Expires: 过期期限
    Last-Modified: 上一次的修改时间
    请求首部:
    Host: 请求的主机名和端口号,虚拟主机环境下用于不同的虚拟主机
    Referer:指明了请求当前资源的原始资源的URL
    User-Agent: 用户代理,使用什么工具发出的请求
    1、Accept首部:用户标明客户自己更倾向于支持的能力
    Accept: 指明服务器能发送的媒体类型
    Accept-Charset: 支持使用的字符集
    Accept-Encoding: 支持使用的编码方式
    Accept-Language: 支持使用语言
    2、条件请求首部:
    Expect: 告诉服务器能够发送来哪些媒体类型
    If-Modified-Since: 是否在指定时间以来修改过此资源
    If-None-Match:如果提供的实体标记与当前文档的实体标记不符,就获取此文档
    跟安全相关的请求首部:
    Authorization: 客户端提交给服务端的认证数据,如帐号和密码
    Cookie: 客户端发送给服务器端身份标识

  3. 请求字段
    1.Accept 作用:浏览器客户端用来告诉服务端能接受什么类型的响应。
    例如: Accept: text/html 代表浏览器可以接受服务器回发html文档,如果服务器无法返回text/html类型的数据,服务器应该返回一个406错误(non acceptable)
    通配符 * 代表任意类型。如: Accept: / 代表浏览器可以处理所有类型
    2.Accept-Encoding 作用:浏览器客户端用来告诉服务器能接受什么编码格式,包括字符编码、压缩方式等
    例如:Accept-Encoding:gzip, deflate
    3.Accept-Language 作用:浏览器客户端用来告诉服务器能接受什么语言。
    例如:Accept-Language:zh-CN,zh;q=0.9
    4.Connection 作用:客户端或服务端用来告诉对方当前tcp连接的状态,默认为keep-alive,即长连接。
    例如:Connection:close 在响应结束后关闭连接
    5.Host 作用:指定要请求的资源所在的主机和端口,通常从url里获取。这个字段是必需的。
    例如:我们在地址栏输入:http://www.baidu.com Host:www.baidu.com
    6.Referer 作用:浏览器客户端用来告诉服务器这个请求是从哪个页面链接过来的,即请求来源。
    7.User-Agent 作用:告诉服务器,客户端使用的操作系统、浏览器版本和名称
    例如:Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36
    另外,有些属性不一定会有但比较常见:
    1.Cache-Control 作用:客户端浏览器用来判断是否需要用本地缓存。默认值为private;常用值有private、no-cache、max-age、must-revalidate。具体场景举例:
    a.打开新窗口时值为private、no-cache、must-revalidate,那么打开新窗口访问时都会重新访问服务器。而如果指定了max-age值(单位为秒),那么在此值内的时间里就不会重新访问服务器,例如:
    Cache-control: max-age=5(表示当访问此网页后的5秒内再次访问不会去服务器)
    b.在地址栏回车
    值为private或must-revalidate则只有第一次访问时会访问服务器,以后就不再访问。
    值为no-cache,那么每次都会访问。
    值为max-age,则在过期之前不会重复访问。
    c.按后退按扭
    值为private、must-revalidate、max-age,则不会重访问,
    值为no-cache,则每次都重复访问
    d.按刷新按扭
    无论为何值,都会重复访问
    2.Cookie 作用:客户端浏览器用来存储一些用户信息以便让服务器辨别用户身份的(大多数需要登录的网站上面会比较常见),比如用户名和密码,sessionId等。
    3.If-Modify-Since 作用:把浏览器端缓存页面的最后修改时间(精确到秒)发送到服务器去,服务器会把这个时间与服务器上实际文件的最后修改时间进行对比。如果时间一致,那么返回304,客户端就直接使用本地缓存文件。如果时间不一致,就会返回200和新的文件内容以及新的修改时间(Last-Modify)。客户端接到之后,会丢弃旧文件,把新文件缓存起来,并显示在浏览器中。
    例如:Wed, 30 May 2018 08:32:42 GMT
    4.If-None-Match 作用:If-None-Match和ETag一起工作,工作原理是在HTTP Response中添加ETag信息。 当用户再次请求该资源时,将在HTTP Request 中加入If-None-Match信息(ETag的值)。如果服务器验证资源的ETag没有改变(该资源没有更新),将返回一个304状态告诉客户端使用本地缓存文件。否则将返回200状态和新的资源和Etag. 使用这样的机制将提高网站的性能。
    例如: If-None-Match: W/"3119-1437038474000"
    注意:If-Modify-Since和If-None-Match都可以给服务器用来判断所请求的文件距离上次访问之间是否被修改过,不过If-Modify-Since只能精确到秒,而If-None-Match只要文件修改过就会变化。
    Etag的使用场景:1.有些文件需要频繁更新,但是文件内容并没有变化。
    2.同一文件存储在多台web服务器中,用户请求在多台之间轮询。

  • 响应报文
  1. HTTP响应报文同样也分为三部分,有状态行、首部行、实体


    响应报文
  2. 状态行:HTTP响应报文的第一行
    状态行包括三个字段:协议版本、状态码与原因短语。
  3. 状态码:
    1xx:
    这一类型的状态码,代表请求已被接受,需要继续处理。这类响应是临时响应,只包含状态行和某些可选的响应头信息,并以空行结束。
    2xx:
    这一类型的状态码,代表请求已成功被服务器接收、理解、并接受。
    3xx:
    这类状态码代表需要客户端采取进一步的操作才能完成请求。通常,这些状态码用来重定向,后续的请求地址(重定向目标)在本次响应的Location域中指明。
    4xx:
    这类的状态码代表客户端类的错误
    5xx:
    服务器类的错误
  4. 常见状态码:


    常见状态码
  5. 响应首部


    响应首部
  6. 响应字段
    响应首部(首部行):位于响应报文状态行之后
    Date标头:消息产生的时间
    Age标头:(从最初创建开始)响应持续时间
    Server标头: 向客户端标明服务器程序名称和版本
    ETage标头:不透明验证者
    Location标头:URL备用的位置
    Content-Length标头:实体的长度
    Content-Tyep标头:实体的媒体类型
    协商首部:
    Accept-Ranges: 对当前资源来讲,服务器所能够接受的范围类型
    Vary: 首部列表,服务器会根据列表中的内容挑选出最适合的版本发送给客户端
    跟安全相关的响应首部:
    Set-Cookie: 服务器端在某客户端第一次请求时发给令牌
    WWW-Authentication: 质询,即要求客户提供帐号和密码
  7. 实体:
    位于首部行之后
    实体包含了Web客户端请求的对象。Content-Length标头及Content-Type标头用于计算实体的位置、数据类型和数据长度。当Web服务器接收到Web客户端的请求报文后,对HTTP请求报文进行解析,并将Web客户端的请求的对象取出打包,通过HTTP响应报文将数据传回给Web客户端,如果出现错误则返回包含对应错误的错误代码和错误原因的HTTP响应报文。

2.2 HTTPS 的一些基本概念

  • 什么是HTTPS

HTTPS实际就是HTTP + SSL,就是在HTTP协议的基础上增加了SSL安全加密传输。
《图解HTTP》这本书中曾提过HTTPS是身披SSL外壳的HTTP。HTTPS是一种通过计算机网络进行安全通信的传输协议,经由HTTP进行通信,利用SSL/TLS建立全信道,加密数据包。HTTPS使用的主要目的是提供对网站服务器的身份认证,同时保护交换数据的隐私与完整性。

TLS是传输层加密协议,前身是SSL协议,由网景公司1995年发布,有时候两者不区分。

  • HTTPS的特点
  1. 内容加密:采用混合加密技术,中间者无法直接查看明文内容
  2. 验证身份:通过证书认证客户端访问的是自己的服务器
  3. 保护数据完整性:防止传输的内容被中间人冒充或者篡改

我们可以对比HTTP抓包分析

  1. HTTP抓包如下:


    HTTP抓包
  2. HTTPS抓包:


    HTTPS抓包

    通过抓包可以看到HTTPS中数据不是明文传输。HTTPS主要通过RSA混合加密,使用RSA加密客户端产生的随机秘钥,服务器端得到客户端的秘钥,双方就可以使用这个随机秘钥进行对称加密传输。

  3. 混合加密:结合非对称加密和对称加密技术。客户端使用对称加密生成密钥对传输数据进行加密,然后使用非对称加密的公钥再对秘钥进行加密,所以网络上传输的数据是被秘钥加密的密文和用公钥加密后的秘密秘钥,因此即使被黑客截取,由于没有私钥,无法获取到加密明文的秘钥,便无法获取到明文数据。

  4. 数字摘要:通过单向hash函数对原文进行哈希,将需加密的明文“摘要”成一串固定长度(如128bit)的密文,不同的明文摘要成的密文其结果总是不相同,同样的明文其摘要必定一致,并且即使知道了摘要也不能反推出明文。

  5. 数字签名技术:数字签名建立在公钥加密体制基础上,是公钥加密技术的另一类应用。它把公钥加密技术和数字摘要结合起来,形成了实用的数字签名技术。

  6. 通过加密后可以做到:
    收方能够证实发送方的真实身份;
    发送方事后不能否认所发送过的报文;
    收方或非法者不能伪造、篡改报文

  • HTTPS 加密,解密流程
HTTPS 加密,解密流程
  • HTTPS对比HTTP的区别
  1. https协议需要到CA申请证书,一般免费证书较少,因而需要一定费用。(原来网易官网是http,而网易邮箱是https。)
  2. http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
  3. http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
  4. http的连接很简单,是无状态的。Https协议是由SSL+Http协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。(无状态的意思是其数据包的发送、传输和接收都是相互独立的。无连接的意思是指通信双方都不长久的维持对方的任何信息。)
  • HTTPS的优点
  1. 使用Https协议可认证用户和服务器,确保数据发送到正确的客户机和服务器。
  2. Https协议是由SSL+Http协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全,可防止数据在传输过程中不被窃取、修改,确保数据的完整性。
  3. Https是现行架构下最安全的解决方案,虽然不是绝对安全,但它大幅增加了中间人攻击的成本。
  • HTTPS的缺点
  1. Https协议握手阶段比较费时,会使页面的加载时间延长近。
  2. Https连接缓存不如Http高效,会增加数据开销,甚至已有的安全措施也会因此而受到影响;
  3. SSL证书通常需要绑定IP,不能在同一IP上绑定多个域名,IPv4资源不可能支撑这个消耗。
  4. Https协议的加密范围也比较有限。最关键的,SSL证书的信用链体系并不安全,特别是在某些国家可以控制CA根证书的情况下,中间人攻击一样可行。
  • HTTPS SSL加密建立连接过程

如下图:


HTTPS SSL加密建立连接过程

过程详解:

  1. ①客户端的浏览器向服务器发送请求,并传送客户端SSL 协议的版本号,加密算法的种类,产生的随机数,以及其他服务器和客户端之间通讯所需要的各种信息。
  2. ②服务器向客户端传送SSL 协议的版本号,加密算法的种类,随机数以及其他相关信息,同时服务器还将向客户端传送自己的证书。
  3. ③客户端利用服务器传过来的信息验证服务器的合法性,服务器的合法性包括:证书是否过期,发行服务器证书的CA 是否可靠,发行者证书的公钥能否正确解开服务器证书的“发行者的数字签名”,服务器证书上的域名是否和服务器的实际域名相匹配。如果合法性验证没有通过,通讯将断开;如果合法性验证通过,将继续进行第四步。
  4. ④用户端随机产生一个用于通讯的“对称密码”,然后用服务器的公钥(服务器的公钥从步骤②中的服务器的证书中获得)对其加密,然后将加密后的“预主密码”传给服务器。
  5. ⑤如果服务器要求客户的身份认证(在握手过程中为可选),用户可以建立一个随机数然后对其进行数据签名,将这个含有签名的随机数和客户自己的证书以及加密过的“预主密码”一起传给服务器。
  6. ⑥如果服务器要求客户的身份认证,服务器必须检验客户证书和签名随机数的合法性,具体的合法性验证过程包括:客户的证书使用日期是否有效,为客户提供证书的CA 是否可靠,发行CA 的公钥能否正确解开客户证书的发行CA 的数字签名,检查客户的证书是否在证书废止列表(CRL)中。检验如果没有通过,通讯立刻中断;如果验证通过,服务器将用自己的私钥解开加密的“预主密码”,然后执行一系列步骤来产生主通讯密码(客户端也将通过同样的方法产生相同的主通讯密码)。
  7. ⑦服务器和客户端用相同的主密码即“通话密码”,一个对称密钥用于SSL 协议的安全数据通讯的加解密通讯。同时在SSL 通讯过程中还要完成数据通讯的完整性,防止数据通讯中的任何变化。
  8. ⑧客户端向服务器端发出信息,指明后面的数据通讯将使用的步骤. ⑦中的主密码为对称密钥,同时通知服务器客户端的握手过程结束。
  9. ⑨服务器向客户端发出信息,指明后面的数据通讯将使用的步骤⑦中的主密码为对称密钥,同时通知客户端服务器端的握手过程结束。
  10. ⑩SSL 的握手部分结束,SSL 安全通道的数据通讯开始,客户和服务器开始使用相同的对称密钥进行数据通讯,同时进行通讯完整性的检验。

2.3 HTTPS 与HTTP的区别

1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

3. HTTPS 通讯中SSL处理流程

3.1 SSL建立连接过程

SSL建立连接过程

连接过程:

  1. client向server发送请求https://baidu.com,然后连接到server的443端口,发送的信息主要是随机值1和客户端支持的加密算法。
  2. server接收到信息之后给予client响应握手信息,包括随机值2和匹配好的协商加密算法,这个加密算法一定是client发送给server加密算法的子集。
  3. 随即server给client发送第二个响应报文是数字证书。服务端必须要有一套数字证书,可以自己制作,也可以向组织申请。区别就是自己颁发的证书需要客户端验证通过,才可以继续访问,而使用受信任的公司申请的证书则不会弹出提示页面,这套证书其实就是一对公钥和私钥。传送证书,这个证书其实就是公钥,只是包含了很多信息,如证书的颁发机构,过期时间、服务端的公钥,第三方证书认证机构(CA)的签名,服务端的域名信息等内容。
  4. 客户端解析证书,这部分工作是由客户端的TLS来完成的,首先会验证公钥是否有效,比如颁发机构,过期时间等等,如果发现异常,则会弹出一个警告框,提示证书存在问题。如果证书没有问题,那么就生成一个随即值(预主秘钥)。
  5. 客户端认证证书通过之后,接下来是通过随机值1、随机值2和预主秘钥组装会话秘钥。然后通过证书的公钥加密会话秘钥。
  6. 传送加密信息,这部分传送的是用证书加密后的会话秘钥,目的就是让服务端使用秘钥解密得到随机值1、随机值2和预主秘钥。
  7. 服务端解密得到随机值1、随机值2和预主秘钥,然后组装会话秘钥,跟客户端会话秘钥相同。
  8. 客户端通过会话秘钥加密一条消息发送给服务端,主要验证服务端是否正常接受客户端加密的消息。
  9. 同样服务端也会通过会话秘钥加密一条消息回传给客户端,如果客户端能够正常接受的话表明SSL层连接建立完成了。
  • 怎么保证保证服务器给客户端下发的公钥是真正的公钥,而不是中间人伪造的公钥呢?


    认证公钥
  • 证书如何安全传输,被掉包了怎么办?


    校验CA证书

4. HTTPS 中使用的自签名证书生成,配置

  • 数字证书内容

包括了加密后服务器的公钥、权威机构的信息、服务器域名,还有经过CA私钥签名之后的证书内容(经过先通过Hash函数计算得到证书数字摘要,然后用权威机构私钥加密数字摘要得到数字签名),签名计算方法以及证书对应的域名。

  • 验证证书安全性过程
  1. 当客户端收到这个证书之后,使用本地配置的权威机构的公钥对证书进行解密得到服务端的公钥和证书的数字签名,数字签名经过CA公钥解密得到证书信息摘要。
  2. 然后证书签名的方法计算一下当前证书的信息摘要,与收到的信息摘要作对比,如果一样,表示证书一定是服务器下发的,没有被中间人篡改过。因为中间人虽然有权威机构的公钥,能够解析证书内容并篡改,但是篡改完成之后中间人需要将证书重新加密,但是中间人没有权威机构的私钥,无法加密,强行加密只会导致客户端无法解密,如果中间人强行乱修改证书,就会导致证书内容和证书签名不匹配。
  • 第三方攻击者能否让自己的证书显示出来的信息也是服务端呢?

(伪装服务端一样的配置)显然这个是不行的,因为当第三方攻击者去CA那边寻求认证的时候CA会要求其提供例如域名的whois信息、域名管理邮箱等证明你是服务端域名的拥有者,而第三方攻击者是无法提供这些信息所以他就是无法骗CA他拥有属于服务端的域名

  • 中间人攻击(MITM攻击)
  1. 中间人攻击(MITM攻击)是指,黑客拦截并篡改网络中的通信数据。又分为被动MITM和主动MITM,被动MITM只窃取通信数据而不修改,而主动MITM不但能窃取数据,还会篡改通信数据。最常见的中间人攻击常常发生在公共wifi或者公共路由上。
  2. HTTPS协议的加密范围也比较有限,在黑客攻击、拒绝服务攻击、服务器劫持等方面几乎起不到什么作用
    SSL证书的信用链体系并不安全,特别是在某些国家可以控制CA根证书的情况下,中间人攻击一样可行
  • HTTPS的一些缺点
  1. SSL证书需要购买申请,功能越强大的证书费用越高
  2. SSL证书通常需要绑定IP,不能在同一IP上绑定多个域名,IPv4资源不可能支撑这个消耗(SSL有扩展可以部分解决这个问题,但是比较麻烦,而且要求浏览器、操作系统支持,Windows XP就不支持这个扩展,考虑到XP的装机量,这个特性几乎没用)。
  3. 根据ACM CoNEXT数据显示,使用HTTPS协议会使页面的加载时间延长近50%,增加10%到20%的耗电。
  4. HTTPS连接缓存不如HTTP高效,流量成本高。
  5. HTTPS连接服务器端资源占用高很多,支持访客多的网站需要投入更大的成本。
  6. HTTPS协议握手阶段比较费时,对网站的响应速度有影响,影响用户体验。比较好的方式是采用分而治之,类似12306网站的主页使用HTTP协议,有关于用户信息等方面使用HTTPS。
  • 为啥使用自签名证书
  1. 这个主要是专业机构认证的CA证书费用很高,一般需要5千美金一年。很多公司承受不了这样的费用
  2. CA认证证书,客户端不需要做任何处理就可以访问,根HTTP一样使用,但是自签名证书需要自己实现安全认证后,信任后才能使用。
  3. 苹果自2017年后要求所有HTTP通讯必须走HTTPS方式,否则不予审核通过。
  4. 对于嵌入式设备和App的通讯使用自签名证书比较灵活一点。
  5. 自签证书相对申请CA证书,流程更简单
  6. 自签证书同样可以对数据进行加密
  7. 自签证书的有效期可以设置很长,免去续签的麻烦
  8. 自签证书更方便测试,比如说你想生成多少个不同服务器ip的都可以
  • SSL协议提供的服务主要有哪些?

1)认证用户和服务器,确保数据发送到正确的客户机和服务器
2)加密数据以防止数据中途被窃取
3)维护数据的完整性,确保数据在传输过程中不被改变

  • SSL证书的作用?
  1. SSL证书是数字证书的一种,类似于驾驶证、护照和营业执照的电子副本。
  2. SSL证书的两大作用:数据加密和身份认证
  3. SSL 证书遵守 SSL协议,通过在客户端浏览器和Web服务器之间建立一条SSL安全通道
  4. 一个有效、可信的 SSL 数字证书包括一个公共密钥和一个私用密钥。公共密钥用于加密信息,私用密钥用于解译加密的信息。因此,浏览器指向一个安全域时,SSL 将同步确认服务器和客户端,并创建一种加密方式和一个唯一的会话密钥。它们可以启动一个保证消息的隐私性和完整性的安全会话。
  • 证书相关概念
  1. 证书分为2类:自签名证书和CA证书。一般自签名证书不能用来进行身份认证,如果一个server端使用自签名证书,client端要么被设置为无条件信任任何证书,要么需要将自签名证书的公钥和私钥加入受信任列表。但这样一来就增加了server的私钥泄露风险
  2. TLS是传输层安全协议(Transport Layer Security)的缩写,是一种对基于网络的传输的加密协议,可以在受信任的第三方公证基础上做双方的身份认证。TLS可以用在TCP上,也可以用在无连接的UDP报文上。协议规定了身份认证、算法协商、密钥交换等的实现。
  3. HTTPS是在基于TLS/SSL的安全套接字上的的应用层协议,除了传输层进行了加密外,其它与常规HTTP协议基本保持一致
  4. 证书是TLS协议中用来对身份进行验证的机制,是一种数字签名形式的文件,包含证书拥有者的公钥及第三方的证书信息。
  5. TLS基于CA的身份认证基本原理是:首先验证方需要信任CA提供方自己的证书(CAcert),比如证书在操作系统的受信任证书列表中,或者用户通过“安装根证书”等方式将 CA的公钥和私钥加入受信任列表;然后CA对被验证方的原始证书进行签名(私钥加密),生成最终的证书;验证方得到最终的证书后,利用CAcert中包含的公钥进行解密,得到被验证方的原始证书。
  6. 根据RSA的加密原理,如果用CA的公钥解密成功,说明该证书的确是用CA的私钥加密的,可以认为被验证方是可信的。
  7. 自签名的证书无法被吊销,CA签名的证书可以被吊销 能不能吊销证书的区别在于,如果你的私钥被黑客获取,如果证书不能被吊销,则黑客可以伪装成你与用户进行通信
  8. 如果你的规划需要创建多个证书,那么使用私有CA的方法比较合适,因为只要给所有的客户端都安装了CA的证书,那么以该证书签名过的证书,客户端都是信任的,也就是安装一次就够了
  9. 如果你直接用自签名证书,你需要给所有的客户端安装该证书才会被信任,如果你需要第二个证书,则还的挨个给所有的客户端安装证书2才会被信任。
  • 证书类型:

x509的证书编码格式有两种:

  1. PEM(Privacy-enhanced Electronic Mail) 是明文格式的 以 -----BEGIN CERTIFICATE-----开头,已-----END CERTIFICATE-----结尾,中间是经过base64编码的内容,apache需要的证书就是这类编码的证书 查看这类证书的信息的命令为 :openssl x509 -noout -text -in server.pem
    其实PEM就是把DER的内容进行了一次base64编码
  2. DER 是二进制格式的证书 查看这类证书的信息的命令为 :openssl x509 -noout -text -inform der -in server.der
  • 证书扩展名:
  1. .crt 证书文件 ,可以是DER(二进制)编码的,也可以是PEM( ASCII (Base64) )编码的 ,在类unix系统中比较常见
  2. .cer 也是证书 常见于Windows系统 编码类型同样可以是DER或者PEM的,windows 下有工具可以转换crt到cer
  3. .csr 证书签名请求 一般是生成请求以后发送给CA,然后CA会给你签名并发回证书
  4. .key 一般公钥或者密钥都会用这种扩展名,可以是DER编码的或者是PEM编码的 查看DER编码的(公钥或者密钥)的文件的命令为 openssl rsa -inform DER -noout -text -in xxx.key 查看PEM编码的(公钥或者密钥)的文件的命令为 openssl rsa -inform PEM -noout -text -in xxx.key
  5. .p12 证书 包含一个X509证书和一个被密码保护的私钥
  • 关于CA 签名SSL证书

相对自签证书的自己给自己颁证,由权威的证书授权机构(Certificate Authority)颁发的签名证书,我们称之为:CA证书
CA证书保证书持有者的身份和公钥的拥有权
浏览器对CA证书是信任的

4.1 CA证书申请

  • 有很多网站能申请CA证书,如沃通,腾讯,阿里云等,大家可根据自己的喜好去申请
    CA证书又分免费和不同价钱的,当然一分钱一分货,对于我们个人开发者来说,免费的CA证书就足够了

4.1.1 从阿里云申请CA证书

  • 1:找到阿里云的CA证书
    打开阿里云主页:https://www.aliyun.com/,选“产品”再选“CA证书”如下图:
    找到阿里云的CA证书
  • 2:选择:立即购买
  • 3:选择免费型DV SSL(最主要是不用钱呵 :p),点立即购买,如下图:


    选择免费型DV SSL
  • 4:点“确认订单”->”去支付“
  • 5:点”证书控制台“
  • 6:点如下图的”补全“,然后填写个人资料


    点如下图的”补全“
  • 7:信息补全后点提交,提交后大概一小时左右就能收到证书申请成功的信息,接下来就下载证书:
    去到:”管理控制台“->”产品与服务”->”我的证书“,选择”下载”


    信息补全后点提交
  • 8:阿里云能根据不同的服务器生成不同的证书,如tomcat,apache等,选择自己所要的,如下图:


    阿里云能根据不同的服务器生成不同的证书

4.1.2

4.1.3

4.2 window生成自签名证书

keytool -genkey -alias tomcat  -storetype PKCS12 -keyalg RSA -keysize 2048  -keystore d:\mykeystore\keystore.p12 -validity 3650  -ext san=ip:192.168.100.132 -dname "CN=garyyan, OU=mycompany, O=mycompany, L=gd, ST=gd, C=china"

此命令中间只需要输入密码,就能生成keystore,假设密码是:123456
其中:
1)keystore可理解为一个数据库,可以存很多个组数据。
每组数据主要包含下面两种数据:
a:密钥实体(Key entity)——密钥(secret key)又或者是私钥和配对公钥(采用非对称加密)
b:可信任的证书实体(trusted certificate entries)——只包含公钥
2)-keystore d:\mykeystore\keystore.p12,指定在d:\mykeystore(先要手动创建此文件夹),生成keystore:keystore.p12
3)-alias tomcat,为其指明在keystore中的唯一的别名:tomcat ,因为keystore中可能还存有其它的别名,如:tomcat 2
4)-storetype PKCS12指明密钥仓库类型是PKCS12
5)-keyalg RSA,指定加密算法,本例中的采用通用的RAS加密算法
6)-keysize 2048指定密钥的长度为2048
7)-validity 3650 指定证书的有效期为3650天
8)-ext san=ip:192.168.100.132请根据你的服务器的IP地址设置,如果不进行设置,客户端在访问的时候可能会报错
9)-dname “CN=garyyan, OU=mycompany,O=mycompany,L=gd, ST=gd, C=china”
其中:”CN=(名字与姓氏), OU=(组织单位名称), O=(组织名称), L=(城市或区域名称), ST=(州或省份名称), C=(单位的两字母国家代码)”,我在测试的过程中发现随便填就行

  • 4:导出公钥证书(主要用于客户端):
    运行命令:
keytool -export -keystore d:\mykeystore\keystore.p12 -alias tomcat -file mycer.cer -storepass 123456

其中:
1)-keystore d:\mykeystore\keystore.p12 是指上面的keystore文件
2)-alias tomcat是指定别名为tomcat的那一组
3)-file mycer.cer指定在当前目录生成名为mycer.cer的证书
4)-storepass 123456是上面生成keystore 所用的密码

4.3 mac生成自签名证书

4.3.1 mac下通过keytool生成自签名证书

  • 首先需要知道jdk安装目录,在terminal下输入: /usr/libexec/java_home -V

    查看jdk版本

  • 2.跳转到jdk目录下


    跳转到jdk目录下
  • 输入keytool查看


    输入keytool查看
  • 3.创建签名:终端输入,keytool -genkey -v -keystore myKey.jks -keyalg RSA -keysize 2048 -validity 10000 -alias myAlias 由于需要权限,执行命令时需要在前面加上sudo

    执行1

  • 按提示输入相关信息,这里会提示要输入密码,我使用的都是相同的密码123456


    按提示输入相关信息
  • 如果前面执行keytool -genkey -v -keystore myKey.jks -keyalg RSA -keysize 2048 -validity 10000 -alias myAlias命令时,没有输入sudo,则会报下面的错误:

    没有输入sudo,则会报下面的错误

  • 输入sudo和密码后执行结果如下:


    输入sudo和密码后执行结果如下
  • 查看keystore的指纹数据:keytool -v -list -keystore myKey.jks -alias myAlias -keypass 123456 -storepass 123456

    在这里插入图片描述

  • 导出证书certificate.pem: sudo keytool -export -rfc -alias myAlias -file upload_certificate.pem -keystore myKey.jks, 这里同样有权限问题,需要使用sudo

    导出证书certificate.pem

4.3.2 mac下通过openssl生成自签名证书

4.4 nginx服务器配置自签名证书

4.5 tomcat服务器配置自签名证书

4.6 appache服务器配置自签名证书

5. HTTPS 自己实现IOS端证书验证

6. Alamofire实现SSL安全认证 源码解析

7. HTTPS实现文件上传

8. HTTPS实现文件下载

9. HTTPS文件传输通过moya+alamofire+rxswift实现

参考文献:

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

推荐阅读更多精彩内容