Skip to content

Commit 7d8e81f

Browse files
committed
feat(x509.extension) support create by ASN.1 octet string and nconf
1 parent 6143659 commit 7d8e81f

File tree

9 files changed

+252
-25
lines changed

9 files changed

+252
-25
lines changed

README.md

+76-13
Original file line numberDiff line numberDiff line change
@@ -1557,6 +1557,12 @@ finds from beginning. Index is 1-based.
15571557

15581558
```lua
15591559
local ext, pos, err = x509:get_extension("keyUsage")
1560+
ngx.say(ext:text())
1561+
-- outputs "Digital Signature, Key Encipherment"
1562+
1563+
local ext, pos, err = x509:get_extension("subjectKeyIdentifier")
1564+
ngx.say(ext:text())
1565+
-- outputs "3D:42:13:57:8F:79:BE:30:7D:86:A9:AC:67:50:E5:56:3E:0E:AF:4F"
15601566
```
15611567

15621568
[Back to TOC](#table-of-contents)
@@ -1569,9 +1575,9 @@ Adds an X.509 `extension` to certificate, the first argument must be a
15691575
[resty.openssl.x509.extension](#restyopensslx509extension) instance.
15701576

15711577
```lua
1572-
local extension, err = require("resty.openssl.extension").new({
1573-
"keyUsage", "critical,keyCertSign,cRLSign",
1574-
})
1578+
local extension, err = require("resty.openssl.x509.extension").new(
1579+
"keyUsage", "critical,keyCertSign,cRLSign"
1580+
)
15751581
local x509, err = require("resty.openssl.x509").new()
15761582
local ok, err = x509:add_extension(extension)
15771583
```
@@ -2261,26 +2267,67 @@ Module to interact with X.509 extensions.
22612267
Creates a new `extension` instance. `name` and `value` are strings in OpenSSL
22622268
[arbitrary extension format](https://www.openssl.org/docs/manmaster/man5/x509v3_config.html).
22632269

2264-
`data` can be a table or nil. Where data is a table, the following key will be looked up:
2270+
`data` can be a table, string or nil. Where `data` is a table, the following key will be looked up:
22652271

22662272
```lua
22672273
data = {
2268-
issuer = resty.openssl.x509 instance,
2269-
subject = resty.openssl.x509 instance,
2270-
request = resty.openssl.x509.csr instance,
2271-
crl = resty.openssl.x509.crl instance,
2274+
issuer = resty.openssl.x509 instance,
2275+
subject = resty.openssl.x509 instance,
2276+
request = resty.openssl.x509.csr instance,
2277+
crl = resty.openssl.x509.crl instance,
22722278
}
22732279
```
22742280

2275-
Example:
2281+
When `data` is a string, it's the full nconf string. Using section lookup from `value` to
2282+
`data` is also supported.
2283+
2284+
<details>
2285+
<summary>Example usages:</summary>
2286+
22762287
```lua
2277-
local x509, err = require("resty.openssl.x509").new()
22782288
local extension = require("resty.openssl.x509.extension")
2289+
-- extendedKeyUsage=serverAuth,clientAuth
22792290
local ext, err = extension.new("extendedKeyUsage", "serverAuth,clientAuth")
2291+
-- crlDistributionPoints=URI:http://myhost.com/myca.crl
2292+
ext, err = extension.new("crlDistributionPoints", "URI:http://myhost.com/myca.crl")
2293+
-- with section lookup
2294+
ext, err = extension.new(
2295+
"crlDistributionPoints", "crldp1_section",
2296+
[[
2297+
[crldp1_section]
2298+
fullname=URI:http://myhost.com/myca.crl
2299+
CRLissuer=dirName:issuer_sect
2300+
reasons=keyCompromise, CACompromise
2301+
2302+
[issuer_sect]
2303+
C=UK
2304+
O=Organisation
2305+
CN=Some Name
2306+
]]
2307+
)
2308+
-- combine section lookup with other value
2309+
ext, err = extension.new(
2310+
"certificatePolicies", "ia5org,1.2.3.4,1.5.6.7.8,@polsect",
2311+
[[
2312+
[polsect]
2313+
policyIdentifier = 1.3.5.8
2314+
CPS.1="http://my.host.name/"
2315+
CPS.2="http://my.your.name/"
2316+
userNotice.1=@notice
2317+
2318+
[notice]
2319+
explicitText="Explicit Text Here"
2320+
organization="Organisation Name"
2321+
noticeNumbers=1,2,3,4
2322+
]]
2323+
))
2324+
-- subjectKeyIdentifier=hash
2325+
local x509, err = require("resty.openssl.x509").new()
22802326
ext, err = extension.new("subjectKeyIdentifier", "hash", {
2281-
subject = crt
2327+
subject = x509
22822328
})
22832329
```
2330+
</details>
22842331

22852332
See [examples/tls-alpn-01.lua](https://github.com/fffonion/lua-resty-openssl/blob/master/examples/tls-alpn-01.lua)
22862333
for an example to create extension with an unknown nid.
@@ -2295,17 +2342,33 @@ Creates a new `extension` instance from `X509_EXTENSION*` pointer.
22952342

22962343
[Back to TOC](#table-of-contents)
22972344

2345+
### extension.from_der
2346+
2347+
**syntax**: *ext, ok = extension.from_der(der, nid_or_txt, crit?)*
2348+
2349+
Creates a new `extension` instance. `der` is the ASN.1 encoded string to be
2350+
set for the extension.
2351+
2352+
`nid_or_txt` is a number or text representation of [NID] and
2353+
`crit` is the critical flag of the extension.
2354+
2355+
See [examples/tls-alpn-01.lua](https://github.com/fffonion/lua-resty-openssl/blob/master/examples/tls-alpn-01.lua)
2356+
for an example to create extension with an unknown nid.
2357+
2358+
[Back to TOC](#table-of-contents)
2359+
22982360
### extension.from_data
22992361

2300-
**syntax**: *ext, ok = extension.from_data(table, nid, crit?)*
2362+
**syntax**: *ext, ok = extension.from_data(table, nid_or_txt, crit?)*
23012363

23022364
Creates a new `extension` instance. `table` can be instance of:
23032365

23042366
- [x509.altname](#restyopensslx509altname)
23052367
- [x509.extension.info_access](#restyopensslx509extensioninfo_access)
23062368
- [x509.extension.dist_points](#restyopensslx509extensiondist_points)
23072369

2308-
`nid` is a number of [NID] and `crit` is the critical flag of the extension.
2370+
`nid_or_txt` is a number or text representation of [NID] and
2371+
`crit` is the critical flag of the extension.
23092372

23102373
[Back to TOC](#table-of-contents)
23112374

examples/tls-alpn-01.lua

+15-6
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,27 @@ end
2222
-- with given domain name and challenge token
2323
function serve_challenge_cert(domain, challenge)
2424
local dgst = assert(digest.new("sha256"):final(challenge))
25+
26+
-- There're two ways to set ASN.1 octect string to the extension
27+
-- The recommanded way is to pass the string directly to extension.from_der()
28+
local ext, err = extension.from_der(dgst, nid, true)
29+
if err then
30+
return nil, nil, err
31+
end
32+
-- OR we put the ASN.1 signature for this string by ourselves
2533
-- 0x04: OCTET STRING
2634
-- 0x20: length
27-
dgst = "DER:0420" .. dgst:gsub("(.)", function(s) return string.format("%02x", string.byte(s)) end)
28-
29-
local key = pkey.new()
30-
local cert = x509.new()
31-
cert:set_pubkey(key)
32-
local ext, err = extension.new(nid, dgst)
35+
local dgst_hex = "DER:0420" .. dgst:gsub("(.)", function(s) return string.format("%02x", string.byte(s)) end)
36+
local ext, err = extension.new(nid, dgst_hex)
3337
if err then
3438
return nil, nil, err
3539
end
3640
ext:set_critical(true)
41+
42+
local key = pkey.new()
43+
local cert = x509.new()
44+
cert:set_pubkey(key)
45+
3746
cert:add_extension(ext)
3847

3948
local alt = assert(altname.new():add(

lib/resty/openssl/include/conf.lua

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
local ffi = require "ffi"
2+
3+
require "resty.openssl.include.ossl_typ"
4+
5+
ffi.cdef [[
6+
CONF *NCONF_new(CONF_METHOD *meth);
7+
void NCONF_free(CONF *conf);
8+
int NCONF_load_bio(CONF *conf, BIO *bp, long *eline);
9+
]]

lib/resty/openssl/include/ossl_typ.lua

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ ffi.cdef(
4343
typedef struct asn1_string_st ASN1_STRING;
4444
typedef struct asn1_object_st ASN1_OBJECT;
4545
typedef struct conf_st CONF;
46+
typedef struct conf_method_st CONF_METHOD;
4647
typedef int ASN1_BOOLEAN;
4748
typedef int ASN1_NULL;
4849
typedef struct ec_key_st EC_KEY;

lib/resty/openssl/include/x509/init.lua

+3
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ ffi.cdef [[
3333
ASN1_OBJECT *X509_EXTENSION_get_object(X509_EXTENSION *ex);
3434
ASN1_OCTET_STRING *X509_EXTENSION_get_data(X509_EXTENSION *ne);
3535
X509_EXTENSION *X509V3_EXT_i2d(int ext_nid, int crit, void *ext_struc);
36+
X509_EXTENSION *X509_EXTENSION_create_by_NID(X509_EXTENSION **ex,
37+
int nid, int crit,
38+
ASN1_OCTET_STRING *data);
3639

3740
// needed by pkey
3841
EVP_PKEY *d2i_PrivateKey_bio(BIO *bp, EVP_PKEY **a);

lib/resty/openssl/include/x509v3.lua

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ ffi.cdef [[
1313
// STACK_OF(OPENSSL_STRING)
1414
OPENSSL_STACK *X509_get1_ocsp(X509 *x);
1515
void X509_email_free(OPENSSL_STACK *sk);
16+
void X509V3_set_nconf(X509V3_CTX *ctx, CONF *conf);
1617

1718
typedef struct EDIPartyName_st EDIPARTYNAME;
1819

lib/resty/openssl/x509/extension.lua

+85-6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ local ffi_cast = ffi.cast
66

77
require "resty.openssl.include.x509"
88
require "resty.openssl.include.x509.extension"
9+
require "resty.openssl.include.x509v3"
10+
require "resty.openssl.include.asn1"
11+
require "resty.openssl.include.bio"
12+
require "resty.openssl.include.conf"
913
local objects_lib = require "resty.openssl.objects"
1014
local stack_lib = require("resty.openssl.stack")
1115
local util = require "resty.openssl.util"
@@ -21,21 +25,49 @@ local extension_types = {
2125
subject = "resty.openssl.x509",
2226
request = "resty.openssl.x509.csr",
2327
crl = "resty.openssl.x509.crl",
24-
-- db, -- NYI
2528
}
29+
30+
local function nconf_load(conf, str)
31+
local bio = C.BIO_new_mem_buf(str, #str)
32+
if bio == nil then
33+
return format_error("BIO_new_mem_buf")
34+
end
35+
ffi_gc(bio, C.BIO_free)
36+
37+
if C.NCONF_load_bio(conf, bio, nil) ~= 1 then
38+
return format_error("NCONF_load_bio")
39+
end
40+
end
41+
2642
function _M.new(txtnid, value, data)
2743
local nid, err = objects_lib.txtnid2nid(txtnid)
2844
if err then
29-
return nil, err
45+
return nil, "x509.extension.new: " .. err
3046
end
3147
if type(value) ~= 'string' then
3248
return nil, "x509.extension.new: expect string at #2"
3349
end
3450
-- get a ptr and also zerofill the struct
3551
local x509_ctx_ptr = ffi_new('X509V3_CTX[1]')
3652

53+
local conf = C.NCONF_new(nil)
54+
if conf == nil then
55+
return nil, format_error("NCONF_new")
56+
end
57+
ffi_gc(conf, C.NCONF_free)
58+
3759
if type(data) == 'table' then
3860
local args = {}
61+
if data.db then
62+
if type(data.db) ~= 'string' then
63+
return nil, "x509.extension.new: expect data.db must be a string"
64+
end
65+
err = nconf_load(conf, data)
66+
if err then
67+
return nil, "x509.extension.new: " .. err
68+
end
69+
end
70+
3971
for k, t in pairs(extension_types) do
4072
if data[k] then
4173
local lib = require(t)
@@ -45,12 +77,21 @@ function _M.new(txtnid, value, data)
4577
args[k] = data[k].ctx
4678
end
4779
end
48-
C.X509V3_set_ctx(x509_ctx_ptr[0], args.issuer, args.subject, args.request, nil, 0)
80+
C.X509V3_set_ctx(x509_ctx_ptr[0], args.issuer, args.subject, args.request, args.crl, 0)
81+
elseif type(data) == 'string' then
82+
err = nconf_load(conf, data)
83+
if err then
84+
return nil, "x509.extension.new: " .. err
85+
end
4986
elseif data then
50-
return nil, "x509.extension.new: expect nil or a table at #3"
87+
return nil, "x509.extension.new: expect nil, string a table at #3"
5188
end
5289

53-
local ctx = C.X509V3_EXT_nconf_nid(nil, x509_ctx_ptr[0], nid, value)
90+
-- setting conf is required for some extensions to load
91+
-- crypto/x509/v3_conf.c:do_ext_conf "else if (method->r2i) {" branch
92+
C.X509V3_set_nconf(x509_ctx_ptr[0], conf)
93+
94+
local ctx = C.X509V3_EXT_nconf_nid(conf, x509_ctx_ptr[0], nid, value)
5495
if ctx == nil then
5596
return nil, format_error("x509.extension.new")
5697
end
@@ -85,7 +126,45 @@ function _M.dup(ctx)
85126
return self, nil
86127
end
87128

88-
function _M.from_data(any, nid, crit)
129+
function _M.from_der(value, txtnid, crit)
130+
local nid, err = objects_lib.txtnid2nid(txtnid)
131+
if err then
132+
return nil, "x509.extension.from_der: " .. err
133+
end
134+
if type(value) ~= 'string' then
135+
return nil, "x509.extension.from_der: expect string at #1"
136+
end
137+
138+
local asn1 = C.ASN1_STRING_new()
139+
if asn1 == nil then
140+
return nil, format_error("x509.extension.from_der: ASN1_STRING_new")
141+
end
142+
ffi_gc(asn1, C.ASN1_STRING_free)
143+
144+
if C.ASN1_STRING_set(asn1, value, #value) ~= 1 then
145+
return nil, format_error("x509.extension.from_der: ASN1_STRING_set")
146+
end
147+
148+
local ctx = C.X509_EXTENSION_create_by_NID(nil, nid, crit and 1 or 0, asn1)
149+
if ctx == nil then
150+
return nil, format_error("x509.extension.from_der: X509_EXTENSION_create_by_NID")
151+
end
152+
ffi_gc(ctx, C.X509_EXTENSION_free)
153+
154+
local self = setmetatable({
155+
ctx = ctx,
156+
}, mt)
157+
158+
return self, nil
159+
160+
end
161+
162+
function _M.from_data(any, txtnid, crit)
163+
local nid, err = objects_lib.txtnid2nid(txtnid)
164+
if err then
165+
return nil, "x509.extension.from_der: " .. err
166+
end
167+
89168
if type(any) ~= "table" or type(any.ctx) ~= "cdata" then
90169
return nil, "x509.extension.from_data: expect a table with ctx at #1"
91170
elseif type(nid) ~= "number" then

lua-resty-openssl-0.6.5-1.rockspec

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ build = {
2626
["resty.openssl.include.asn1"] = "lib/resty/openssl/include/asn1.lua",
2727
["resty.openssl.include.bio"] = "lib/resty/openssl/include/bio.lua",
2828
["resty.openssl.include.bn"] = "lib/resty/openssl/include/bn.lua",
29+
["resty.openssl.include.conf"] = "lib/resty/openssl/include/conf.lua",
2930
["resty.openssl.include.crypto"] = "lib/resty/openssl/include/crypto.lua",
3031
["resty.openssl.include.ec"] = "lib/resty/openssl/include/ec.lua",
3132
["resty.openssl.include.evp"] = "lib/resty/openssl/include/evp.lua",

0 commit comments

Comments
 (0)