关于 HTTPS 协议的文章已经有很多,其原理和与 HTTP 协议的区别大家也都知道了,所以本文着重于介绍 HTTPS 服务器的搭建和相互通信的方法的介绍。
本文使用 Node 搭建服务器。
HTTP 服务器
我们首先搭 HTTP 服务器:
import { createServer } from 'http'
createServer((req, res) => {
res.end('http-a')
}).listen(9000)
用 HTTP 协议可以直接访问。
HTTPS 服务器
证书
众所周知,HTTPS 服务器需要证书。
我们使用 OpenSSL 生成自签名证书:
openssl genrsa -out key.pem
openssl req -new -key key.pem -out csr.pem
openssl x509 -req -days 365 -in csr.pem -signkey key.pem -out cert.pem
genrsa命令用于生成私钥。
req命令用于生成符合证书要求并包含私钥信息的文件。使用时会要求输入参数。
在这一步中,为了接下来访问时的域名统一,我们将参数 Common Name (CN) 设置为 localhost,其余默认。
x509命令用于生成公钥,-days参数表示有效天数。
需要注意的是,生成文件的后缀名并不关键,比如有些人习惯生成 .key,.csr,.crt 文件。
服务器
创建 HTTPS 服务器时需要读取证书和私钥文件。
import { readFileSync } from 'fs'
import { createServer } from 'https'
createServer(
{
key: readFileSync('./certificate/key.pem'),
cert: readFileSync('./certificate/cert.pem')
},
(req, res) => {
res.end('HTTPS')
}
).listen(9001)
其它格式的证书
https 包也可以使用 pfx 格式的证书。
{
pfx: readFileSync('.pfx'),
passphrase: 'password',
}
访问
如果使用 Postman 访问 https://127.0.0.1:9001,可以成功。但如果我们用 Chrome 访问,会提示“您的连接不是私密连接”,这是 Chrome 对自签名服务器的限制,我们需要点击“高级”才能继续访问。
在 Node 程序中,如果我们用 http 和 https 包的 get 方法访问:
import Http from 'http'
import Https from 'https'
async function request(url) {
let method = /^https/.test(url) ? Https.get : Http.get
return new Promise((resolve, reject) => {
method(url, res => {
if (res.statusCode === 200) {
let data = ''
res.setEncoding('utf-8')
res.on('data', payload => (data += payload))
res.on('end', () => {
resolve(data)
})
} else {
reject(new Error(res.statusCode))
}
}).on('error', er => reject(er))
})
}
console.debug(await request('http://localhost:9000'))
console.debug(await request('https://localhost:9001'))
访问 http://127.0.0.1:9000 时没有问题,但访问 https://127.0.0.1:9001,会报错提示 “RequestError: self signed certificate”,同样也是自签名证书的问题。
自签名证书
有以下几种办法忽略“自签名证书”的问题。
- 设置全局配置
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
或者
Https.globalAgent.options.rejectUnauthorized = false
这种方法有点危险,因为全局的 https 请求都不会验证证书。
- 设置
Http.get方法的参数
Http.get(url,{
rejectUnauthorized: false
},callback)
- 设置公钥
在发出请求时,设置于服务器使用的相同公钥。
Http.get(url,{
ca: readFileSync('./certificate/cert.pem')
},callback)
权威签名证书
如果想规避自签名证书的缺点,特别是在外网环境中,那么可以从权威机构申请证书,放到服务器中。