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. Prism 4 文档 ---第10章 Silverlight和WPF之间共享代码

        本主题帮助你理解来自Prism的多目标和它的优缺点.多目标的代码针对两个不同的平台有大致相同的代码库.这允许同时保持代码尽可能多一样能够产生针对两种不同技术的二进制文件.在这种情况下,本节介绍 ...

  2. Linux:vim环境设置

    vim环境设置 vim的环境设置在/etc/vimrc的这个文件中,不过不建议直接修改该配置文件.但是可以修改~/.vimrc文件,默认是不存在的,要手动创建并写入设置值. set hlsearch ...

  3. MATROSKA 文件格式

    MATROSKA 文件格式 1.EBML (Extensible Binary Meta Language): EBML语言使用不定长整数,这种方式相对于固定长度的32位/64位字长的整数值更节约空间 ...

  4. CUDA Samples: Ray Tracking

    以下CUDA sample是分别用C++和CUDA实现的生成光线跟踪图像,并对其中使用到的CUDA函数进行了解说,code参考了<GPU高性能编程CUDA实战>一书的第六章,CUDA各实现 ...

  5. 《Tomcat内核设计剖析》勘误表

    <Tomcat内核设计剖析>勘误表 书中第95页图request部分印成了reqiest. 书中第311页两个tomcat3,其中一个应为tomcat4. 书中第5页URL应为URI. 书 ...

  6. 显卡、显卡驱动、显存、GPU、CUDA、cuDNN

    显卡 Video card,Graphics card,又叫显示接口卡,是一个硬件概念(相似的还有网卡),执行计算机到显示设备的数模信号转换任务,安装在计算机的主板上,将计算机的数字信号转换成模拟 ...

  7. 将HTML的页脚固定在屏幕下方

    /********************************************************************* * 将HTML的页脚固定在屏幕下方 * 说明: * 处理的 ...

  8. day5 io模型

    五种概览:http://www.cnblogs.com/xiehongfeng100/p/4763225.html http://sukai.me/linux-five-io-models/  内有多 ...

  9. ranch分析学习(三)

    接着上一篇继续研究 上一篇结尾的时候,我们谈到了连接,监听两个监督树,今天我们就来看看这两个监督树和他们的工作者都是干什么的,怎么实现的.文件编号接上篇. 6. ranch_acceptors_sup ...

  10. Django博客开发实践,初学者开发经验

    python,Django初学者,开发简易博客,做了一下笔记,记录了开发的过程,功力浅薄,仅供初学者互相 交流,欢迎意见建议.具体链接:Django博客开发实践(一)--分析需求并创建项目 地址:ht ...