使用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 -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');