接理解加密算法(一)——加密算法分類、理解加密算法(二)——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/