接理解加密算法(一)——加密算法分類、理解加密算法(二)——TLS/SSL 1 不安全的TCP通信普通的TCP通信數(shù)據(jù)是明文傳輸?shù)?,所以存在?shù)據(jù)泄露和被篡改的風(fēng)險(xiǎn),我們可以寫一段測(cè)試代碼試驗(yàn)一下。 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);
}); 啟動(dòng)Server和Client后,可以在Server的控制臺(tái)中看到來(lái)自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 數(shù)據(jù)泄露數(shù)據(jù)在傳輸?shù)倪^(guò)程中是可以被所有人看到的,可以用WireShark抓包測(cè)試一下。由于WireShark無(wú)法直接抓取發(fā)送給本地的TCP包,我將Server部署到了另外一臺(tái)機(jī)器上,需要做如下修改:
配置好抓取IP: 可能有人會(huì)說(shuō):我臉皮厚,隨便看~ 但是要注意了,所有http協(xié)議的請(qǐng)求,他們的數(shù)據(jù)都是這樣發(fā)送的!可以認(rèn)為,在一個(gè)使用http協(xié)議而不是https協(xié)議的網(wǎng)站上,你的游戲賬號(hào)、銀行卡密碼,都是這樣赤果果的暴露在別人眼前的! 不僅如此,別人還可以隨意篡改你的數(shù)據(jù)! 1.2 數(shù)據(jù)篡改我們上網(wǎng)的過(guò)程中,數(shù)據(jù)從我們的電腦到達(dá)目標(biāo)服務(wù)器的過(guò)程中,可能會(huì)經(jīng)過(guò)層層代理和多次路由,最終才到達(dá)目標(biāo)服務(wù)器并不是像上面我們的Demo那樣是直連的! 為了模擬這種情況,我們可以在Demo的Client和Server之間加上一個(gè)耿直的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;
//代理連接到真實(shí)目標(biāo)Server
const proxySocket=new net.Socket();
proxySocket.connect({host:serverHost,port:serverPort},()=>{
console.log(`proxy has connected to host ${serverHost} , port ${serverPort}`);
});
//啟動(dòng)代理Server
proxyServer.on('connection',(clientSocket)=>{
//直接將客戶端的數(shù)據(jù)發(fā)給真實(shí)目標(biāo)Server
clientSocket.pipe(proxySocket);
});
proxyServer.listen({host:proxyHost,port:proxyPort},()=>{
console.log(`proxy server is listening on port ${8889}`)
}); 修改Client的連接端口,連到proxy的8889端口而不是真實(shí)目標(biāo)的8888端口,依次啟動(dòng)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 需要注意到這一行代碼 換一個(gè)不耿直的代理,它會(huì)這樣做: clientSocket.setEncoding('utf8');
clientSocket.on('data',(data)=>{
data=data.replace(/love/g,'hate');
proxySocket.write(data);
}); 重新依次啟動(dòng)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機(jī)構(gòu)先梳理一下思路:按照之前了解的加密算法原理,我們可以讓Server給Client下發(fā)一份非對(duì)稱加密的公鑰,client用公鑰加密數(shù)據(jù)然后發(fā)送,這樣就不存在數(shù)據(jù)泄露和篡改的風(fēng)險(xiǎn)了。 然而,這個(gè)世界是很險(xiǎn)惡的,會(huì)有人把自己偽裝成Server,給Client下發(fā)他們自己的公鑰,并攔截真實(shí)Server下發(fā)給Client的真實(shí)公鑰。 由于我們沒(méi)辦法判定Client拿到的公鑰是真實(shí)Server還是惡意代理發(fā)過(guò)來(lái)的,所以我們需要一個(gè)可信賴的第三方,來(lái)告訴Client拿到的公鑰到底是不是可信的,這個(gè)第三方就是CA機(jī)構(gòu),Certificate Authority,證書授權(quán)中心。 引入了CA機(jī)構(gòu)后,獲取證書流程如下: 2.1 創(chuàng)辦CA機(jī)構(gòu)我們可以利用開(kāi)源的openssl庫(kù)來(lái)創(chuàng)辦一家私人的CA機(jī)構(gòu),上面的演示Demo目錄結(jié)構(gòu)為: ├─client
│ client.js
├─proxy
│ proxy.js
└─server
server.js 新建一個(gè)CA目錄,創(chuàng)建一家CA機(jī)構(gòu),可以通俗地理解為:
這樣,我們就得到了一份稱為“根證書”的證書文件,瀏覽器如果信任我們的CA機(jī)構(gòu),就可以把我們的根證書內(nèi)置到瀏覽器中。 對(duì)應(yīng)的openssl命令為:
至此,我們成功創(chuàng)辦了一家擁有自己根證書的CA機(jī)構(gòu),文件列表: 證書的細(xì)節(jié)遠(yuǎn)不止這么簡(jiǎn)單,具體的可以參見(jiàn)CA證書標(biāo)準(zhǔn)X.509,https://www./rfc/rfc5280.txt 2.2 簽發(fā)CA證書為了安全,我們升級(jí)一下前邊Demo中的Server,創(chuàng)建自己的證書,并請(qǐng)求CA機(jī)構(gòu)簽名頒發(fā)CA證書,來(lái)進(jìn)行TLS安全通信。
這里要注意的是,本地測(cè)試的時(shí)候, Error: Hostname/IP doesn't match certificate's altnames: 'Host: localhost. is not cert's CN: ' 2.3 開(kāi)始安全的TLS通信使用上面一步頒發(fā)的CA證書來(lái)進(jìn)行TLS通信,需要三個(gè)步驟:
① 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機(jī)構(gòu)根證書內(nèi)置到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)}); 再將服務(wù)端部署到另外一臺(tái)機(jī)器上,抓包: 將自己的測(cè)試TLS服務(wù)部署到另外一臺(tái)機(jī)器上時(shí),有個(gè)要注意的地方,TlsClient的option中需要修改如下: const options = {
ca: [ fs.readFileSync('ca.crt') ],
checkServerIdentity: function (host, cert) {
return undefined;
}
}; 這是因?yàn)門LS通信時(shí),對(duì)于服務(wù)端身份的檢查,使用域名和使用IP的情況下,驗(yàn)證的策略不同,當(dāng)我們?cè)诒镜販y(cè)試,使用IP時(shí),需要將IP加入證書的SAN擴(kuò)展(Subject Alternative Name)中,關(guān)于此擴(kuò)展的內(nèi)容,可以到https://www./rfc/rfc5280.txt查詢,我沒(méi)有深入研究。 3 基于TLS的HTTPS協(xié)議前邊1.1小節(jié)中說(shuō)道,http協(xié)議是基于tcp傳輸協(xié)議的不安全協(xié)議,那么https協(xié)議為什么被認(rèn)為是安全的協(xié)議呢? 答案就是,它是基于tls傳輸協(xié)議的應(yīng)用層協(xié)議。 3.1 創(chuàng)建https服務(wù)有了前邊對(duì)LTS通信原理的了解,再來(lái)看https就非常簡(jiǎn)單了,我們可以直接復(fù)用剛剛為TLS Server頒發(fā)的CA證書,來(lái)創(chuàng)建一個(gè)https服務(wù)器。 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);
3.2 讓自己的CA機(jī)構(gòu)被chrome信任可以將我們的CA機(jī)構(gòu)根證書導(dǎo)入chrome,在chrome設(shè)置中: 最后,本文所有Demo代碼存放于:https://github.com/zouchengzhuo/nodejsLearn/tree/master/caAndTLS 原文來(lái)自我的個(gè)人站點(diǎn):http:///blog/2017/01/05/understand-crypto-3/ 如果您覺(jué)得這篇文章對(duì)您有幫助,歡迎點(diǎn)擊右下角【推薦】支持一下我,謝謝! 這篇文章為原創(chuàng)文章,轉(zhuǎn)載請(qǐng)注明出處! 作者 TZYY / 原文鏈接 http://www.cnblogs.com/tzyy/p/6252729.html
|
|