使用openssl和nodejs搭建本地https服務
本地開發有時會遇到必須使用https服務的情況,這裡介紹一下使用openssl自簽名證書,並使用nodejs開啟https服務。
1. 安裝openssl
在http://slproweb.com/products/Win32OpenSSL.html可以找到openssl安裝包,可以根據介紹下載對應版本安裝,安裝完成後將安裝位置bin目錄的檔案路徑新增到系統環境變數,此時就可以在全域性使用openssl指令,開啟命令列輸入`openssl -version`檢視openssl是否正確安裝。安裝完成後,選擇一個存放證書的位置,開啟控制檯通過openssl生成證書。
生成證書的步驟主要包含建立本地CA機構、建立伺服器證書和建立客戶端證書。
2.生成證書
在建立證書的過程中,會要求輸入密碼和證書資訊(瀏覽器地址左側有一個鎖,點開後選擇證書看到的資訊),密碼在輸入過程中,控制檯不會有任何顯示。輸入資訊需要填寫國家(ZH)、省市、機構等資訊,由於自己簽名並沒有公網的可認證性,所以這些資訊隨便填都可以。後面會要求輸入密碼生成證書,所以需要記住密碼。
CA:
生成私鑰:
openssl genrsa -out ca-key.pem -des 1024
生成公鑰:
openssl req -new -key ca-key.pem -out ca-csr.pem
生成證書:
openssl x509 -req -inca-csr.pem -signkey ca-key.pem -out ca-cert.pem
檢視資料夾中應該會有 ca-key.pem 、 ca-csr.pem、 ca-cert.pem三個檔案,如果其中有步驟出現操作,將這一段指令重新輸入即可覆蓋原檔案。
服務端:
服務端生成公鑰需要讀取配置檔案,建立openssl.cnf檔案在統計目錄下,內容為:
[req] distinguished_name = req_distinguished_name req_extensions= v3_req [req_distinguished_name] countryName = ZH countryName_default = CN stateOrProvinceName = BeiJing stateOrProvinceName_default = BeiJing localityName = ChengDu localityName_default = YaYunCun organizationalUnitName = public section organizationalUnitName_default = Domain Control Validated commonName = Internet Widgits Ltd commonName_max = 64 [ v3_req ] # Extensions to add to a certificate request basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment subjectAltName = @alt_names [alt_names] IP.1 = 127.0.0.1
上述資訊也是證書相關的資訊,後面的值都是隨意寫的,替換與否都沒有關係。
生成私鑰:
openssl genrsa -out server-key.pem 1024
生成公鑰:
openssl req -new -key server-key.pem -config openssl.cnf -out server-csr.pem
生成證書:
openssl x509 -req -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -in server-csr.pem -out server-cert.pem -extensions v3_req -extfile openssl.cnf
客戶端:
搭建https伺服器不需要客戶端證書,生成指令和上面類似:
生成私鑰:
openssl genrsa -out client-key.pem
生成公鑰:
openssl req -new -key client-key.pem -out client-csr.pem
生成證書:
openssl x509 -req -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -in client-csr.pem -out client-cert.pem
3. 使用nodejs搭建https服務
關鍵部分是https.createServer傳遞的options引數:
let options = { key: fs.readFileSync('./server-key.pem'), ca: [fs.readFileSync('./ca-cert.pem')], cert: fs.readFileSync('./server-cert.pem') };
使用https模組和fs模組,搭建伺服器訪問本地html檔案:
const https = require('https'); const fs = require('fs'); const url = require('url') // 引入證書 let options = { key: fs.readFileSync('./server-key.pem'), ca: [fs.readFileSync('./ca-cert.pem')], cert: fs.readFileSync('./server-cert.pem') }; // 建立https服務 https.createServer(options,function(req,res){ // 獲取請求檔案路徑 var pathname = url.parse(req.url).pathname; // 設定預設訪問index.html檔案 if(pathname == '/') { pathname = '/index.html'; } // 讀取檔案,並設定為html型別,返回給瀏覽器 fs.readFile(__dirname + pathname, function (err, data) { if (err) { if(pathname == '/favicon.ico') { res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8;'}); res.end(); } console.error(err); res.writeHead(404, {'Content-Type': 'text/html'}); } else { res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8;'}); res.write(data.toString()); } res.end(); }); // 監聽本地3000埠 }).listen(3000,'127.0.0.1');
當然,使用框架也可以更改為https服務,例如express或者koa2.
例如koa2,在bin目錄www檔案中替換以下為程式碼:
const https = require('https'); const fs = require('fs'); let options = { key: fs.readFileSync('./keys/server-key.pem'), ca: [fs.readFileSync('./keys/ca-cert.pem')], cert: fs.readFileSync('./keys/server-cert.pem') }; // 將原本開啟服務的程式碼註釋更換為以下部分,根據需求監聽埠 https.createServer(options,function(req,res){ res.writeHead(200); res.end(app.callback()); }).listen(3000,'127.0.0.1');