Skip to content

Commit ddd9290

Browse files
feature: support passphrase protected private key.
1 parent df3dd35 commit ddd9290

File tree

6 files changed

+285
-6
lines changed

6 files changed

+285
-6
lines changed

lib/ngx/ssl.lua

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ if subsystem == 'http' then
6565
size_t pem_len, unsigned char *der, char **err);
6666

6767
int ngx_http_lua_ffi_priv_key_pem_to_der(const unsigned char *pem,
68-
size_t pem_len, unsigned char *der, char **err);
68+
size_t pem_len, const unsigned char *passphrase,
69+
unsigned char *der, char **err);
6970

7071
int ngx_http_lua_ffi_ssl_get_tls1_version(ngx_http_request_t *r,
7172
char **err);
@@ -135,7 +136,8 @@ elseif subsystem == 'stream' then
135136
size_t pem_len, unsigned char *der, char **err);
136137

137138
int ngx_stream_lua_ffi_priv_key_pem_to_der(const unsigned char *pem,
138-
size_t pem_len, unsigned char *der, char **err);
139+
size_t pem_len, const unsigned char *passphrase,
140+
unsigned char *der, char **err);
139141

140142
int ngx_stream_lua_ffi_ssl_get_tls1_version(ngx_stream_lua_request_t *r,
141143
char **err);
@@ -330,10 +332,11 @@ function _M.cert_pem_to_der(pem)
330332
end
331333

332334

