1 不安全的TCP通信

普通的TCP通信数据是明文传输的,所以存在数据泄露和被篡改的风险,我们可以写一段测试代码试验一下,NODE.JS代码:

TCP Server:

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:

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的消息:

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

1.1 数据泄露

数据在传输的过程中是可以被所有人看到的,可以用WireShark抓包测试一下。由于WireShark无法直接抓取发送给本地的TCP包,我将Server部署到了另外一台机器上,需要做如下修改:

  • serverHost都修改为另一台机器的IP。
  • 打开Server机器防火墙的8888端口。

配置好抓取IP:

抓包:

可以看到,表白信息全被别人看了去了 :(

可能有人会说:我脸皮厚,随便看~

但是要注意了,所有http协议的请求,他们的数据都是这样发送的!可以认为,在一个使用http协议而不是https协议的网站上,你的游戏账号、银行卡密码,都是这样赤果果的暴露在别人眼前的!

不仅如此,别人还可以随意篡改你的数据!

1.2 数据篡改

我们上网的过程中,数据从我们的电脑到达目标服务器的过程中,可能会经过层层代理和多次路由,最终才到达目标服务器并不是像上面我们的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了吧,妹子肯定是追不到了:( 咋办呢?

2 CA机构

先梳理一下思路:按照之前了解的加密算法原理,我们可以让Server给Client下发一份非对称加密的公钥,client用公钥加密数据然后发送,这样就不存在数据泄露和篡改的风险了。

然而,这个世界是很险恶的,会有人把自己伪装成Server,给Client下发他们自己的公钥,并拦截真实Server下发给Client的真实公钥。

由于我们没办法判定Client拿到的公钥是真实Server还是恶意代理发过来的,所以我们需要一个可信赖的第三方,来告诉Client拿到的公钥到底是不是可信的,这个第三方就是CA机构,Certificate Authority,证书授权中心。

引入了CA机构后,获取证书流程如下:

在实际Client应用中,例如浏览器中,扮演可信角色——CA机构的实际上是浏览器提前内置好的,一部分浏览器厂商认为可信的CA机构的根证书,下面演示一下如何创办一家CA机构,并为一个服务器颁发CA证书。

2.1 创办CA机构

我们可以利用开源的openssl库来创办一家私人的CA机构,上面的演示Demo目录结构为:

├─client
│ client.js
├─proxy
│ proxy.js
└─server
server.js

新建一个CA目录,创建一家CA机构,可以通俗地理解为:

  1. 生成一份非对称加密私钥
  2. 生成一份明文的证书文件,证书中记录该机构的地址、名称、email、机构公钥、有效期等信息
  3. 为自己的证书签名:即通过散列函数计算出证书文件的散列值,并通过私钥对散列值加密。将签名附加到证书中。

这样,我们就得到了一份称为“根证书”的证书文件,浏览器如果信任我们的CA机构,就可以把我们的根证书内置到浏览器中。

对应的openssl命令为:

  1. openssl genrsa -out caPrivate.key 2048 创建一个2048位的非对称加密私钥
  2. openssl req -new -key caPrivate.key -out ca.csr 通过私钥创建一个正式签名请求文件,期间会要求输入机构名称、地址、email等信息。
  3. 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

2.2 签发CA证书

为了安全,我们升级一下前边Demo中的Server,创建自己的证书,并请求CA机构签名颁发CA证书,来进行TLS安全通信。

  1. 新建目录 TlsServer
  2. 创建私钥 openssl genrsa -out private.key 2048
  3. 创建证书签名请求 openssl req -new -key private.key -out request.csr
  4. 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"

2.3 开始安全的TLS通信

使用上面一步颁发的CA证书来进行TLS通信,需要三个步骤:

  • 使用CA机构为Tls Server颁发的CA证书创建TLS Server
  • 将CA机构的根证书内置到TLS Client中
  • 使用CA根证书创建TLS Client

① 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查询,我没有深入研究。

3 基于TLS的HTTPS协议

前边1.1小节中说道,http协议是基于tcp传输协议的不安全协议,那么https协议为什么被认为是安全的协议呢? 答案就是,它是基于tls传输协议的应用层协议。

3.1 创建https服务

有了前边对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里边。点继续访问:

查看证书:

3.2 让自己的CA机构被chrome信任

可以将我们的CA机构根证书导入chrome,在chrome设置中:

重启chrome,再次访问我们的https服务

看,变成小绿锁了~

最后,本文所有Demo代码存放于:https://github.com/zouchengzhuo/nodejsLearn/tree/master/caAndTLS

理解加密算法——创建CA机构,签发证书并开始TLS通信的更多相关文章

  1. 通过Go语言创建CA与签发证书

    本篇文章中,将描述如何使用go创建CA,并使用CA签署证书.在使用openssl创建证书时,遵循的步骤是 创建秘钥 > 创建CA > 生成要颁发证书的秘钥 > 使用CA签发证书.这种 ...

  2. 理解加密算法(三)——创建CA机构,签发证书并开始TLS通信

    接理解加密算法(一)--加密算法分类.理解加密算法(二)--TLS/SSL 1 不安全的TCP通信 普通的TCP通信数据是明文传输的,所以存在数据泄露和被篡改的风险,我们可以写一段测试代码试验一下. ...

  3. 数字证书的理解以及自建CA机构颁发证书

    一.理解什么是数字证书   http://www.cnblogs.com/JeffreySun/archive/2010/06/24/1627247.html    理解数字证书等概念,无数次想好好看 ...

  4. https学习笔记三----OpenSSL生成root CA及签发证书

    在https学习笔记二,已经弄清了数字证书的概念,组成和在https连接过程中,客户端是如何验证服务器端的证书的.这一章,主要介绍下如何使用openssl库来创建key file,以及生成root C ...

  5. OpenSSL生成root CA及签发证书

    一.openssl 简介 openssl 是目前最流行的 SSL 密码库工具,其提供了一个通用.健壮.功能完备的工具套件,用以支持SSL/TLS 协议的实现.官网:https://www.openss ...

  6. Centos7创建CA和申请证书

    转载:http://rackie386.blog.51cto.com/11279229/1947999 Centos7.3创建CA和申请证书 openssl 的配置文件:/etc/pki/tls/op ...

  7. Centos7创建CA和申请证书 转自https://www.cnblogs.com/mingzhang/p/8949541.html

    Centos7.3创建CA和申请证书 openssl 的配置文件:/etc/pki/tls/openssl.cnf 重要参数配置路径 dir   = /etc/pki/CA               ...

  8. IIS8中使用OpenSSL来创建CA并且签发SSL证书

    前言 [转载]http://alvinhu.com/blog/2013/06/12/creating-a-certificate-authority-and-signing-the-ssl-certi ...

  9. 创建私有CA并签发证书

    一.创建私有CA 1.创建所需要的文件 2.创建私有密钥 3.CA自签证书 -new: 生成新证书签署请求:               -x509: 专用于CA生成自签证书:不自签的时候不要加该选项 ...

随机推荐

  1. [转载]Java读取Excel中的单元格数据

    目前网上能找到的读取Excel表格中数据的两种比较好的方案:PageOffice好用开发效率高:POI免费.供大家参考,针对具体情况选择具体方案. 1. PageOffice读取excel impor ...

  2. Java 进阶7 并发优化 1 并行程序的设计模式

       本章重点介绍的是基于 Java并行程序开发以及优化的方法,对于多核的 CPU,传统的串行程序已经很好的发回了 CPU性能,此时如果想进一步提高程序的性能,就应该使用多线程并行的方式挖掘 CPU的 ...

  3. ubuntu安装amd/ati显卡驱动

    原网页: http://forum.ubuntu.org.cn/viewtopic.php?f=126&t=390372 整合了几个帖子,大概如此:用以下命令卸载所有驱动: 代码: sudo ...

  4. windows下的一些命令

    dir 相当于linux下的ls clear 清屏 netstat 活动连接 | 管道命令 findstr 查询类似linux的grep tasklist 查看进程列表 taskkill 杀死进程 d ...

  5. Python基础学习----元组

    # 元组和列表的区别: # 相同点: # 1.表示多个元素的序列 # 2.数据之间用 , 分开 # 3.索引都是[0]开始 # # 不同点: # 1.列表创建是[],元组是()表示 # 2.元组元素一 ...

  6. L156

    China has specified the definition and diagnosis standard for internet addiction in its latest adole ...

  7. Talk to customer about the trouble with wireless failure connection。

    It’s upset for  me... 1 ATemel Studio61 and  MKII program failure. Notes: The Flash file : A 90 is w ...

  8. pdi vcard-2.1

    vCard The Electronic Business Card Version 2.1 A versit Consortium Specification September 18, 1996 ...

  9. Shell 命令行批量处理图片文件名

    Shell 命令行批量处理图片文件名 从网上下载了一堆图片,有的是*.jpg的,有的是*.jpeg的.并且文件名有长有短,很是糟心.因此,我想把这些文件给全部整理好,当然是用shell来处理啦! 说干 ...

  10. 每周荐书:云原生、Docker、Web算法(评论送书)

    每周荐书:云原生.Docker.Web算法(评论送书) 感谢大家对每周荐书栏目的支持,先公布下上周中奖名单 名优秀评论可以免费获得此书.   云原生应用架构实践 云原生架构,关注简化开发流程.提升研发 ...