安全通信
应用层协议大多数自己都没有实现加解密功能,比如http等。http就是直接把数据加载进来然后做简单编码(也就是流式化)然后响应客户端,然后数据在浏览器展示,这个数据在传输过程是明文的,你截获就可以直接查看。这显然不安全,要想做到安全那么就只能在发送端加密然后接收端还能把信息还原成原来的内容,这就是加密解密。其实这个事情很好理解,加密解密在古代就有也不是现代的概念。在安全通信里面我经常听到的2个东西就是SSL和TLS,这2个有什么区别呢?以及HTTPS是怎么通信的?
SSL和TLS
SSL
SSL是网景公司研发的一种功能模块这种模块或者叫库就在应用层和传输层之间又加了一层,而且不是一整层只是半层,这要做的好处是你调用这个库就可以使用这个功能如果不调用则还是按照之前的方式来使用,这个半层的库就是SSL,叫做安全套接字层。不过SSL只是一种规范和协议并不是具体实现。OpenSSL则是对SSL的实现而且众多实现中的一种。什么叫做一种实现呢?比如httpd和Nginx这两个应用都是http协议的实现或者说x86是一种标准那么DELL和HP都可以基于这种标准来设计自己的电脑。
如果你自己本身不具备开发这种加密解密的功能,那么你就可以使用SSL完成这个工作,不过无论你自己开发还是使用SSL,这些加密解密协议都需要具备2个基本功能:
加密和解密
秘钥分发
在Web应用方面,http调用了ssl就变成了https,虽然就是调用了ssl,但是http和https这两个在机制上有巨大的区别。
TLS
叫做传输层安全,为什么有了SSL还会有一个和SSL类似的TLS呢?因为SSL协议是发明者是网景公司,它拥有版权,而且互联网公司很多都需要使用安全通信,所以国际标准化机构就参照SSL弄了一个TLS协议。两者兼容。TLS由IETF在1999年发布。那么由于SSL的3个版本有漏洞所有很多不用了。现在主流都使用TLS。总之你可以把TLS当做SSL使用其原理都是一样的。
早期使用SSL较多后来大部分使用TLS,虽然我们平时会说ssl它可能指的是SSL也可以是TLS,所以你不用纠结它指的是什么你只要理解为一种安全通信机制就可以。所以我们后面会不加区分的统称SSL。
为什么现在都是全站启用SSL呢
之前很多网站都不是https,也只有某些特殊阶段才使用,比如支付阶段、登录验证阶段。那么后来为什么大家包括百度搜索首页这种都变成https了呢?
如果使用SSL加密那么服务端就需要解密,加解密其实非常消耗CPU资源,早期处于性能考虑不会大面积使用SSL,比如只是普通的浏览网页没必要进行安全通信。但是后来服务器性能越来越强而且大规模使用x86服务器以及有些网卡也支持SSL卸载,所以全站使用SSL也成为可能。
不过由于http是明文传输的,用户访问什么网页其实可以通过流量拦截,数据可以被修改,有些不法行为就可以利用这一点来实现广告投放,但是使用https通信则不会出现这种情况。
安全通信到底怎么安全呢?
基本概念
为了解决安全问题实现安全目标比如保密性、完整性、可用性等其解决方案有两类,就是技术和服务,技术指的是加密和解密;服务指的是认证机制和访问控制机制。
针对加密和解密以及实现服务功能的所用到的加密算法和协议就有如下几种:对称加密、非对称加密、单向加密和认证协议。
在Linux上实现上述算法和协议的工具有OpenSSL(ssl协议的实现)和GPG(pgp协议的实现)。
数字加密算法
先说一下算法,虽然加密用秘钥但是如何用这个秘钥来对数据加密或者解密这个是算法来决定的,但是通常算法是公开的比如RSA(个别安全公司有自己的独有的算法除外)所以是否安全不取决于算法而是秘钥。
什么叫密码,什么叫秘钥?我上面那段话肯定会有人迷糊,密码是编码和解码的规则你可以理解为加密和解密规则而秘钥则是改变密码行为的外在参数,也就是说相同的规则当你使用不同的秘钥加密相同内容时加密后的结果也是不同的。比如字母移位这就是密码,而移动N位的这个N则是秘钥。假设移动3位,那么ABC则变成DEF。所以这就是为什么上面那段话的结论是不取决于算法(密码)而是秘钥。
对称秘钥加密:对称加密算法有很多,但是无论什么算法加密解密都使用相同的秘钥,好处是简单计算量小也就是不会对CPU产生太大压力,缺点以及密码过多(你要和很多人通信就需要很多人的密码)、密码分发困难。因为你要让对方可以解密你就需要把密码先给对方,这个给密码的过程要不要加密呢?这就变成了先有鸡还是先有蛋的事情永远扯不清。常用算法DES(目前已经弃用)、AES等。
非对称秘钥加密:有公钥和私钥,使用公钥加密私钥解密。如果A和B通信每个人都有一对儿公钥和私钥,彼此还都有对方的公钥。A发信息给B则使用B的公钥加密,然后B收到后使用自己的私钥解密,然后B回复信息给A则使用A的公钥加密,A收到后使用自己的私钥解密。这个过程看似完美,可是由于公钥大家都能获取,B怎么知道信息是来自A而不是来自其他冒名A的人呢?这就是数字签名,这个后面再说。非对称加密主要用于数字签名和秘钥交换。加密数据虽然它也能做但是通常不这么用,你看后面的HTTPS通信过程就知道了。非对称加密主要用来做对称秘钥的交换,完成交换后大家其实就是使用对称密码来做数据加密和解密。常用算法RSA(加解密和签名)、DSA(仅能签名)等。
单向加密**:不可逆的,只能加密不能解密,比如MD5、SHA1等。其实主要用来提取数据摘要或者说指纹,所以并不能算作我们常规意义上的加密。它的特点是数据微小变化都会导致指纹的不同,所以主要用来做数据完整性验证。
特别注意:并不是说只能用公钥加密私钥解密,非对称加密方式的目的就是使用一种秘钥加密的数据必须能使用另外一种秘钥解密,所以无论是公钥还是私钥都可以加密,我们之所以说用公钥加密用私钥解密是首先是因为公钥是从私钥中提取出来的,其次公钥是公开的如果你用私钥加密那么任何拥有该私钥对应公钥的人都可以解密 。所以才有公钥发给其他人私钥自己留存且用公钥加密私钥解密,因为只有这样才能发挥这种非对称加密或者说公钥加密的主要作用也就是密码交换和数字签名。不过使用私钥加密数据可以证明一件事情就是如果用该私钥的公钥可以解密那就证明这个数据一定是私钥持有人发来的。
秘钥交换(IKE,互联网秘钥交换):IKE实现的方式有公钥加密和DH算法。这种标准主要用来做秘钥交换。不过对于秘钥交换来说更加安全的其实是DH算法,虽然常用的是公钥加密方式。
公钥加密最大的问题是密码是要从一方传送到一方,虽然使用对方的公钥加密了。但是DH的好处是双方不用发密码而且双方还可以得到密码。
数字签名
数字签名的主要作用是为了检查数据是否被篡改以及数据是谁发的(就是向对方证明此时他收到的消息是来自消息宣称的人本人而不是冒名顶替的第三人,当然真正验证对方身份的还不能仅仅靠数字签名这个后面再说)。
按照上面的例子A发消息给B,A使用B的公钥加密,B通过自己的私钥解密,可是公钥大家都可以获得那么B怎么知道这个消息是来自A呢?难道仅仅凭借消息内容说我是A就断定消息来自A么?另外B怎么确定信息在传递过程中没有被篡改呢?
为了解决这个问题A对消息做哈希摘要(MD5算法或者SHA1算法等)然后用A自己的私钥对摘要进行加密(数字签名),然后把加密后的摘要和消息本身一起使用B的公钥加密发送给B,这样B收到消息后用自己的私钥可以解密,然后用A的公钥可以解密签名(上面提到的摘要)如果成功就说明消息来自A。这就是签名和验证签名过程。消息来源确认了,此时就会担心消息传递过程中如果被篡改怎么办?这就是那段被加密的摘要的作用,如果B使用相同的算法来计算信息的摘要,如果B计算得到的值和解密签名后的得到的值一致说明信息没有被篡改。
上面的过程的确看起来很好但是有一个漏洞就是所有的公钥都要自己保存和管理万一被别人替换了呢,或者说A和B从未通信过现在要做第一次通信如何把公钥给对方呢?难道就不怕有人冒充A把自己的公钥给了B那么反过来也是一样,另外A对信息做哈希摘要的时候用的什么算法,万一B没有这个算法怎么办呢,所以这就引出了CA和证书的概念。
为什么需要CA、证书以及CA根证书
公钥加密私钥解密,私钥肯定是自己保存其实也就保存一份,如果我需要给很多人通信难道我要保存很多人的公钥么?如果这个数量达到一个量级在管理上也很困难另外就是如何防止公钥被篡改呢也就是如何验证这个公钥是不是我要通信的那个人的公钥而不是别人发来冒充他的。如果要是有一个第三方机构来统一管理就好了,这就是CA证书管理机构。CA的作用就是对申请人的公钥进行认证和加密,加密后的公钥就是数字证书,又称CA证书,证书中包含了很多重要信息比如申请人、用途、申请人支持的加密算法、申请人使用hash算法,证书有效期等其中最重要的就是证书申请人的公钥。当然前提是通信双方都要信任同一个CA机构。
那有了CA之后该怎么做呢?申请者需要自己生成自己的公钥和私钥,然后把公钥和其他申请信息发给证书颁发机构,证书颁发机构自己也有公钥和私钥,然后证书颁发机构提取申请者的公钥和其他信息的特征码,然后CA使用自己的私钥加密这个特征码也就是签名, 这样形成一个由CA签名的证书然后发送给申请者 。此时还是A和B通信且为第一次通信并且双方都信任同一CA颁发机构 。
- A把自己支持的对称加密算法、单向加密算法、公钥加密算法以及自己的证书发给B
- B拿到信息后需要对A的证书解密(这样才能获取A的公钥),但证书是CA用私钥加密的需要用CA的公钥解密这怎么办?这就是CA根证书(包含CA的公钥)的作用,B需要下载CA根证书并且安装到自己的电脑里通常只需要安装一次(通常现在操作系统 MacOX、Windows都已经内置了国际著名CA的根证书,因为全球著名CA都是有限的,Linux没有内置)。有了这个根证书也就有了CA的公钥,这样就可以解密A的证书来获取A的公钥。能解密成功说明A的证书是CA颁发的。然后使用证书中的单向加密算法来计算证书的特征码如果和解密签名得到的特征码一致则表明内容没有被篡改,所以这一步完成了对A的身份验证和证书信息的完整性验证。
- 验证完之后还要看该证书的有效期以及证书主体名称和该证书是否被吊销
- 验证都通过之后B选择自己支持的对称加密算法、单向加密算法、公钥加密算法以及自己的证书发送给A
- A采用相同的步骤来验证B,如果验证通过,这时候大家就确定了使用哪种对称加密算法、单向加密算法、公钥加密算法并且完成了双方的公钥交换以及身份验证
- A使用对称加密算法生成一个密码,然后对密码计算摘要,然后用自己的私钥对摘要进行签名,之后用B的公钥进行加密发送给B
- B收到信息后使用自己的私钥解密,然后使用A的公钥解密签名得到摘要,如果解密成功则说明是A发来的,然后对密码提取摘要并和解密签名得到的摘要对比,如果一直则说明信息完整,这时候就完成了密码交换,从此双发就可以使用对称加密方式来通信。
上述这个过程就是大家通过CA来间接获取通信双方公钥的过程。如果B使用CA的根证书无法解密A发来的证书说明就说明A的证书不是该CA签发的,由于世界上公认的CA也就那么几个所以如果B用这些CA的根证书都解密不了A的证书,那么就会提示风险,很有可能A这个证书就是一个自签名的证书。
这里我们只是阐明概念,在实际应用中上述过程还有些不同,比如上面A发给B的包括签名和证书以及信息,难道信息不加密么?如果加密用什么加密等。这些东西都会在具体应用场景中再说明。
既然明白了CA那么我们就说一下PKI。
PKI
公钥基础设施,以CA为中心所生成的一套体系就是PKI。它有四个重要组件:
签证机构,就是CA,这个是负责生成证书的
注册机构,就是RA,这个是负责接收证书申请的
证书吊销列表,就是CRL,这个是标记哪些证书已经不可用或者不可信了,相当于证书黑名单,这里的证书都不能被信任了。
证书存取库,就是CB,这里存放的是CA发的证书可以供其他人下载和使用
证书常用字段和格式
首先要说一下X.509,它就是证书标准,也叫做证书格式。所有证书都要符合这个标准,它定义了证书中包含那些内容。比如版本号、序列号(CA发的第几个证书)、签名算法ID(这个证书用什么算法做的签名)、CA颁发机构名称、证书有效期、申请者名称、申请者公钥、CA颁发机构的唯一标识、申请者的唯一标识、CA对该证书的签名(用CA自己的私钥对所有信息的摘要做签名)等扩展信息。下面就看看证书中常见的字段:
字段 | 含义 |
---|---|
Common Name | 简称CN,对于SSL证书来说一般为网站域名;而对于代码签名证书则为申请单位名称;而对于客户端证书来说则是申请者名称也可以当做用户账号名称 |
Organization Name | 简称O,对于SSL证书来说一般为网站域名;而对于代码签名证书则为申请单位名称;而对于客户端证书来说则是申请者名称也可当做用户账号所属的组 |
Locality | 简称L,表示城市 |
State/Provice | 简称ST,表示省份 |
Country | 简称C,表示国家,比如中国为CN,美国为US |
浏览器使用HTTPS访问时它是怎么检查证书和地址栏中的名称呢?有下面三种方式
- 主机名(地址栏中的域名)与证书Subject中的CN字段完全匹配
- 主机名称与通配符通用名称相匹配,比如www.abc.com匹配通用名称*.abc.co
- 主机名在主题备用名称(Subject Alternative Name检查sans)字段中列出
我们知道了X.509是证书格式,可是很多时候看到的.pem、.key都什么呢?,这里就要做一些区分,证书编码格式和证书扩展名。
证书编码格式
所有的证书都是通过X.509标准生成的证书,但是有2中编码格式:
PEM(Privacy Enhanced Mail),它是基于X.509标准生成的,文件可读可以直接打开查看,它是以"-----BEGIN CERTIFICATE-----" 和 "-----END CERTIFICATE-----"开头和结尾且用Base64编码的证书,这种编码格式的证书通常是.pem扩展名。
使用命令
openssl x509 -in XXX.pem -text -noout
来查看证书内容。DER(Distinguished Encoding Rules),二进制格式的,无法直接读取。
使用命令 openssl x509 -in XXX.der -inform der -noout 来查看证书内容。
证书扩展名
虽然证书编码格式有2种,但是扩展名有很多:
扩展名 | 说明 |
---|---|
.der | 用于DER编码的证书 |
.pem | 它是基于X.509标准生成的,它是以"-----BEGIN CERTIFICATE-----" 和 "-----END CERTIFICATE-----"开头和结尾且用Base64编码的证书 |
.crt | 这种扩展名的证书可以是DER编码也可以是PEM编码,在Unix系统中常见。 |
.cer | 微软系统常见,在微软系统中可以将.crt转换为.cer。同样可以是DER编码也可以是PEM编码。 |
.key | 用于存储公钥和私钥,同样可以使用DER或者PEM编码。 |
.csr | 这个不是证书文件,而是证书签名请求文件,向CA申请获得签名证书时需要提供的申请文件。 |
HTTPS通信过程
双向认证
上图为SSL的双向验证过程
- 浏览器发起https请求,生成随机数(用于稍后生成会话秘钥),并发送client_hello信息里面将自己支持的加密协议版本、加密算法、压缩算法和生成的随机数发送给服务端。
- 服务端收到信息以后,也生成随机数(用于稍后生成会话秘钥),并发送server_hello信确认自己客户端发送来的协议版本、算法等。如果不支持那么就无法通信了。
- 服务端发送服务器证书给客户端,并且要求客户端也发送证书过来
- 客户端检查服务端证书合法性和有效性,通过以后,发送客户端证书给服务端
- 服务器检查客户端的证书合法
- 客户端把所有之前收到的信息做HASH,然后使用自己私钥做签名,发送给服务器端
- 服务端检查哈希值和签名
- 客户端使用对称加密算法生成一个加密密码,然后使用客户端公钥进行加密发送给服务端
- 服务端收到以后就可以使用对称加密密码和客户端通信
- 断开连接
单向认证
HTTPS访问通常使用单向认证,比如你访问百度、淘宝、京东等虽然是https通信但是它们不会去验证客户端证书,客户端只会去验证服务端证书。为什么呢?因为证书花钱啊,哪个网站要是需要做客户端证书认证那么估计就没有人去访问了,再有对于服务端来说它不在乎你是谁来的人越多越好。当然也有通信双方要求做双向认证的比如登录网银的U盾就是客户端证书。
- 浏览器发起https请求,生成随机数将自己支持的一套加密规则、协议版本发送给服务端。
- 服务端从中选出一组加密算法与HASH算法,服务端也生成一个随机数并将自己的身份信息以证书的形式发回给浏览器。证书里面包含了服务端地址,加密公钥,以及证书的颁发机构等信息。
- 客户端获得服务端证书之后浏览器要做以下工作:(验证我访问的www.abc.com是不是真正的那个www.abc.com,这个验证过程就通过服务端证书和自己的CA根证书来完成)
验证证书的合法性和有效性(使用客户端保存的CA根证书的公钥解密服务端发来的证书的签名,解密成功说明服务端证书可以信任,然后使用HASH算法计算证书摘要然后对比解密签名后得到的摘要,如果一致则说明内容没有篡改,然后 检查证书中的其他信息,可以检查证书中的域名和我访问的域名是否一致 (这里防止钓鱼网站)、检查是否过期、检查是否被吊销等,如果检查都通过则证书受信任,则浏览器栏里面会显示一个小锁头,否则会给出证书不受信的提示。
如果证书受信任,或者是用户接受了不受信的证书,浏览器会生成一串随机数,然后把随机数、编码变更通知(表示随后的通信都使用双方商定的协议版本以及加密算法)和客户端握手结束通知发送给服务器
- 服务端接收浏览器发来的数据之后要做以下的操作:
这时候服务端会有3个随机数,2个是客户端发来的,一个是服务端自己产生的,用着三个随机数并结合对称加密算法生成“会话密码”
生成编码变更通知、服务端握手结束通知
使用商定好的HASH算法对会话密码、编码变更通知、握手结束通知计算摘要,然后使用自己的私钥进行对摘要进行签名,最后使用客户端公钥加密信息并发送给客户端
- 浏览器解密,然后得到消息的HASH,如果与服务端发来的HASH一致,此时握手过程结束,之后所有的通信数据都使用得到的“会话密码” 加密。
这里为什么在认证通过后的数据传输使用认证过程产生的随机密码呢?这是因为使用对称加密解密性能要比非对称的高,尤其是对服务端的CPU压力比较小,但是如果使用一个长期固定的对称密码就很不安全而随机生成的就会有一个如何告诉通信双方且传递过程中不被窃取的问题所以就使用非对称加密方式这样不但保证了随机密码的安全同时也可以验证服务端身份的真实性。
为什么会产生这么多随机数呢?为了避免计算机上产生的随机数不是真的随机数,所以双方都产生随机数然后使用大家的随机数来生成密码。