333-
function _M.priv_key_pem_to_der(pem)
335+
function _M.priv_key_pem_to_der(pem, passphrase)
334336
local outbuf = get_string_buf(#pem)
335337

336-
local sz = ngx_lua_ffi_priv_key_pem_to_der(pem, #pem, outbuf, errmsg)
338+
local sz = ngx_lua_ffi_priv_key_pem_to_der(pem, #pem,
339+
passphrase, outbuf, errmsg)
337340
if sz > 0 then
338341
return ffi_str(outbuf, sz)
339342
end

lib/ngx/ssl.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,9 @@ server {
8989
-- assuming the user already defines the my_load_private_key()
9090
-- function herself.
9191
local pem_pkey = assert(my_load_private_key())
92+
local passphrase = "password" -- or nil
9293
93-
local der_pkey, err = ssl.priv_key_pem_to_der(pem_pkey)
94+
local der_pkey, err = ssl.priv_key_pem_to_der(pem_pkey, passphrase)
9495
if not der_pkey then
9596
ngx.log(ngx.ERR, "failed to convert private key ",
9697
"from PEM to DER: ", err)
@@ -188,14 +189,16 @@ function to do the conversion first.
188189

189190
priv_key_pem_to_der
190191
-------------------
191-
**syntax:** *der_priv_key, err = ssl.priv_key_pem_to_der(pem_priv_key)*
192+
**syntax:** *der_priv_key, err = ssl.priv_key_pem_to_der(pem_priv_key, passphrase)*
192193

193194
**context:** *any*
194195

195196
Converts the PEM-formatted SSL private key data into the DER format (for later uses
196197
in the [set_der_priv_key](#set_der_priv_key)
197198
function, for example).
198199

200+
The `passphrase` is the passphrase for `pem_priv_key` if the private key is password protected.
201+
199202
In case of failures, returns `nil` and a string describing the error.
200203

201204
Alternatively, you can do the PEM to DER conversion *offline* with the `openssl` command-line utility, like below

t/cert/test_passphrase.crt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIICozCCAgwCCQDEutRdSs3vZjANBgkqhkiG9w0BAQUFADCBlDELMAkGA1UEBhMC
3+
Q04xEjAQBgNVBAgMCUd1YW5nZG9uZzERMA8GA1UEBwwIU2hlblpoZW4xEjAQBgNV
4+
BAoMCU9wZW5SZXN0eTESMBAGA1UECwwJT3BlblJlc3R5MREwDwYDVQQDDAh0ZXN0
5+
LmNvbTEjMCEGCSqGSIb3DQEJARYUZ3VhbmdsaW5sdkBnbWFpbC5jb20wIBcNMTYw
6+
NDI4MTQ0MzI4WhgPMjE1MTAzMjcxNDQzMjhaMIGUMQswCQYDVQQGEwJDTjESMBAG
7+
A1UECAwJR3Vhbmdkb25nMREwDwYDVQQHDAhTaGVuWmhlbjESMBAGA1UECgwJT3Bl
8+
blJlc3R5MRIwEAYDVQQLDAlPcGVuUmVzdHkxETAPBgNVBAMMCHRlc3QuY29tMSMw
9+
IQYJKoZIhvcNAQkBFhRndWFuZ2xpbmx2QGdtYWlsLmNvbTCBnzANBgkqhkiG9w0B
10+
AQEFAAOBjQAwgYkCgYEA2KZ+HdH9R2tarxD8PKqu5EYq2BNGlFRg1xJmrw0XZBRM
11+
UP/VPb+sIeioooz36uhiXfQjExlpBCA/0zNAN+HbFyqpPPTf1qLGrj/dqeE4MJaN
12+
Bwzxiv3fZnENT65u2qbiFWIY+ATNHgA20d50nxNNjPTzLbkx/nYXL92r4kuAGk0C
13+
AwEAATANBgkqhkiG9w0BAQUFAAOBgQCfMo0qbcs3kwl1tcNBO5hCcUUJRzyv041V
14+
ff/nZ/JPIMo/LSZd12K82G/dLRN7uRT9nzqtm+JRkHALHWWWFKi6bdg1vcdOTWqC
15+
08bCkJHQoXJQQLvvA6gNvnR+0b7L4CrCmrcyYgKDLXVGNP9Wv/PqSWWbxsmqngkA
16+
Mvy6CVytFw==
17+
-----END CERTIFICATE-----

t/cert/test_passphrase.key

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
-----BEGIN RSA PRIVATE KEY-----
2+
Proc-Type: 4,ENCRYPTED
3+
DEK-Info: DES-EDE3-CBC,679ACC8E69ACAA92
4+
5+
Ssrjp3VU4somCNPiXkWqcudDnvnwbyj/Q0pS07at3lXKbhQSgI1Tzhg9Pm3BXXj5
6+
mkLdeGG5ocrj1Q9dhtmZgZeHHQIiynZBhjBu1Y+HPef8jXOWLrCOi8EKiWkJ2qG3
7+
V1KFM/95CcDt0mRLykUXEL3IpUst05SFb9XwiLokB7ypeu3NhgNUHjL6G+ubB4ri
8+
TOUjCW4pEoNHjdC22IiqSncwCVhluYSGhr6ktHKehZMhYIXmL1wmSLdhTlsPXCQl
9+
xvYILQ2vJcKIR1BkeYYPD/OQC6zCZlXIErzfgeZiz2+NTudKYpb9VmsQKsO+R8L7
10+
tZ/fNaR0vk8bbimMHgStAV4acVsC/7WxsqOjMJ8VTq1iqhYPl6N7kRdR3H3kSSOm
11+
cN9T3SrOHDVaHbnWgToaOE4mKFjvFSLIOcWgus0iOHWXmY+SLG+Ndag3oVB6R9oB
12+
cAHX19mq99+GhzA8IV4I0En2UCKQhnGPvkM+9mcCDxhRETlwncDjlMGOHpQ65J9r
13+
eReVPIpnDkvHxPGTtsR3ZHTdWTZb+C0W2N3QIlJKrOzxFmfoj++yG3tMX42aDY0g
14+
DVkrXgcKobiWN0AVrJNAwfG7uObKSCFYgz/0RRMCO4cjXRW99nxdjVDZhyc6R0Te
15+
jzuF04okkOLNb25n2hP+yIULrn+6Nv/uHtFI0j0n3hOzcKh//dNbACSAKgkHni9g
16+
JKDFJXgLJxf+Wc3So0DF9gYMKJJ+WbcdVT9gkC7RyQHlC90Pn7kNXzHr0ZawUsNI
17+
ZxSkL4dMhYAfA4lUBJbOkwbSurv97LinOSRffpM0Nmf7VNw/Ue15eg==
18+
-----END RSA PRIVATE KEY-----

t/ssl.t

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2705,3 +2705,135 @@ read server port from Lua: nilunix domain has no port
27052705
[error]
27062706
[alert]
27072707
[emerg]
2708+
2709+
2710+
2711+
=== TEST 28: PEM key protected by passphrase
2712+
--- http_config
2713+
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
2714+
2715+
server {
2716+
listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
2717+
server_name test.com;
2718+
ssl_certificate_by_lua_block {
2719+
local ssl = require "ngx.ssl"
2720+
2721+
ssl.clear_certs()
2722+
2723+
local f = assert(io.open("t/cert/test_passphrase.crt"))
2724+
local cert_data = f:read("*a")
2725+
f:close()
2726+
2727+
local cert, err = ssl.cert_pem_to_der(cert_data)
2728+
if not cert then
2729+
ngx.log(ngx.ERR, "failed to convert pem cert to der cert: ", err)
2730+
return
2731+
end
2732+
2733+
local ok, err = ssl.set_der_cert(cert)
2734+
if not ok then
2735+
ngx.log(ngx.ERR, "failed to set DER cert: ", err)
2736+
return
2737+
end
2738+
2739+
local f = assert(io.open("t/cert/test_passphrase.key"))
2740+
local pkey_data = f:read("*a")
2741+
f:close()
2742+
2743+
pkey_data, err = ssl.priv_key_pem_to_der(pkey_data, "123456")
2744+
if not pkey_data then
2745+
ngx.log(ngx.ERR, "failed to convert pem key to der key: ", err)
2746+
return
2747+
end
2748+
local ok, err = ssl.set_der_priv_key(pkey_data)
2749+
if not ok then
2750+
ngx.log(ngx.ERR, "failed to set private key: ", err)
2751+
return
2752+
end
2753+
}
2754+
ssl_certificate ../../cert/test2.crt;
2755+
ssl_certificate_key ../../cert/test2.key;
2756+
2757+
server_tokens off;
2758+
location /foo {
2759+
default_type 'text/plain';
2760+
content_by_lua_block { ngx.status = 201 ngx.say("foo") ngx.exit(201) }
2761+
more_clear_headers Date;
2762+
}
2763+
}
2764+
--- config
2765+
server_tokens off;
2766+
lua_ssl_trusted_certificate ../../cert/chain/root-ca.crt;
2767+
lua_ssl_verify_depth 3;
2768+
2769+
location /t {
2770+
content_by_lua_block {
2771+
do
2772+
local sock = ngx.socket.tcp()
2773+
2774+
sock:settimeout(3000)
2775+
2776+
local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock")
2777+
if not ok then
2778+
ngx.say("failed to connect: ", err)
2779+
return
2780+
end
2781+
2782+
ngx.say("connected: ", ok)
2783+
2784+
local sess, err = sock:sslhandshake(nil, "test.com", false)
2785+
if not sess then
2786+
ngx.say("failed to do SSL handshake: ", err)
2787+
return
2788+
end
2789+
2790+
ngx.say("ssl handshake: ", type(sess))
2791+
2792+
local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n"
2793+
local bytes, err = sock:send(req)
2794+
if not bytes then
2795+
ngx.say("failed to send http request: ", err)
2796+
return
2797+
end
2798+
2799+
ngx.say("sent http request: ", bytes, " bytes.")
2800+
2801+
while true do
2802+
local line, err = sock:receive()
2803+
if not line then
2804+
-- ngx.say("failed to receive response status line: ", err)
2805+
break
2806+
end
2807+
2808+
ngx.say("received: ", line)
2809+
end
2810+
2811+
local ok, err = sock:close()
2812+
ngx.say("close: ", ok, " ", err)
2813+
end -- do
2814+
-- collectgarbage()
2815+
}
2816+
}
2817+
2818+
--- request
2819+
GET /t
2820+
--- response_body
2821+
connected: 1
2822+
ssl handshake: userdata
2823+
sent http request: 56 bytes.
2824+
received: HTTP/1.1 201 Created
2825+
received: Server: nginx
2826+
received: Content-Type: text/plain
2827+
received: Content-Length: 4
2828+
received: Connection: close
2829+
received:
2830+
received: foo
2831+
close: 1 nil
2832+
2833+
--- error_log
2834+
lua ssl server name: "test.com"
2835+
2836+
--- no_error_log
2837+
[error]
2838+
[alert]
2839+
[emerg]

t/stream/ssl.t

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2034,3 +2034,109 @@ client certificate subject: nil
20342034
[error]
20352035
[alert]
20362036
[emerg]
2037+
2038+
2039+
2040+
=== TEST 26: private key protected by passphrase
2041+
--- stream_config
2042+
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
2043+
2044+
server {
2045+
listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
2046+
ssl_certificate_by_lua_block {
2047+
local ssl = require "ngx.ssl"
2048+
2049+
ssl.clear_certs()
2050+
2051+
local f = assert(io.open("t/cert/test_passphrase.crt"))
2052+
local cert_data = f:read("*a")
2053+
f:close()
2054+
2055+
local cert, err = ssl.cert_pem_to_der(cert_data)
2056+
if not cert then
2057+
ngx.log(ngx.ERR, "failed to convert pem cert to der cert: ", err)
2058+
return
2059+
end
2060+
2061+
local ok, err = ssl.set_der_cert(cert)
2062+
if not ok then
2063+
ngx.log(ngx.ERR, "failed to set DER cert: ", err)
2064+
return
2065+
end
2066+
2067+
local f = assert(io.open("t/cert/test_passphrase.key"))
2068+
local pkey_data = f:read("*a")
2069+
f:close()
2070+
2071+
pkey_data, err = ssl.priv_key_pem_to_der(pkey_data, "123456")
2072+
if not pkey_data then
2073+
ngx.log(ngx.ERR, "failed to convert pem key to der key: ", err)
2074+
return
2075+
end
2076+
2077+
local ok, err = ssl.set_der_priv_key(pkey_data)
2078+
if not ok then
2079+
ngx.log(ngx.ERR, "failed to set DER private key: ", err)
2080+
return
2081+
end
2082+
}
2083+
ssl_certificate ../../cert/test2.crt;
2084+
ssl_certificate_key ../../cert/test2.key;
2085+
2086+
return 'it works!\n';
2087+
}
2088+
--- stream_server_config
2089+
lua_ssl_trusted_certificate ../../cert/chain/root-ca.crt;
2090+
lua_ssl_verify_depth 3;
2091+
2092+
content_by_lua_block {
2093+
do
2094+
local sock = ngx.socket.tcp()
2095+
2096+
sock:settimeout(3000)
2097+
2098+
local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock")
2099+
if not ok then
2100+
ngx.say("failed to connect: ", err)
2101+
return
2102+
end
2103+
2104+
ngx.say("connected: ", ok)
2105+
2106+
local sess, err = sock:sslhandshake(nil, "test.com")
2107+
if not sess then
2108+
ngx.say("failed to do SSL handshake: ", err)
2109+
return
2110+
end
2111+
2112+
ngx.say("ssl handshake: ", type(sess))
2113+
2114+
while true do
2115+
local line, err = sock:receive()
2116+
if not line then
2117+
-- ngx.say("failed to receive response status line: ", err)
2118+
break
2119+
end
2120+
2121+
ngx.say("received: ", line)
2122+
end
2123+
2124+
local ok, err = sock:close()
2125+
ngx.say("close: ", ok, " ", err)
2126+
end -- do
2127+
-- collectgarbage()
2128+
}
2129+
2130+
--- stream_response
2131+
connected: 1
2132+
ssl handshake: userdata
2133+
received: it works!
2134+
close: 1 nil
2135+
2136+
--- error_log
2137+
lua ssl server name: "test.com"
2138+
2139+
--- no_error_log
2140+
[error]
2141+
[alert]
2142+
[emerg]

0 commit comments

Comments
 (0)