接理解加密算法(一)——加密算法分类、理解加密算法(二)——TLS/SSLjavascript
普通的TCP通讯数据是明文传输的,因此存在数据泄露和被篡改的风险,咱们能够写一段测试代码试验一下。java
TCP Server:node
const net=require('net'); const server=net.createServer(); const serverHost='127.0.0.1'; const serverPort=8888; server.on('connection',(clientSocket)=>{ clientSocket.setEncoding('utf8'); clientSocket.on('data',(data)=>{ console.log(`client say:${data}`); }); clientSocket.on('error',()=>{}); }); server.listen({host:serverHost,port:serverPort},()=>{ console.log(`server is listening on port ${8888}`) });
TCP Client:git
const net=require('net'); const socket=new net.Socket(); const serverHost='127.0.0.1'; const serverPort=8888; let index=0; socket.on('error',()=>{}); socket.connect({host:serverHost,port:serverPort},()=>{ console.log(`client has connected to host ${serverHost} , port ${serverPort}`); setInterval(()=>{ socket.write(`i love u ${index++}`); },3000); });
启动Server和Client后,能够在Server的控制台中看到来自Client的消息:github
client say:i love u 0 client say:i love u 1 client say:i love u 2 client say:i love u 3 client say:i love u 4
数据在传输的过程当中是能够被全部人看到的,能够用WireShark抓包测试一下。因为WireShark没法直接抓取发送给本地的TCP包,我将Server部署到了另一台机器上,须要作以下修改:算法
配置好抓取IP:
抓包:
能够看到,表白信息全被别人看了去了 :(chrome
可能有人会说:我脸皮厚,随便看~浏览器
可是要注意了,全部http协议的请求,他们的数据都是这样发送的!能够认为,在一个使用http协议而不是https协议的网站上,你的游戏帐号、银行卡密码,都是这样赤果果的暴露在别人眼前的!安全
不只如此,别人还能够随意篡改你的数据!服务器
咱们上网的过程当中,数据从咱们的电脑到达目标服务器的过程当中,可能会通过层层代理和屡次路由,最终才到达目标服务器并非像上面咱们的Demo那样是直连的!
为了模拟这种状况,咱们能够在Demo的Client和Server之间加上一个耿直的Proxy:
const net=require('net'); const proxyServer=net.createServer(); const proxyHost='127.0.0.1'; const proxyPort=8889; const serverHost='127.0.0.1'; const serverPort=8888; //代理链接到真实目标Server const proxySocket=new net.Socket(); proxySocket.connect({host:serverHost,port:serverPort},()=>{ console.log(`proxy has connected to host ${serverHost} , port ${serverPort}`); }); //启动代理Server proxyServer.on('connection',(clientSocket)=>{ //直接将客户端的数据发给真实目标Server clientSocket.pipe(proxySocket); }); proxyServer.listen({host:proxyHost,port:proxyPort},()=>{ console.log(`proxy server is listening on port ${8889}`) });
修改Client的链接端口,连到proxy的8889端口而不是真实目标的8888端口,依次启动Server→Proxy→Client,能够看到Server收到了:
client say:i love u 0 client say:i love u 1 client say:i love u 2 client say:i love u 3
须要注意到这一行代码 clientSocket.pipe(proxySocket)
,因此说这是一个耿直的代理:)
换一个不耿直的代理,它会这样作:
clientSocket.setEncoding('utf8'); clientSocket.on('data',(data)=>{ data=data.replace(/love/g,'hate'); proxySocket.write(data); });
从新依次启动Server→Proxy→Client,能够看到Server收到了:
client say:i hate u 0 client say:i hate u 1 client say:i hate u 2 client say:i hate u 3 client say:i hate u 4
这下shabi了吧,妹子确定是追不到了:( 咋办呢?
先梳理一下思路:按照以前了解的加密算法原理,咱们可让Server给Client下发一份非对称加密的公钥,client用公钥加密数据而后发送,这样就不存在数据泄露和篡改的风险了。
然而,这个世界是很险恶的,会有人把本身假装成Server,给Client下发他们本身的公钥,并拦截真实Server下发给Client的真实公钥。
因为咱们没办法断定Client拿到的公钥是真实Server仍是恶意代理发过来的,因此咱们须要一个可信赖的第三方,来告诉Client拿到的公钥究竟是不是可信的,这个第三方就是CA机构,Certificate Authority,证书受权中心。
引入了CA机构后,获取证书流程以下:
在实际Client应用中,例如浏览器中,扮演可信角色——CA机构的其实是浏览器提早内置好的,一部分浏览器厂商认为可信的CA机构的根证书,下面演示一下如何创办一家CA机构,并为一个服务器颁发CA证书。
咱们能够利用开源的openssl库来创办一家私人的CA机构,上面的演示Demo目录结构为:
├─client │ client.js ├─proxy │ proxy.js └─server server.js
新建一个CA目录,建立一家CA机构,能够通俗地理解为:
这样,咱们就获得了一份称为“根证书”的证书文件,浏览器若是信任咱们的CA机构,就能够把咱们的根证书内置到浏览器中。
对应的openssl命令为:
openssl genrsa -out caPrivate.key 2048
建立一个2048位的非对称加密私钥openssl req -new -key caPrivate.key -out ca.csr
经过私钥建立一个正式签名请求文件,期间会要求输入机构名称、地址、email等信息。openssl x509 -req -in ca.csr -signkey caPrivate.key -out ca.crt
使用x509证书协议为刚刚建立的证书签名请求签名,获得ca.crt文件,即“根证书”。至此,咱们成功创办了一家拥有本身根证书的CA机构,文件列表:
ca.crt
ca.csr
caPrivate.key
证书的细节远不止这么简单,具体的能够参见CA证书标准X.509,https://www.ietf.org/rfc/rfc5280.txt
为了安全,咱们升级一下前边Demo中的Server,建立本身的证书,并请求CA机构签名颁发CA证书,来进行TLS安全通讯。
openssl genrsa -out private.key 2048
openssl req -new -key private.key -out request.csr
openssl x509 -req -CA ../CA/ca.crt -CAkey ../CA/caPrivate.key -CAcreateserial -in request.csr -out server.crt
CA机构为LtsServer的证书签名,并颁发CA证书文件server.crt这里要注意的是,本地测试的时候,Common Name
属性要填写localhost,若填写线上应用地址,则使用时客户端会报错:
Error: Hostname/IP doesn't match certificate's altnames: "Host: localhost. is not cert's CN: zoucz.com"
使用上面一步颁发的CA证书来进行TLS通讯,须要三个步骤:
① TLS Server:
const tls = require('tls'); const fs=require('fs'); const serverHost='127.0.0.1'; const serverPort=8888; const options = { key: fs.readFileSync('private.key'), cert: fs.readFileSync('server.crt'), }; var tlsServer = tls.createServer(options,(clientSocket) => { clientSocket.setEncoding('utf8'); clientSocket.on('data',(data)=>{ console.log(`client say:${data}`); }); clientSocket.on('error',(e)=>{console.log(e)}); }); tlsServer.listen({host:serverHost,port:serverPort},()=>{ console.log(`lts server is listening on port ${8888}`) });
② 将CA机构根证书内置到Client中:
③ 建立 TLS Client
const tls = require('tls'); const fs = require('fs'); const serverHost='127.0.0.1'; const serverPort=8888; const options = { ca: [ fs.readFileSync('ca.crt') ] }; let index=0; var tlsSocket = tls.connect(serverPort, options, () => { console.log(`tls client has connected to host ${serverHost} , port ${serverPort}`); setInterval(()=>{ tlsSocket.write(`i love u ${index++}`); },3000); }); tlsSocket.on('error',(e)=>{console.log(e)});
再将服务端部署到另一台机器上,抓包:
如今看到的内容就是乱码了,没有内容泄露的风险。同理,在数据传输的过程当中,第三方也没法篡改咱们的数据了。
将本身的测试TLS服务部署到另一台机器上时,有个要注意的地方,TlsClient的option中须要修改以下:
const options = { ca: [ fs.readFileSync('ca.crt') ], checkServerIdentity: function (host, cert) { return undefined; } };
这是由于TLS通讯时,对于服务端身份的检查,使用域名和使用IP的状况下,验证的策略不一样,当咱们在本地测试,使用IP时,须要将IP加入证书的SAN扩展(Subject Alternative Name)中,关于此扩展的内容,能够到https://www.ietf.org/rfc/rfc5280.txt查询,我没有深刻研究。
前边1.1小节中说道,http协议是基于tcp传输协议的不安全协议,那么https协议为何被认为是安全的协议呢? 答案就是,它是基于tls传输协议的应用层协议。
有了前边对LTS通讯原理的了解,再来看https就很是简单了,咱们能够直接复用刚刚为TLS Server颁发的CA证书,来建立一个https服务器。
var https = require('https'); var fs = require('fs'); var options = { key: fs.readFileSync('./private.key'), cert: fs.readFileSync('./server.crt') }; https.createServer(options, function(req, res) { res.writeHead(200); res.end('hello https'); }).listen(8866);
chrome会这样提示你,咱们的浏览器里边找不到为这个服务器CA证书签名的CA证书,这极可能是一个骗子网站,这是由于咱们的CA机构根证书没有被内置到chrome里边。点继续访问:
查看证书:
能够将咱们的CA机构根证书导入chrome,在chrome设置中:
重启chrome,再次访问咱们的https服务
看,变成小绿锁了~
最后,本文全部Demo代码存放于:https://github.com/zouchengzhuo/nodejsLearn/tree/master/caAndTLS
原文来自个人我的站点:http://zoucz.com/blog/2017/01/05/understand-crypto-3/