在網(wǎng)絡(luò)中,數(shù)據(jù)在服務(wù)器端和客戶端之間傳遞批旺,由于是明文幌陕,一旦被人監(jiān)控,數(shù)據(jù)就會(huì)泄漏汽煮。為此我們需要將數(shù)據(jù)加密后再進(jìn)行傳輸搏熄,但是對(duì)于應(yīng)用層協(xié)議而言棚唆,如HTTP、FTP等心例,仍希望能夠透明的處理數(shù)據(jù)宵凌,于是就有NetSpace提出了SSL(Secure Sockets Layer
,安全套接層)契邀,它在傳輸層提供對(duì)網(wǎng)絡(luò)連接加密功能摆寄,對(duì)于應(yīng)用層而言他是透明的,數(shù)據(jù)在傳遞到應(yīng)用層之前就完成了加密和解密的過(guò)程坯门。最后IETF將其標(biāo)準(zhǔn)化,成為TLS(Transport Layer Security
, 安全傳輸層協(xié)議)逗扒。
Node在網(wǎng)絡(luò)完全提供了3個(gè)模塊古戴,分別為crypto、tls矩肩、https现恼。crypto主要用于加密解密,SHA1黍檩、MD5等加密算法都在其中有體現(xiàn)叉袍,tls模塊提供了與net模塊類似的功能,區(qū)別在于它建立在TSL/SSL加密的TCP連接上刽酱,https模塊與http模塊接口一致喳逛,區(qū)別在于它建議在安全的連接之上。
TSL/SSL
TSL/SSL是一個(gè)公鑰/私鑰的結(jié)構(gòu)棵里,它是一個(gè)非對(duì)稱的結(jié)構(gòu)润文,每個(gè)服務(wù)器和客戶端都有自己的公私鑰,公鑰用來(lái)加密殿怜,私鑰用來(lái)解密典蝌,公私鑰是配對(duì)的,客戶端和服務(wù)端之間需要互換公鑰头谜,客戶端發(fā)送數(shù)據(jù)時(shí)要通過(guò)服務(wù)器的公鑰進(jìn)行加密骏掀,服務(wù)器端發(fā)送數(shù)據(jù)時(shí)則需要客戶端的公鑰進(jìn)行加密。
Node在底層采用openssl實(shí)現(xiàn)TSL/SSL柱告,為此生成私鑰和公鑰可以通過(guò)openssl來(lái)完成截驮。
# 生成服務(wù)端私鑰
$ openssl genrsa -out server.key 1024
# 生成客戶端私鑰
$ openssl genrsa -out client.key 1024
上述命令生成兩個(gè)長(zhǎng)度為1024的RSA私鑰文件,我們可以繼續(xù)通過(guò)它生成公鑰:
$ openssl rsa -in server.key -pubout -out server.pem
$ openssl rsa -in client.key -pubout -out client.pem
公私鑰的非對(duì)稱加密雖好末荐,但是在網(wǎng)絡(luò)中依然存在被竊聽的可能侧纯,典型的例子就是中間人攻擊,客戶端和服務(wù)端在互換公鑰的過(guò)程中甲脏,中間人攔截請(qǐng)求對(duì)客戶端扮演服務(wù)端的角色眶熬,對(duì)服務(wù)器端扮演客戶端角色妹笆,將自己的公鑰發(fā)送給服務(wù)端和客戶端,攔截到數(shù)據(jù)后娜氏,再用自己的私鑰解密拳缠,并且它知道了服務(wù)端的公鑰,就可以用來(lái)偽造請(qǐng)求贸弥,知道了客戶端的公鑰就能偽造響應(yīng)窟坐。
為了解決這個(gè)問(wèn)題,TSL/SSL引入了數(shù)字證書來(lái)進(jìn)行認(rèn)證绵疲,數(shù)字證書中包含了服務(wù)器的名稱和主機(jī)名哲鸳、服務(wù)器的公鑰、簽名頒發(fā)機(jī)構(gòu)的簽名盔憨。在建立連接前徙菠,會(huì)通過(guò)證書中的簽名確認(rèn)收到的公鑰是否來(lái)自目標(biāo)服務(wù)器,從而決定信任與否郁岩。
數(shù)字證書
CA(Certificate Authority,數(shù)字證書認(rèn)證中心)的作用是為站點(diǎn)頒發(fā)證書婿奔,且這個(gè)證書中具有CA通過(guò)自己的公鑰和私鑰實(shí)現(xiàn)的簽名。
服務(wù)器端需要通過(guò)自己的私鑰生成CSR(Certificate Signing Request,證書簽名請(qǐng)求)文件问慎,CA機(jī)構(gòu)將通過(guò)這個(gè)文件頒發(fā)屬于該服務(wù)器的簽名證書萍摊,只要通過(guò)CA機(jī)構(gòu)就能驗(yàn)證證書是否合法。
通過(guò)CA頒發(fā)證書通常是一個(gè)繁瑣的過(guò)程如叼,需要付出一定的精力和費(fèi)用冰木,對(duì)于中小企業(yè)而言,多半采用自簽名證書來(lái)構(gòu)建安全網(wǎng)絡(luò)薇正,所謂自簽名證書就是自己扮演CA機(jī)構(gòu)片酝,給自己的服務(wù)器頒發(fā)簽名證書。
以下為扮演CA角色挖腰,生成私鑰雕沿、生成SCR文件、通過(guò)私鑰自簽名生成證書的過(guò)程:
$ openssl genrsa -out ca.key 1024
$ openssl req -new -key ca.key -out ca.csr
$ openssl x509 -req -in ca.csr -signkey ca.key -out ca.crt
以下為扮演服務(wù)器角色猴仑,向我們自己的CA機(jī)構(gòu)申請(qǐng)簽名:
# 生成服務(wù)器端的CSR文件
$ openssl req -new -key server.key -out server.csr
# 向自己的CA機(jī)構(gòu)申請(qǐng)簽名
$ openssl x509 -req -CA ca.crt -CAkey ca.key -CAcreateserial -in server.csr -out server.crt
CA機(jī)構(gòu)將證書頒發(fā)給服務(wù)端后审轮,證書在請(qǐng)求的過(guò)程中會(huì)被發(fā)送到客戶端,客戶端需要通過(guò)CA的證書驗(yàn)證真?zhèn)瘟伤祝绻侵腃A機(jī)構(gòu)疾渣,它們的證書一般預(yù)裝在瀏覽器中。如果是自己扮演CA機(jī)構(gòu)崖飘,頒發(fā)自簽名證書則不能享受則不能享受這個(gè)福利榴捡,客戶端需要獲取到CA的證書才能進(jìn)行驗(yàn)證。
TLS服務(wù)
創(chuàng)建服務(wù)器端
將構(gòu)建服務(wù)所需要的證書備齊之后朱浴,通過(guò)Node的tls模塊來(lái)創(chuàng)建一個(gè)安全的TCP服務(wù)吊圾。
var tls = require('tls');
var fs = require('fs');
var options = {
key: fs.readFileSync('./keys/server.key'),
cert: fs.readFileSync('./keys/server.crt'),
requestCert: true,
ca: [ fs.readFileSync('./keys/ca.crt') ]
};
var server = tls.createServer(options, function (stream) {
console.log('server connected', stream.authorized ? 'authorized' : 'unauthorized');
stream.write("welcome!\n");
stream.setEncoding('utf8');
stream.pipe(stream);
});
server.listen(8000, function() {
console.log('server bound');
});
啟動(dòng)服務(wù)后达椰,通過(guò)下述命令測(cè)試證書是否正常。
$ openssl s_client -connect 127.0.0.1:8000
TLS客戶端
構(gòu)建客戶端之前项乒,也要生成客戶端屬于自己的私鑰和簽名啰劲。
# 創(chuàng)建私鑰
$ openssl genrsa -out client.key 1024
# 生成CSR
$ openssl req -new -key client.key -out client.csr
# 生成簽名證書
$ openssl x509 -req -CA ca.crt -CAkey ca.key -CAcreateserial -in client.csr -out client.crt
創(chuàng)建客戶端
var tls = require('tls');
var fs = require('fs');
var options = {
key: fs.readFileSync('./keys/client.key'),// 私鑰
cert: fs.readFileSync('./keys/client.crt'), // 根據(jù)私鑰生成CSR,想CA機(jī)構(gòu)申請(qǐng)的客戶端簽名證書
ca: [ fs.readFileSync('./keys/ca.crt') ] // CA的簽名證書
};
var stream = tls.connect(8000, options, function () {
console.log('client connected', stream.authorized ? 'authorized' : 'unauthorized');
process.stdin.pipe(stream);
});
stream.setEncoding('utf8');
stream.on('data', function(data) {
console.log(data);
});
stream.on('end', function() {
server.close();
});
啟動(dòng)客戶端的過(guò)程中檀何,用到了為客戶端生成的私鑰蝇裤、證書、CA證書频鉴,與普通的TCP服務(wù)端和客戶端相比栓辜,TLS就僅僅在這些證書的配置上有差別,其余部分基本相同砚殿。
HTTPS服務(wù)
HTTPS服務(wù)就是工作在TSL/SSL的HTTP啃憎,根據(jù)上面生成的私鑰和證書就能創(chuàng)建HTTPS服務(wù)。
var https = require('https');
var fs = require('fs');
var options = {
key: fs.readFileSync('./keys/server.key'),// 服務(wù)器私鑰
cert: fs.readFileSync('./keys/server.crt') // 服務(wù)器簽名證書
};
https.createServer(options, function (req, res) {
res.writeHead(200);
res.end("hello world\n");
}).listen(8000);
https客戶端
var https = require('https');
var fs = require('fs');
var options = {
hostname: 'localhost',
port: 8000,
path: '/',
method: 'GET',
key: fs.readFileSync('./keys/client.key'),// 客戶端私鑰
cert: fs.readFileSync('./keys/client.crt'), // 客戶端簽名證書
ca: [fs.readFileSync('./keys/ca.crt')] // CA機(jī)構(gòu)證書
};
options.agent = new https.Agent(options);
var req = https.request(options, function(res) {
res.setEncoding('utf-8');
res.on('data', function(d) {
console.log(d);
});
});
req.end();
req.on('error', function(e) {
console.log(e);
});