一直对Https的原理只知其一;不知其二,只知道Https比Http协议更安全,可是为何更安全呢?以及怎么具体去部署一个Https呢?都是迷糊状态,经过参考以下文章,对Https有一个入门理解(参考文章以及我的的看法)。javascript
参考资源:html
首先放一张图解HTTPS基本原理文章中的图(加了我的的理解注释):java
咱们后面会针对这个图进行一步步的分析。node
下面咱们针对如下两个主题来理解Https:git
咱们须要理解https, 那咱们若是只是针对原理来理解,相信不少环节都串联不起来,看完后最终仍是一头雾水。 因此咱们须要先实践下Https究竟是个什么东西, 到底该怎么部署一个https服务。 参考文章: HTTPS证书生成原理和部署细节算法
正常的咱们要运行一个Https, 须要向一个CA数字证书认证中心去申请一个咱们的CA证书。可是咱们只是想测试一下Https, 并不可能真的去一个合法的CA机构去申请一个证书,因此咱们可使用自签名 来构建本身的CA机构, 也就是一个能够给本身的服务器颁发证书的机构。shell
下面咱们来看怎么生成一个本身的CA机构。浏览器
openssl genrsa -out ca.key 1024
复制代码
上面的ca.key
就是CA机构的私钥文件 关于openssl
命令的使用方式能够参考:openssl命令目录安全
# a.根据私钥ca.key生成一个新的证书请求文件。其中"-new"表示新生成一个新的证书请求文件,
# "-key"指定私钥文件,"-out"指定输出文件,此处输出文件即为证书请求文件。
openssl req -new -key ca.key -out ca.csr
# x509 表示自签署证书,可用于自建根CA时。
openssl x509 -req -in ca.csr -signkey ca.key -out ca.crt
# 上面两个步骤能够合并为: openssl req -x509 -key ca.key -in ca.csr -out ca.crt -days 365
复制代码
在上面第二步的时候,会有以下的指引:bash
➜ keys openssl req -new -key ca.key -out ca.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:Zhejiang
Locality Name (eg, city) []:Hangzhou
Organization Name (eg, company) [Internet Widgits Pty Ltd]:My CA
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:localhost
Email Address []:
复制代码
注意:咱们的服务器在向CA申请证书的时候,也会有如上的指引提示, Organization Name (eg, company) [Internet Widgits Pty Ltd]
的值不能同样, 咱们能够给CA机构设置值为My CA
, 咱们服务器在申请证书的时候设置的值为My CA Server
.
经过上面两个步骤,咱们已经建立了自签名机构。
咱们已经有了CA机构,如今咱们须要给咱们的服务向CA机构申请一个CA证书
咱们在给咱们的服务申请CA证书的时候, 咱们首先须要给咱们本身的服务建立两个钥匙:公钥和私钥。
生成步骤以下:
openssl genrsa -out server.key 1024
复制代码
openssl rsa -in server.key -pubout -out server.pem
复制代码
上面生成了两个文件: server.key
和 server.pem
, 咱们在生成公钥的时候,是根据私钥生成的。
咱们已经生成了公钥和私钥了,咱们如今须要向CA机构申请证书了, 能够经过以下的脚本生成:
# 服务器端须要向 CA 机构申请签名证书,在申请签名证书以前依然是建立本身的 CSR 文件
openssl req -new -key server.key -out server.csr
# 向本身的 CA 机构申请证书,签名过程须要 CA 的证书和私钥参与,最终颁发一个带有 CA 签名的证书
openssl x509 -req -CA ca.crt -CAkey ca.key -CAcreateserial -in server.csr -out server.crt
复制代码
上面的server.crt
就是咱们服务向CA申请的证书。
咱们能够分析上面的脚本得:
总结:
在上面咱们已经准备好了自前面CA机构 ,已经咱们服务生成了公钥,私钥,以及向CA申请到了证书了,下面咱们就来看搭建Https服务了。
咱们经过Nodejs 来搭建一个https服务,其脚本以下(咱们须要将上面生成的文件都放在咱们项目的keys
目录下):
// file http-server.js
var https = require('https');
var fs = require('fs');
var options = {
key: fs.readFileSync('./keys/server.key'),// 服务器端私钥
cert: fs.readFileSync('./keys/server.crt')
};
https.createServer(options, function(req, res) {
res.writeHead(200);
res.end('hello world 8000');
}).listen(8000, () => {
console.log(`服务器启动:localhost:8000`)
});
复制代码
接下来咱们能够经过node http-server.js
来启动咱们的服务了,咱们能够在浏览器打开https://localhost:8000/
咱们能够看到以下的效果:
看到上面的效果表面咱们的https服务搭建成功了,之因此浏览器显示不安全,是由于浏览器只承认其内置的CA证书, 但这不影响咱们学习。
咱们点击上面的证书,而后查看Certification Path
选项:
发现根证书不被信任,那咱们怎么可让浏览器信任咱们的自签名CA呢, 我么能够按照以下操做:
ca.crt
文件,进行安装。安装完成后,咱们能够再次查看咱们的证书:发现咱们的证书已经被浏览器信任了.
上面咱们已经简单搭建了一个自签名的https服务,咱们已经已经知道一些基本名词: 私钥,公钥,CA,申请CA证书。下面咱们就根据上面咱们贴的图片来解释https运行的基本原理。
①:通常【客户端】首先发起请求,例如请求网站https://www.thinktxt.com/, 生成一个随机数(RandomC),携带支持的TLS版本、加密算法等信息发送至【服务端】
客户端经过本地浏览器或操做系统内置的权威第三方认证机构的CA证书进行验证,一个证书包含域名、证书编号、公钥、有效期等信息(这里是指浏览器内置的CA机构,已经保存了咱们服务的证书的相关信息).
证书编号是在服务器管理员经过第三方证书机构申请证书的时候,第三方机构用他们的私钥对证书编号进行加密存入证书,
上面一句话能够根据咱们上面申请CA证书的里的步骤进行理解:
# 服务器端须要向 CA 机构申请签名证书,在申请签名证书以前依然是建立本身的 CSR 文件
openssl req -new -key server.key -out server.csr
# 向本身的 CA 机构申请证书,签名过程须要 CA 的证书和私钥参与,最终颁发一个带有 CA 签名的证书
openssl x509 -req -CA ca.crt -CAkey ca.key -CAcreateserial -in server.csr -out server.crt
复制代码
证书编号: 能够理解我上面脚本生成的server.crt
的编号。 在上面的脚本中,用到的ca.key文件, 这个文件就是CA的私钥
根据编号生成方法生成证书编号(证书自己携带了生成证书编号的方法),
与CA证书公钥解密得出的证书编号进行对比,验证不经过或者证书过时等状况就提示存在风险(浏览器的红色警告),验证经过则进行下一步。
这里是指咱们浏览器内置的CA保存咱们服务的CA证书的编号,可是经过
ca.key
私钥加密过的, 咱们在验证证书编号是否合法的时候,咱们再经过浏览器内置的CA的公钥 对CA里面保存的编号进行解密,用解密后的证书编号和服务器返回来的证书编号进行匹配。
④:【客户端】生成一个随机数(PreMaster Key),此时已经有第三个随机数了,根据三个随机数(RandomC、RandomS、PreMaster Key)按照双方约定的算法生成用于后面会话的同一把的“会话密钥”。
可是这个时候生成的“会话密钥” 知识在客户端生成保存起来,服务器这个时候仍是没有第三个随机数PreMaster Key, 服务器要想生成相同的“会话密钥”, 则必须同时也知晓一样的三个随机数,因此如今的问题就是怎么将第三个随机数PreMaster Key传给服务器。
⑤:【客户端】将随机数(PreMaster Key)经过公钥加密后发送至【服务端】(这个步骤只是传递了加密后的随机数PreMaster Key, 会话密钥并无发送到服务端,由于须要服务端根据只有本身知道的私钥去解密PreMaster Key, 而后生成对应的会话密钥 )。
- 在这里的公钥 就是咱们服务端申请的CA证书里面的内置的公钥,也就是咱们服务器在申请CA证书时候生成的公钥。经过公钥加密的数据,只有对应的私钥才能解密, 而这个私钥只有咱们的服务端才知道,即便是CA机构都不知道的
- 咱们在①② 中传递的随机数RandomC、RandomS 都是明文传递的,这里为何须要加密呢?由于咱们已经明文传递了两个随机数,若是第三个随机数也明文传递, 那都是能够被第三方拦截处理的。
此时在服务端生成的会话密钥 和④ 中浏览器生成的会话密钥是同样的啦。
⑦:【服务端】计算此前全部内容的握手消息hash值,并用“会话密钥”加密后发送至客户端用于验证。
这个会话密钥是浏览器和服务端根据三个随机数生成的,并无经过网络传递,第三方是获取不到这个值的,而后经过会话密钥加密数据传递给客户端(其实这个时候能够算是对称加密了,由于浏览器和客户端都已经知道密钥了)
在⑦计算全部的内容的握手消息的hash 值, 并用【会话密钥】加密后发送给客户端。
⑧:【客户端】解密并计算握手消息的hash值,若是与服务端发来的hash一致,此时握手过程结束。
- 客户端根据本身在④生成的会话密钥解密收到的消息
- 客户端从新计算全部握手消息的hash值
- 将解密后的hash 和本身计算的hash 进行判断,若是同样,则算是真正的验证经过了,能够进行正常通讯了
⑨:验证经过后,开始正常的加密通讯。
开始正常的进行通讯了,通讯内容会用会话密钥和以前协商好的加密算法进行加密了。
总结: