-- 获取证书内容,比如 io.open("my.crt"):read("*a")
local cert_data, err = get_my_pem_cert_data()
if not cert_data then
ngx.log(ngx.ERR, "failed to get PEM cert: ", err)
return
end
-- 解析出 cdata 类型的证书值,你可以用 lua-resty-lrucache 缓存解析结果
local cert, err = ssl.parse_pem_cert(cert_data)
if not cert then
ngx.log(ngx.ERR, "failed to parse PEM cert: ", err)
return
end
local ok, err = ssl.set_cert(cert)
if not ok then
ngx.log(ngx.ERR, "failed to set cert: ", err)
return
end
local pkey_data, err = get_my_pem_priv_key_data()
if not pkey_data then
ngx.log(ngx.ERR, "failed to get DER private key: ", err)
return
end
local pkey, err = ssl.parse_pem_priv_key(pkey_data)
if not pkey then
ngx.log(ngx.ERR, "failed to parse pem key: ", err)
return
end
local ok, err = ssl.set_priv_key(pkey)
if not ok then
ngx.log(ngx.ERR, "failed to set private key: ", err)
return
end
再看 DER 的处理方式:
-- 获取证书内容,比如 io.open("my.crt.der"):read("*a")
local cert_data, err = get_my_der_cert_data()
-- 你也可以把 pem 格式的证书直接转换成 der 格式的,像这样:
-- local cert_pem_data = get_my_pem_cert_data()
-- local cert_data, err = ssl.cert_pem_to_der(cert_pem_data)
if not cert_data then
ngx.log(ngx.ERR, "failed to get DER cert: ", err)
return
end
-- 这里的 cert_data 是 string 类型的,所以可以直接缓存到 lua_shared_dict 当中
local ok, err = ssl.set_der_cert(cert_data)
if not ok then
ngx.log(ngx.ERR, "failed to set DER cert: ", err)
return
end
local pkey_data, err = get_my_der_priv_key_data()
-- 你也可以把 pem 格式的私钥直接转换成 der 格式的,像这样:
-- local pkey_pem_data = get_my_pem_priv_key_data()
-- local pkey_data, err = ssl.priv_key_pem_to_der(pkey_pem_data)
if not pkey_data then
ngx.log(ngx.ERR, "failed to get DER private key: ", err)
return
end
local ok, err = ssl.set_der_priv_key(pkey_data)
if not ok then
ngx.log(ngx.ERR, "failed to set DER private key: ", err)
return
end
作为开发者,我们并不关心第一点。但第二点却不能不解决。 还好 OCSP 有一个“补丁”,叫 OCSP stapling。 Web 应用可以定期通过 OCSP stapling 从 CA 处获取自己证书的吊销状态,然后在 SSL 握手时把结果返回给浏览器。
既然我们的证书已经是动态加载的,我们也需要实现动态的 OCSP stapling。
看下面的示例代码:
-- ngx.ocsp 来自于 lua-resty-core 标准库
local ocsp = require("ngx.ocsp")
local http = require("resty.http")
-- 上接动态获取 DER 格式的证书
-- 当前 OCSP 接口只支持 DER 格式的证书
local ocsp_url, err = ocsp.get_ocsp_responder_from_der_chain(cert_der_data)
if not ocsp_url then
ngx.log(ngx.ERR, "failed to get OCSP responder: ", err)
return ngx.exit(ngx.ERROR)
end
-- 生成 OCSP 请求体
local ocsp_req, ocsp_request_err = ocsp.create_ocsp_request(cert_der_data)
if not ocsp_req then
ngx.log(ngx.ERR, "failed to create OCSP request: ", err)
return ngx.exit(ngx.ERROR)
end
local httpc = http.new()
httpc:set_timeout(10000)
local res, req_err = httpc:request_uri(ocsp_url, {
method = "POST",
body = ocsp_req,
headers = {
["Content-Type"] = "application/ocsp-request",
}
})
-- 校验 CA 的返回结果
if not res then
ngx.log(ngx.ERR, "OCSP responder query failed: ", err)
return ngx.exit(ngx.ERROR)
end
local http_status = res.status
if http_status ~= 200 then
ngx.log(ngx.ERR, "OCSP responder returns bad HTTP status code ",
http_status)
return ngx.exit(ngx.ERROR)
end
local ocsp_resp = res.body
if ocsp_resp and #ocsp_resp > 0 then
local ok, err = ocsp.validate_ocsp_response(ocsp_resp, der_cert_chain)
if not ok then
ngx.log(ngx.ERR, "failed to validate OCSP response: ", err)
return ngx.exit(ngx.ERROR)
end
-- 设置当前 SSL 连接的 OCSP stapling
ok, err = ocsp.set_ocsp_status_resp(ocsp_resp)
if not ok then
ngx.log(ngx.ERR, "failed to set ocsp status resp: ", err)
return ngx.exit(ngx.ERROR)
end
end