本地開發有時會遇到必須使用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 -in ca-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');