diff --git a/CHANGELOG.md b/CHANGELOG.md index f2385672a..115933b75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added the `APICAST_PROXY_BUFFER_SIZE` variable to allow configuration of the buffer size for handling response from the proxied servers. [PR #1473](https://github.com/3scale/APIcast/pull/1473), [THREESCALE-8410](https://issues.redhat.com/browse/THREESCALE-8410) - Added the `APICAST_HTTPS_VERIFY_CLIENT` variable to allow configuration of the `ssl_verify_client` directive. [PR #1491](https://github.com/3scale/APIcast/pull/1491) [THREESCALE-10156](https://issues.redhat.com/browse/THREESCALE-10156) +- Replace internal OPENSSL module with lua-resty-openssl [PR #1502](https://github.com/3scale/APIcast/pull/1502) [THREESCALE-11412](https://issues.redhat.com/browse/THREESCALE-11412) ## [3.15.0] 2024-04-04 diff --git a/Dockerfile b/Dockerfile index 3e6a33319..9719cb222 100644 --- a/Dockerfile +++ b/Dockerfile @@ -61,6 +61,7 @@ RUN luarocks install --deps-mode=none --tree /usr/local https://luarocks.org/man RUN luarocks install --deps-mode=none --tree /usr/local https://luarocks.org/manifests/hamish/lua-resty-iputils-0.3.0-1.src.rock RUN luarocks install --deps-mode=none --tree /usr/local https://luarocks.org/manifests/golgote/net-url-0.9-1.src.rock RUN luarocks install --deps-mode=none --tree /usr/local https://luarocks.org/manifests/membphis/lua-resty-ipmatcher-0.6.1-0.src.rock +RUN luarocks install --deps-mode=none --tree /usr/local https://luarocks.org/manifests/fffonion/lua-resty-openssl-1.5.1-1.src.rock RUN yum -y remove libyaml-devel m4 openssl-devel git gcc luarocks && \ rm -rf /var/cache/yum && yum clean all -y && \ @@ -93,7 +94,7 @@ WORKDIR /opt/app-root/app USER 1001 ENV LUA_CPATH "./?.so;/usr/lib64/lua/5.1/?.so;/usr/lib64/lua/5.1/loadall.so;/usr/local/lib64/lua/5.1/?.so" -ENV LUA_PATH "/usr/lib64/lua/5.1/?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/*/?.lua;" +ENV LUA_PATH "/usr/lib64/lua/5.1/?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/*/?.lua;;" WORKDIR /opt/app-root ENTRYPOINT ["container-entrypoint"] diff --git a/gateway/Roverfile.lock b/gateway/Roverfile.lock index 77848e70f..f20d26b7c 100644 --- a/gateway/Roverfile.lock +++ b/gateway/Roverfile.lock @@ -1,32 +1,33 @@ argparse 0.7.1-1||production -busted 2.2.0-1||testing -date 2.2-2||production +busted 2.2.0-1|02f31a9c103a44e166617cfdb6ba1b8994a9c912|testing +date 2.2-2|8d74567cf979c1eab2c6b6ca2e3b978fa40569a2|production dkjson 2.8-1||testing fifo 0.2-0||development inspect 3.1.3-0||production jsonschema 0.8-0|c1d72d86bb3dc5b33da57d47febc47657d29ea74|testing -ldoc 1.5.0-1||development +ldoc 1.5.0-1|09f82c959c50d8c3d5a968c379b1c75de66b002d|development liquid 0.2.0-2||production lua-resty-env 0.4.0-1||production lua-resty-execvp 0.1.1-1||production -lua-resty-http 0.17.1-0||production -lua-resty-ipmatcher 0.6.1-0||production -lua-resty-iputils 0.3.0-2||production -lua-resty-jit-uuid 0.0.7-2||production -lua-resty-jwt 0.2.0-0||production +lua-resty-http 0.17.1-0|4ab4269cf442ba52507aa2c718f606054452fcad|production +lua-resty-ipmatcher 0.6.1-0|62d4c44d67227e8f3fe02331c2f8b90fe0d7ccd1|production +lua-resty-iputils 0.3.0-2|6110b41eaa52efd25e56f89e34412ab95f700d57|production +lua-resty-jit-uuid 0.0.7-2|64ae38de75c9d58f330d89e140ac872771c19223|production +lua-resty-jwt 0.2.0-0|2a62ff95eae91df6bd8655080a4b9b04c61bec6b|production +lua-resty-openssl 1.5.1-1|a900c5f5897448c181dd58073e51cdeeb3fd0029|production lua-resty-repl 0.0.6-0|3878f41b7e8f97b1c96919db19dbee9496569dda|development lua-resty-url 0.3.5-1||production lua-term 0.8-1||testing lua_cliargs 3.0-2||testing -luacov 0.15.0-1||testing -luafilesystem 1.8.0-1||production,development,testing -luassert 1.9.0-1||testing -luasystem 0.4.1-1||testing +luacov 0.15.0-1|19b52ca0298c8942df82dd441d7a4a588db4c413|testing +luafilesystem 1.8.0-1|7c6e1b013caec0602ca4796df3b1d7253a2dd258|production,development,testing +luassert 1.9.0-1|8d8dc8a54cc468048a128a867f6449a6c3fdd11a|testing +luasystem 0.4.1-1|c832d2b857d4174d17247de837426d4cfc95ec2f|testing lyaml 6.2.8-1||production -markdown 0.33-1||development +markdown 0.33-1|8c09109924b218aaecbfd4d4b1de538269c4d765|development mediator_lua 1.1.2-0||testing -net-url 1.1-1||testing -nginx-lua-prometheus 0.20181120-3||production -penlight 1.13.1-1||production,development,testing +net-url 1.1-1|32acd84d06e16ddffc975adafce9cea26f3b2dd1|testing +nginx-lua-prometheus 0.20181120-3|379c0a4d4d6f3c5b0eb93691fc7e14fff498e1ca|production +penlight 1.13.1-1|3b42fd05d8e60998b0fc5830a397b2354a81170b|production,development,testing router 2.1-0||production -say 1.4.1-3||testing \ No newline at end of file +say 1.4.1-3|45a3057e68c52b34ab59ef167efeb2340e356661|testing \ No newline at end of file diff --git a/gateway/apicast-scm-1.rockspec b/gateway/apicast-scm-1.rockspec index e6477cd75..e4c019c36 100644 --- a/gateway/apicast-scm-1.rockspec +++ b/gateway/apicast-scm-1.rockspec @@ -24,6 +24,7 @@ dependencies = { 'nginx-lua-prometheus == 0.20181120', 'lua-resty-jit-uuid', 'lua-resty-ipmatcher', + 'lua-resty-openssl' } build = { type = "make", diff --git a/gateway/src/apicast/policy/fapi/fapi.lua b/gateway/src/apicast/policy/fapi/fapi.lua index ce908f47a..fd597816f 100644 --- a/gateway/src/apicast/policy/fapi/fapi.lua +++ b/gateway/src/apicast/policy/fapi/fapi.lua @@ -69,7 +69,7 @@ function _M:access(context) if self.validate_oauth2_certificate_bound_access_token then if not context.jwt then return error(context.service or {}) end - local cert = X509.parse_pem_cert(ngx.var.ssl_client_raw_cert) + local cert = X509.new(ngx.var.ssl_client_raw_cert) if not check_certificate(cert, context.jwt.cnf) then ngx.log(ngx.WARN, 'fapi oauth_mtls failed for service ', context.service and context.service.id) diff --git a/gateway/src/apicast/policy/oauth_mtls/oauth_mtls.lua b/gateway/src/apicast/policy/oauth_mtls/oauth_mtls.lua index b6b0bb40d..82f901619 100644 --- a/gateway/src/apicast/policy/oauth_mtls/oauth_mtls.lua +++ b/gateway/src/apicast/policy/oauth_mtls/oauth_mtls.lua @@ -33,7 +33,7 @@ end function _M.access(_, context) if not context.jwt then return error(context.service or {}) end - local cert = X509.parse_pem_cert(ngx.var.ssl_client_raw_cert) + local cert = X509.new(ngx.var.ssl_client_raw_cert) if not check_certificate(cert, context.jwt.cnf) then return error(context.service or {}) diff --git a/gateway/src/apicast/policy/tls_validation/README.md b/gateway/src/apicast/policy/tls_validation/README.md index 97adb15d2..a67dbc7fa 100644 --- a/gateway/src/apicast/policy/tls_validation/README.md +++ b/gateway/src/apicast/policy/tls_validation/README.md @@ -1,8 +1,9 @@ # TLS Validation policy -This policy can validate TLS Client Certificate against a whitelist. +This policy can validate TLS Client Certificate against a whitelist and crl. Whitelist expects PEM formatted CA or Client certificates. +Revocation List expects PEM formatted certificates. It is not necessary to have the full certificate chain, just partial matches are allowed. For example you can add to the whitelist just leaf client certificates without the whole bundle with a CA certificate. @@ -28,3 +29,20 @@ NOTE: This policy is not compatible with `APICAST_PATH_ROUTING` or `APICAST_PATH } } ``` + +With Certificate Revocation List (CRL) + +``` +{ + "name": "apicast.policy.tls_validation", + "configuration": { + "whitelist": [ + { "pem_certificate": ""-----BEGIN CERTIFICATE----- XXXXXX -----END CERTIFICATE-----"} + ], + "revocation_check_type": "crl", + "revoke_list": [ + { "pem_certificate": ""-----BEGIN CERTIFICATE----- XXXXXX -----END CERTIFICATE-----"} + ] + } +} +``` diff --git a/gateway/src/apicast/policy/tls_validation/apicast-policy.json b/gateway/src/apicast/policy/tls_validation/apicast-policy.json index 7705ec752..329c13e60 100644 --- a/gateway/src/apicast/policy/tls_validation/apicast-policy.json +++ b/gateway/src/apicast/policy/tls_validation/apicast-policy.json @@ -26,6 +26,13 @@ "items": { "$ref": "#/definitions/certificate" } + }, + "revoke": { + "$id": "#/definitions/revoke", + "type": "array", + "items": { + "$ref": "#/definitions/certificate" + } } }, "properties": { @@ -33,6 +40,60 @@ "$ref": "#/definitions/store", "title": "Certificate Whitelist", "description": "Individual certificates and CA certificates to be whitelisted." + }, + "allow_partial_chain": { + "description": "Allow certificate verification with only an intermediate certificate", + "type": "boolean", + "default": true + }, + "revocation_check_type": { + "title": "Certificate Revocation Check type", + "type": "string", + "oneOf": [ + { + "enum": [ + "crl" + ], + "title": "Use revoked certificates (CRL) in the PEM format to verify client certificates." + }, + { + "enum": [ + "none" + ], + "title": "Do not check for certificate recovation status" + } + ], + "default": "none" + } + }, + "dependencies": { + "revocation_check_type": { + "oneOf": [ + { + "properties": { + "revocation_check_type": { + "describe": "Use the Client credentials and the Token Introspection Endpoint from the OpenID Connect Issuer setting.", + "enum": [ + "none" + ] + } + } + }, + { + "properties": { + "revocation_check_type": { + "enum": [ + "crl" + ] + }, + "revoke_list": { + "title": "Certificate Whitelist", + "description": "Individual certificates and CA certificates to be revoked.", + "$ref": "#/definitions/store" + } + } + } + ] } } } diff --git a/gateway/src/apicast/policy/tls_validation/tls_validation.lua b/gateway/src/apicast/policy/tls_validation/tls_validation.lua index 07a6c619d..a36838432 100644 --- a/gateway/src/apicast/policy/tls_validation/tls_validation.lua +++ b/gateway/src/apicast/policy/tls_validation/tls_validation.lua @@ -4,6 +4,7 @@ local policy = require('apicast.policy') local _M = policy.new('tls_validation') local X509_STORE = require('resty.openssl.x509.store') local X509 = require('resty.openssl.x509') +local X509_CRL = require('resty.openssl.x509.crl') local ngx_ssl = require "ngx.ssl" local ipairs = ipairs @@ -13,10 +14,10 @@ local debug = ngx.config.debug local function init_trusted_store(store, certificates) for _,certificate in ipairs(certificates) do - local cert, err = X509.parse_pem_cert(certificate.pem_certificate) -- TODO: handle errors + local cert, err = X509.new(certificate.pem_certificate) -- TODO: handle errors if cert then - store:add_cert(cert) + store:add(cert) if debug then ngx.log(ngx.DEBUG, 'adding certificate to the tls validation ', tostring(cert:subject_name()), ' SHA1: ', cert:hexdigest('SHA1')) @@ -33,6 +34,29 @@ local function init_trusted_store(store, certificates) return store end +local function init_crl_list(store, crl_certificates) + local ok, err + local crl + for _, certificate in ipairs(crl_certificates) do + -- add crl to store, but skip setting the flag + crl, err = X509_CRL.new(certificate.pem_certificate) + if crl then + ok, err = store:add(crl) + + if debug then + ngx.log(ngx.DEBUG, 'adding crl certificate to the tls validation ', tostring(crl:subject_name()), ' SHA1: ', crl:hexdigest('SHA1')) + end + else + ngx.log(ngx.WARN, 'failed to add crl certificate, err: ', err) + + if debug then + ngx.log(ngx.DEBUG, 'certificate: ', certificate.pem_certificate) + end + end + end + return store +end + local new = _M.new --- Initialize a tls_validation -- @tparam[opt] table config Policy configuration. @@ -42,6 +66,11 @@ function _M.new(config) self.x509_store = init_trusted_store(store, config and config.whitelist or {}) self.error_status = config and config.error_status or 400 + self.allow_partial_chain = config and config.allow_partial_chain or true + self.revocation_type = config and config.revocation_check_type or "none" + if self.revocation_type == "crl" then + init_crl_list(store, config and config.revoke_list or {}) + end return self end @@ -60,26 +89,40 @@ function _M:ssl_certificate() end function _M:access() - local cert = X509.parse_pem_cert(ngx.var.ssl_client_raw_cert) - if not cert then + local client_cert = ngx.var.ssl_client_raw_cert + if not client_cert then ngx.status = self.error_status ngx.say("No required TLS certificate was sent") return ngx.exit(ngx.status) end + local cert, err = X509.new(client_cert) + if not cert then + ngx.status = self.error_status + ngx.log(ngx.WARN, "Invalid TLS certificate, err: ", err) + ngx.say("Invalid TLS certificate") + return ngx.exit(ngx.status) + end + local store = self.x509_store + if self.allow_partial_chain then + store:set_flags(store.verify_flags.X509_V_FLAG_PARTIAL_CHAIN) + end + -- err is printed inside validate_cert method -- so no need capture the err here - local ok, _ = store:validate_cert(cert) + local chain, ok + chain, err = store:verify(cert, nil, true) - if not ok then + if not chain then ngx.status = self.error_status + ngx.log(ngx.WARN, "TLS certificate validation failed, err: ", err) ngx.say("TLS certificate validation failed") return ngx.exit(ngx.status) end - return ok, nil + return true, nil end return _M diff --git a/gateway/src/apicast/policy/upstream_mtls/upstream_mtls.lua b/gateway/src/apicast/policy/upstream_mtls/upstream_mtls.lua index 235ac26e5..da9bbd37c 100644 --- a/gateway/src/apicast/policy/upstream_mtls/upstream_mtls.lua +++ b/gateway/src/apicast/policy/upstream_mtls/upstream_mtls.lua @@ -58,17 +58,17 @@ local function read_ca_certificates(ca_certificates) local valid = false local store = X509_STORE.new() for _,certificate in pairs(ca_certificates) do - local cert, err = X509.parse_pem_cert(certificate) + local cert, err = X509.new(certificate) if cert then valid = true - store:add_cert(cert) + store:add(cert) else ngx.log(ngx.INFO, "cannot load certificate, err: ", err) end end if valid then - return store.store + return store.ctx end end diff --git a/gateway/src/resty/oidc/jwk.lua b/gateway/src/resty/oidc/jwk.lua index e4541e7fd..813f1c1ef 100644 --- a/gateway/src/resty/oidc/jwk.lua +++ b/gateway/src/resty/oidc/jwk.lua @@ -1,127 +1,11 @@ local ipairs = ipairs -local b64 = require('ngx.base64') -local ffi = require('ffi') local tab_new = require('resty.core.base').new_tab -local base = require('resty.openssl.base') - -ffi.cdef [[ -typedef struct bio_st BIO; -typedef struct bio_method_st BIO_METHOD; -BIO_METHOD *BIO_s_mem(void); -BIO * BIO_new(BIO_METHOD *type); -void BIO_vfree(BIO *a); -int BIO_read(BIO *b, void *data, int len); - -size_t BIO_ctrl_pending(BIO *b); - -typedef struct bignum_st BIGNUM; -typedef void FILE; - -BIGNUM *BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret); -BIGNUM *BN_new(void); -void BN_free(BIGNUM *a); - -int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d); -RSA * RSA_new(void); - -void RSA_free(RSA *rsa); - -int PEM_write_RSA_PUBKEY(FILE *fp, RSA *x); -int PEM_write_bio_RSA_PUBKEY(BIO *bp, RSA *x); -]] - -local C = ffi.C -local ffi_gc = ffi.gc -local ffi_assert = base.ffi_assert +local pkey = require ('resty.openssl.pkey') +local cjson = require ('cjson') local _M = { } -_M.jwk_to_pem = { } - -local function b64toBN(str) - local val, err = b64.decode_base64url(str) - if not val then return nil, err end - - local bn = ffi_assert(C.BN_new()) - ffi_gc(bn, C.BN_free) - - ffi_assert(C.BN_bin2bn(val, #val, bn)) - - return bn -end - -local function read_BIO(bio) - -- BIO_ctrl_pending() return the amount of pending data. - local len = C.BIO_ctrl_pending(bio) - local buf = ffi.new("char[?]", len) - ffi_assert(C.BIO_read(bio, buf, len) >= 0) - return ffi.string(buf, len) -end - -local bio_mem = C.BIO_s_mem() - -local function new_BIO() - local bio = ffi_assert(C.BIO_new(bio_mem)) - - ffi_gc(bio, C.BIO_vfree) - - return bio -end - -local function RSA_to_PEM(rsa) - local bio = new_BIO() - - ffi_assert(C.PEM_write_bio_RSA_PUBKEY(bio, rsa), 1) - - return read_BIO(bio) -end - - -local function RSA_new(n, e, d) - --- https://github.com/sfackler/rust-openssl/blob/2df87cfd5974da887b5cb84c81e249f485bed9f7/openssl/src/rsa.rs#L420-L437 - local rsa = ffi_assert(C.RSA_new()) - ffi_gc(rsa, C.RSA_free) - - --[[ - The n, e and d parameter values can be set by calling RSA_set0_key() - and passing the new values for n, e and d as parameters to the function. - The values n and e must be non-NULL the first time this function is called - on a given RSA object. The value d may be NULL. - ]]-- - - ffi_assert(C.RSA_set0_key(rsa, n, e, d), 1) - - --[[ - Calling this function transfers the memory management of the values - to the RSA object, and therefore the values that have been passed - in should not be freed by the caller after this function has been called. - ]]-- - ffi_gc(n, nil) - ffi_gc(e, nil) - - return rsa -end - -function _M.jwk_to_pem.RSA(jwk) - local n, e, err - - -- parameter n: Base64 URL encoded string representing the modulus of the RSA Key. - n, err = b64toBN(jwk.n) - if err then return nil, err end - - -- parameter e: Base64 URL encoded string representing the public exponent of the RSA Key. - e, err = b64toBN(jwk.e) - if err then return nil, err end - - local rsa = RSA_new(n, e) - - -- jwk.rsa = rsa - jwk.pem = RSA_to_PEM(rsa) - - return jwk -end - function _M.convert_keys(res, ...) if not res then return nil, ... end local keys = tab_new(0, #res.keys) @@ -134,13 +18,13 @@ function _M.convert_keys(res, ...) end function _M.convert_jwk_to_pem(jwk) - local fun = _M.jwk_to_pem[jwk.kty] - - if not fun then - return nil, 'unsupported kty' - end + local val, err = pkey.new(cjson.encode(jwk), { format = "JWK" }) + if not val then + return nil, err + end + jwk.pem = val:tostring("public", "PEM") - return fun(jwk) + return jwk end return _M diff --git a/gateway/src/resty/openssl/base.lua b/gateway/src/resty/openssl/base.lua deleted file mode 100644 index 2c2cd0d29..000000000 --- a/gateway/src/resty/openssl/base.lua +++ /dev/null @@ -1,87 +0,0 @@ -local ffi = require('ffi') - -ffi.cdef([[ - typedef long time_t; - - // https://github.com/openssl/openssl/blob/4ace4ccda2934d2628c3d63d41e79abe041621a7/include/openssl/ossl_typ.h - typedef struct x509_store_st X509_STORE; - typedef struct x509_st X509; - typedef struct X509_crl_st X509_CRL; - typedef struct X509_name_st X509_NAME; - typedef struct bio_st BIO; - typedef struct bio_method_st BIO_METHOD; - typedef struct X509_VERIFY_PARAM_st X509_VERIFY_PARAM; - typedef struct stack_st OPENSSL_STACK; - typedef struct evp_md_st { - int type; - int pkey_type; - int md_size; - } EVP_MD; - - unsigned long ERR_get_error(void); - const char *ERR_reason_error_string(unsigned long e); - - void ERR_clear_error(void); -]]) - -local C = ffi.C -local _M = { } - -local error = error - -local function openssl_error() - local code, reason - - while true do - --[[ - https://www.openssl.org/docs/man1.1.0/crypto/ERR_get_error.html - - ERR_get_error() returns the earliest error code - from the thread's error queue and removes the entry. - This function can be called repeatedly - until there are no more error codes to return. - ]]-- - code = C.ERR_get_error() - - if code == 0 then - break - else - reason = C.ERR_reason_error_string(code) - end - end - - C.ERR_clear_error() - - if reason then - return ffi.string(reason) - end -end - -local function ffi_value(ret, expected) - if ret == nil or ret == -1 or (expected and ret ~= expected) then - return nil, openssl_error() or 'expected value, got nil' - end - - return ret -end - -local function ffi_assert(ret, expected) - local value, err = ffi_value(ret, expected) - - if not value then - error(err, 2) - end - - return value -end - -local function tocdata(obj) - return obj and obj.cdata or obj -end - -_M.ffi_assert = ffi_assert -_M.ffi_value = ffi_value -_M.openssl_error = openssl_error -_M.tocdata = tocdata - -return _M diff --git a/gateway/src/resty/openssl/bio.lua b/gateway/src/resty/openssl/bio.lua deleted file mode 100644 index 5fb00b124..000000000 --- a/gateway/src/resty/openssl/bio.lua +++ /dev/null @@ -1,59 +0,0 @@ -local base = require('resty.openssl.base') -local ffi = require('ffi') - -ffi.cdef([[ - // https://www.openssl.org/docs/manmaster/man3/BIO_write.html - BIO_METHOD *BIO_s_mem(void); - BIO * BIO_new(BIO_METHOD *type); - void BIO_vfree(BIO *a); - int BIO_read(BIO *b, void *data, int len); - int BIO_write(BIO *b, const void *data, int dlen); - - size_t BIO_ctrl_pending(BIO *b); -]]) -local C = ffi.C -local ffi_assert = base.ffi_assert -local str_len = string.len -local assert = assert - -local _M = { - -} - -local mt = { - __index = _M, - __new = function(ct, bio_method) - local bio = ffi_assert(C.BIO_new(bio_method)) - - return ffi.new(ct, bio) - end, - __gc = function(self) - C.BIO_vfree(self.cdata) - end, -} - --- no changes to the metamethods possible from this point -local BIO = ffi.metatype('struct { BIO *cdata; }', mt) - -local bio_mem = C.BIO_s_mem() - -function _M:read() - local bio = self.cdata - -- BIO_ctrl_pending() return the amount of pending data. - local len = C.BIO_ctrl_pending(bio) - local buf = ffi.new("char[?]", len) - ffi_assert(C.BIO_read(bio, buf, len) >= 0) - return ffi.string(buf, len) -end - -function _M:write(str) - local len = str_len(assert(str, 'expected string')) - - return ffi_assert(C.BIO_write(self.cdata, str, len)) -end - -function _M.new() - return BIO(bio_mem) -end - -return _M diff --git a/gateway/src/resty/openssl/evp.lua b/gateway/src/resty/openssl/evp.lua deleted file mode 100644 index 2e6a656e4..000000000 --- a/gateway/src/resty/openssl/evp.lua +++ /dev/null @@ -1,61 +0,0 @@ -local ffi = require('ffi') -local base = require('resty.openssl.base') - -ffi.cdef([[ -const EVP_MD *EVP_sha1(void); -const EVP_MD *EVP_sha256(void); -const EVP_MD *EVP_sha512(void); -const EVP_MD *EVP_get_digestbyname(const char *name); -]]) - -local C = ffi.C -local assert = assert -local tocdata = base.tocdata - -local _M = { } - -local function find(name) - local md = C.EVP_get_digestbyname(name) - - if not md then - return nil, 'not found' - end - - return md -end - -local mt = { - __index = _M, - - __new = function(ct, md) - return ffi.new(ct, assert(md)) - end, - - __len = function(self) - return tocdata(self).md_size - end, -} - -local EVP_MD = ffi.metatype('struct { const EVP_MD *cdata; }', mt) - -function _M.new(name) - local md, err = find(name) - - if not md then return nil, err end - - return EVP_MD(md) -end - -function _M.sha1() - return _M.new('SHA1') -end - -function _M.sha256() - return _M.new('SHA256') -end - -function _M.sha512() - return _M.new('SHA512') -end - -return _M diff --git a/gateway/src/resty/openssl/x509.lua b/gateway/src/resty/openssl/x509.lua deleted file mode 100644 index 329edb3bb..000000000 --- a/gateway/src/resty/openssl/x509.lua +++ /dev/null @@ -1,103 +0,0 @@ -local base = require('resty.openssl.base') -local BIO = require('resty.openssl.bio') -local X509_NAME = require('resty.openssl.x509.name') -local EVP_MD = require('resty.openssl.evp') -local resty_str = require('resty.string') -local ffi = require('ffi') -local re_gsub = ngx.re.gsub - -ffi.cdef([[ -int OPENSSL_sk_num(const OPENSSL_STACK *); -void *OPENSSL_sk_value(const OPENSSL_STACK *, int); -void *OPENSSL_sk_shift(OPENSSL_STACK *st); - -X509 *PEM_read_bio_X509(BIO *bp, X509 **x, pem_password_cb *cb, void *u); -X509_NAME *X509_get_subject_name(const X509 *x); -X509_NAME *X509_get_issuer_name(const X509 *x); - -X509 *X509_new(void); -void X509_free(X509 *a); - -int X509_digest(const X509 *data, const EVP_MD *type, unsigned char *md, unsigned int *len); -]]) - -local C = ffi.C -local openssl_error = base.openssl_error -local ffi_assert = base.ffi_assert -local tocdata = base.tocdata -local assert = assert -local _M = {} -local mt = { - __index = _M, - __new = function(ct, x509) - if x509 == nil then - return nil, openssl_error() - else - return ffi.new(ct, x509) - end - end, - __gc = function(self) - C.X509_free(self.cdata) - end -} - -local X509 = ffi.metatype('struct { X509 *cdata; }', mt) - -local function parse_pem_cert(str) - local bio = BIO.new() - - assert(bio:write(str)) - - return X509(C.PEM_read_bio_X509(bio.cdata, nil, nil, nil)) -end - -local function normalize_pem_cert(str) - if not str then return end - if #(str) == 0 then return end - - -- using also jit compiler (j) will result in a segfault with some certificates - return re_gsub(str, [[\s(?!CERTIFICATE)]], '\n', 'o') -end - -function _M.parse_pem_cert(str) - local crt = normalize_pem_cert(str) - - if crt then - return parse_pem_cert(crt) - else - return nil, 'invalid certificate' - end -end - -function _M:subject_name() - -- X509_get_subject_name() returns the subject name of certificate x. - -- The returned value is an internal pointer which MUST NOT be freed. - -- https://www.openssl.org/docs/man1.1.0/crypto/X509_get_subject_name.html - return X509_NAME.new(C.X509_get_subject_name(tocdata(self))) -end - -function _M:issuer_name() - -- X509_get_issuer_name() and X509_set_issuer_name() are identical to X509_get_subject_name() - -- and X509_set_subject_name() except the get and set the issuer name of x. - -- https://www.openssl.org/docs/man1.1.0/crypto/X509_get_subject_name.html - return X509_NAME.new(C.X509_get_issuer_name(tocdata(self))) -end - -function _M:digest(name) - local evp = EVP_MD.new(name) -- TODO: this EVP_MD object can he cached or passed - local md_size = #evp - local buf = ffi.new("unsigned char[?]", md_size) - local len = ffi.new("unsigned int[1]", md_size) - - ffi_assert(C.X509_digest(tocdata(self), tocdata(evp), buf, len), 1) - - return ffi.string(buf, len[0]) -end - -function _M:hexdigest(evp_md) - local digest = self:digest(evp_md) - - return resty_str.to_hex(digest) -end - -return _M diff --git a/gateway/src/resty/openssl/x509/name.lua b/gateway/src/resty/openssl/x509/name.lua deleted file mode 100644 index 42a99e9f3..000000000 --- a/gateway/src/resty/openssl/x509/name.lua +++ /dev/null @@ -1,103 +0,0 @@ -local base = require('resty.openssl.base') -local BIO = require('resty.openssl.bio') -local ffi = require('ffi') -local bit = require('bit') - -ffi.cdef([[ -int X509_NAME_print_ex(BIO *out, const X509_NAME *nm, int indent, unsigned long flags); -char * X509_NAME_oneline(const X509_NAME *a, char *buf, int size); -int X509_NAME_print(BIO *bp, const X509_NAME *name, int obase); -]]) - -local const = { - -} - -const.XN_FLAG_SEP_MASK = bit.lshift(0xf, 16) - -const.XN_FLAG_COMPAT = 0 -const.XN_FLAG_SEP_COMMA_PLUS = bit.lshift(1, 16) -const.XN_FLAG_SEP_CPLUS_SPC = bit.lshift(2, 16) -const.XN_FLAG_SEP_SPLUS_SPC = bit.lshift(3, 16) -const.XN_FLAG_SEP_MULTILINE = bit.lshift(4, 16) -const.XN_FLAG_DN_REV = bit.lshift(1, 20) -const.XN_FLAG_FN_MASK = bit.lshift(0x3, 21) -const.XN_FLAG_FN_SN = 0 -const.XN_FLAG_FN_LN = bit.lshift(1, 21) -const.XN_FLAG_FN_OID = bit.lshift(2, 21) -const.XN_FLAG_FN_NONE = bit.lshift(3, 21) -const.XN_FLAG_SPC_EQ = bit.lshift(1, 23) -const.XN_FLAG_DUMP_UNKNOWN_FIELDS = bit.lshift(1, 24) -const.XN_FLAG_FN_ALIGN = bit.lshift(1, 25) -const.ASN1_STRFLGS_ESC_2253 = 1 -const.ASN1_STRFLGS_ESC_CTRL = 2 -const.ASN1_STRFLGS_ESC_MSB = 4 -const.ASN1_STRFLGS_ESC_QUOTE = 8 -const.CHARTYPE_PRINTABLESTRING = 0x10 -const.CHARTYPE_FIRST_ESC_2253 = 0x20 -const.CHARTYPE_LAST_ESC_2253 = 0x40 -const.ASN1_STRFLGS_UTF8_CONVERT = 0x10 -const.ASN1_STRFLGS_IGNORE_TYPE = 0x20 -const.ASN1_STRFLGS_SHOW_TYPE = 0x40 -const.ASN1_STRFLGS_DUMP_ALL = 0x80 -const.ASN1_STRFLGS_DUMP_UNKNOWN = 0x100 -const.ASN1_STRFLGS_DUMP_DER = 0x200 -const.SN1_STRFLGS_ESC_2254 = 0x400 - -const.ASN1_STRFLGS_RFC2253 = bit.bor( - const.ASN1_STRFLGS_ESC_2253, - const.ASN1_STRFLGS_ESC_CTRL, - const.ASN1_STRFLGS_ESC_MSB, - const.ASN1_STRFLGS_UTF8_CONVERT, - const.ASN1_STRFLGS_DUMP_UNKNOWN, - const.ASN1_STRFLGS_DUMP_DER -) - -const.XN_FLAG_RFC2253 = bit.bor( - const.ASN1_STRFLGS_RFC2253, - const.XN_FLAG_SEP_COMMA_PLUS, - const.XN_FLAG_DN_REV, - const.XN_FLAG_FN_SN, - const.XN_FLAG_DUMP_UNKNOWN_FIELDS -) - -const.XN_FLAG_ONELINE = bit.bor( - const.ASN1_STRFLGS_RFC2253, - const.ASN1_STRFLGS_ESC_QUOTE, - const.XN_FLAG_SEP_CPLUS_SPC, - const.XN_FLAG_SPC_EQ, - const.XN_FLAG_FN_SN -) - -const.XN_FLAG_MULTILINE = bit.bor( - const.ASN1_STRFLGS_ESC_CTRL, - const.ASN1_STRFLGS_ESC_MSB, - const.XN_FLAG_SEP_MULTILINE, - const.XN_FLAG_SPC_EQ, - const.XN_FLAG_FN_LN, - const.XN_FLAG_FN_ALIGN -) - -local C = ffi.C -local tocdata = base.tocdata -local assert = assert -local _M = {} -local mt = { - __index = _M, - __new = ffi.new, - __tostring = function(self) - local bio = BIO.new() - - C.X509_NAME_print_ex(tocdata(bio), tocdata(self), 0, const.XN_FLAG_ONELINE) - - return bio:read() - end -} - -local X509_NAME = ffi.metatype('struct { X509_NAME *cdata; }', mt) - -function _M.new(name) - return X509_NAME(assert(name)) -end - -return _M diff --git a/gateway/src/resty/openssl/x509/store.lua b/gateway/src/resty/openssl/x509/store.lua deleted file mode 100644 index 6d14eb090..000000000 --- a/gateway/src/resty/openssl/x509/store.lua +++ /dev/null @@ -1,90 +0,0 @@ -local base = require('resty.openssl.base') -local X509_STORE_CTX = require('resty.openssl.x509.store.ctx') -local ffi = require('ffi') -local ffi_gc = ffi.gc - -ffi.cdef([[ -// https://www.openssl.org/docs/man1.1.0/crypto/X509_STORE_new.html -X509_STORE *X509_STORE_new(void); -void X509_STORE_free(X509_STORE *v); -int X509_STORE_lock(X509_STORE *v); -int X509_STORE_unlock(X509_STORE *v); -int X509_STORE_up_ref(X509_STORE *v); - -// https://www.openssl.org/docs/man1.1.1/man3/X509_STORE_add_cert.html -int X509_STORE_add_cert(X509_STORE *store, X509 *x); -int X509_STORE_add_crl(X509_STORE *ctx, X509_CRL *x); -int X509_STORE_set_depth(X509_STORE *store, int depth); -int X509_STORE_set_flags(X509_STORE *ctx, unsigned long flags); -int X509_STORE_set_purpose(X509_STORE *ctx, int purpose); -int X509_STORE_set_trust(X509_STORE *ctx, int trust); - -// https://www.openssl.org/docs/man1.1.0/crypto/X509_STORE_set1_param.html -int X509_STORE_set1_param(X509_STORE *store, X509_VERIFY_PARAM *pm); -X509_VERIFY_PARAM *X509_STORE_get0_param(X509_STORE *ctx); - -// https://www.openssl.org/docs/man1.1.0/crypto/X509_STORE_CTX_set0_param.html -X509_VERIFY_PARAM *X509_VERIFY_PARAM_new(void); -int X509_VERIFY_PARAM_set_flags(X509_VERIFY_PARAM *param, - unsigned long flags); -void X509_VERIFY_PARAM_set_time(X509_VERIFY_PARAM *param, time_t t); -time_t X509_VERIFY_PARAM_get_time(const X509_VERIFY_PARAM *param); -void X509_VERIFY_PARAM_free(X509_VERIFY_PARAM *param); - -// https://www.openssl.org/docs/man1.1.1/man3/X509_VERIFY_PARAM_set_depth.html -]]) - -local C = ffi.C -local ffi_assert = base.ffi_assert -local tocdata = base.tocdata - -local X509_V_FLAG_PARTIAL_CHAIN = 0x80000 - -local function X509_VERIFY_PARAM(flags) - local verify_param = ffi_assert(C.X509_VERIFY_PARAM_new()) - - -- https://www.openssl.org/docs/man1.1.0/crypto/X509_VERIFY_PARAM_get_depth.html#example - ffi_assert(C.X509_VERIFY_PARAM_set_flags(verify_param, flags)) - - return ffi_gc(verify_param, C.X509_VERIFY_PARAM_free) -end - -local _M = {} -local mt = { __index = _M } - -function _M:add_cert(x509) - return ffi_assert(C.X509_STORE_add_cert(self.store, tocdata(x509))) -end - -function _M:validate_cert(x509, chain) - local ctx = X509_STORE_CTX.new(self.store, x509, chain) - - return ctx:validate() -end - -function _M:set_time(seconds) - local verify_param = ffi_assert(C.X509_STORE_get0_param(self.store)) - C.X509_VERIFY_PARAM_set_time(verify_param, seconds) -end - -function _M:time() - local verify_param = ffi_assert(C.X509_STORE_get0_param(self.store)) - return C.X509_VERIFY_PARAM_get_time(verify_param) -end - -function _M.new() - local store = ffi_assert(C.X509_STORE_new()) - ffi_gc(store, C.X509_STORE_free) - - -- enabling partial chains allows us to trust leaf certificates - local verify_param = X509_VERIFY_PARAM(X509_V_FLAG_PARTIAL_CHAIN) - - ffi_assert(C.X509_STORE_set1_param(store, verify_param),1) - - local self = setmetatable({ - store = store, - }, mt) - return self -end - -return _M diff --git a/gateway/src/resty/openssl/x509/store/ctx.lua b/gateway/src/resty/openssl/x509/store/ctx.lua deleted file mode 100644 index 1e442ee50..000000000 --- a/gateway/src/resty/openssl/x509/store/ctx.lua +++ /dev/null @@ -1,56 +0,0 @@ -local base = require('resty.openssl.base') local ffi = require('ffi') - -ffi.cdef([[ -// https://www.openssl.org/docs/man1.1.0/crypto/X509_STORE_CTX_init.html -int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, - X509 *x509, const OPENSSL_STACK *chain); -void X509_STORE_CTX_cleanup(X509_STORE_CTX *ctx); -void X509_STORE_CTX_free(X509_STORE_CTX *ctx); -void X509_STORE_CTX_set0_param(X509_STORE_CTX *ctx, X509_VERIFY_PARAM *param); -X509_VERIFY_PARAM *X509_STORE_CTX_get0_param(X509_STORE_CTX *ctx); - -int X509_verify_cert(X509_STORE_CTX *ctx); -int X509_STORE_CTX_get_error(X509_STORE_CTX *ctx); - -const char *X509_verify_cert_error_string(long n); -]]) - -local C = ffi.C -local ffi_assert = base.ffi_assert -local tocdata = base.tocdata -local openssl_error = base.openssl_error -local setmetatable = setmetatable - -local _M = {} -local mt = { __index = _M } - -local function new_ctx() - local ctx = ffi_assert(C.X509_STORE_CTX_new()) - - return ffi.gc(ctx, C.X509_STORE_CTX_free) -end - -function _M:validate() - local ctx = new_ctx() - - ffi_assert(C.X509_STORE_CTX_init(ctx, self.store, tocdata(self.x509), self.chain), 1) - - local ret = C.X509_verify_cert(ctx) - - if ret == 1 then - return true - else - local err = ffi.string(C.X509_verify_cert_error_string(C.X509_STORE_CTX_get_error(ctx))) - ngx.log(ngx.DEBUG, 'OpenSSL cert validation err: ', openssl_error()) - return false, err - end -end - --- this could be optimized by reusing the context between validations, --- but it is way harder to make safe when there are exceptions - -function _M.new(store, x509, chain) - return setmetatable({ store = store, x509 = x509, chain = chain }, mt) -end - -return _M diff --git a/spec/fixtures/oidc/jwk/forgerock.apicast.json b/spec/fixtures/oidc/jwk/forgerock.apicast.json index dd8f9ca29..cfa2935a6 100644 --- a/spec/fixtures/oidc/jwk/forgerock.apicast.json +++ b/spec/fixtures/oidc/jwk/forgerock.apicast.json @@ -16,5 +16,35 @@ "pem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAi7t6m4d/02dZ8dOe+DFc\nuUYiOWueHlNkFwdUfOs06eUETOV6Y9WCXu3D71dbF0Fhou69ez5c3HAZrSVS2qC1\nHtw9NkVlLDeED7qwQQMmSr7RFYNQ6BYekAtn/ScFHpq8Tx4BzhcDb6P0+PHCo+bk\nQedxwhbMD412KSM2UAVQaZ+TW+ngdaaVEs1Cgl4b8xxZ9ZuApXZfpddNdgvjBeeY\nQbZnaqU3b0P5YE0s0YvIQqYmTjxh4RyLfkt6s/BS1obWUOC+0ChRWlpWE7QTEVEW\nJP5yt8hgZ5MecTmBi3yZ/0ts3NsL83413NdbWYh+ChtP696mZbJozflF8jR9pewT\nbQIDAQAB\n-----END PUBLIC KEY-----\n", "alg": "RS256", "e": "AQAB" - } + }, + "Fol7IpdKeLZmzKtCEgi1LDhSIzM=": { + "alg": "ES256", + "crv": "P-256", + "kid": "Fol7IpdKeLZmzKtCEgi1LDhSIzM=", + "kty": "EC", + "pem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEN7MtObVf92FJTwYvY2ZvTVT3rgZp\n7a7XDtzT/9Rw7IC7E2bKihw+iliHiTUUJzjXK4llWiHGWEyjcpMglVM/dw==\n-----END PUBLIC KEY-----\n", + "use": "sig", + "x": "N7MtObVf92FJTwYvY2ZvTVT3rgZp7a7XDtzT_9Rw7IA", + "y": "uxNmyoocPopYh4k1FCc41yuJZVohxlhMo3KTIJVTP3c" + }, + "I4x/IijvdDsUZMghwNq2gC/7pYQ=": { + "alg": "ES384", + "crv": "P-384", + "kid": "I4x/IijvdDsUZMghwNq2gC/7pYQ=", + "kty": "EC", + "pem": "-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEk5wSvW/6JhOuCj+9PdDWdEA4oH90RSmC\n2GTliiUHAhXj6rmTdE2S+/zGmMFxufuVXfbR+tRoVcZMCoUrkKtuZUIyfCgAy8b0\nFWnPZqevwpdoTzGQBOXSNi6uItN/o4tH\n-----END PUBLIC KEY-----\n", + "use": "sig", + "x": "k5wSvW_6JhOuCj-9PdDWdEA4oH90RSmC2GTliiUHAhXj6rmTdE2S-_zGmMFxufuV", + "y": "XfbR-tRoVcZMCoUrkKtuZUIyfCgAy8b0FWnPZqevwpdoTzGQBOXSNi6uItN_o4tH" + }, + "pZSfpEq8tQPeiIe3fnnaWnnr/Zc=": { + "alg": "ES512", + "crv": "P-521", + "kid": "pZSfpEq8tQPeiIe3fnnaWnnr/Zc=", + "kty": "EC", + "pem": "-----BEGIN PUBLIC KEY-----\nMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAd1Ups0MfKb4yJSHpxpqh3+S2nw9c\n3qDXMgovOVoJI5k/zYoppCx/CW0AqcyvDXb2dXH7aS3zHUnWmnUAN70e1e0BSqFE\npxi8DqUSUjdSt+q+m6FWzIQUFDhwz8T2/hYadFz+muKJNVr9QLth8K1AlP0Xw5zb\nW/+LntKncyr+/Dm55F0=\n-----END PUBLIC KEY-----\n", + "use": "sig", + "x": "AHdVKbNDHym-MiUh6caaod_ktp8PXN6g1zIKLzlaCSOZP82KKaQsfwltAKnMrw129nVx-2kt8x1J1pp1ADe9HtXt", + "y": "AUqhRKcYvA6lElI3UrfqvpuhVsyEFBQ4cM_E9v4WGnRc_priiTVa_UC7YfCtQJT9F8Oc21v_i57Sp3Mq_vw5ueRd" + } } diff --git a/spec/policy/apicast/apicast_spec.lua b/spec/policy/apicast/apicast_spec.lua index 21460f200..4f674daf9 100644 --- a/spec/policy/apicast/apicast_spec.lua +++ b/spec/policy/apicast/apicast_spec.lua @@ -43,10 +43,10 @@ describe('APIcast policy', function() local certificate_content = util.read_file(certificate_path) local key_content = util.read_file(certificate_key_path) - local ca_cert, _ = X509.parse_pem_cert(certificate_content) + local ca_cert, _ = X509.new(certificate_content) local ca_store = X509_STORE.new() - ca_store:add_cert(ca_cert) + ca_store:add(ca_cert) local cert = ssl.parse_pem_cert(certificate_content) local key = ssl.parse_pem_priv_key(key_content) diff --git a/spec/policy/fapi/fapi_spec.lua b/spec/policy/fapi/fapi_spec.lua index 144adda68..6e91f39ba 100644 --- a/spec/policy/fapi/fapi_spec.lua +++ b/spec/policy/fapi/fapi_spec.lua @@ -7,8 +7,8 @@ local clientCert = assert(fixture('CA', 'client.crt')) local header_parameter = 'x5t#S256' local function jwt_cnf() - local cnf = b64.encode_base64url(X509.parse_pem_cert(clientCert):digest('SHA256')) - return { [header_parameter] = b64.encode_base64url(X509.parse_pem_cert(clientCert):digest('SHA256')) } + local cnf = b64.encode_base64url(X509.new(clientCert):digest('SHA256')) + return { [header_parameter] = b64.encode_base64url(X509.new(clientCert):digest('SHA256')) } end describe('fapi_1_baseline_profile policy', function() diff --git a/spec/policy/oauth_mtls/oauth_mtls_spec.lua b/spec/policy/oauth_mtls/oauth_mtls_spec.lua index 6dd3028e4..691dc6b87 100644 --- a/spec/policy/oauth_mtls/oauth_mtls_spec.lua +++ b/spec/policy/oauth_mtls/oauth_mtls_spec.lua @@ -7,7 +7,7 @@ local header_parameter = 'x5t#S256' local context = {} local function jwt_cnf() - return { [header_parameter] = b64.encode_base64url(X509.parse_pem_cert(client):digest('SHA256')) } + return { [header_parameter] = b64.encode_base64url(X509.new(client):digest('SHA256')) } end describe('oauth_mtls policy', function() diff --git a/spec/policy/tls_validation/tls_validation_spec.lua b/spec/policy/tls_validation/tls_validation_spec.lua index 9dc438617..c059ab2d0 100644 --- a/spec/policy/tls_validation/tls_validation_spec.lua +++ b/spec/policy/tls_validation/tls_validation_spec.lua @@ -3,6 +3,9 @@ local _M = require('apicast.policy.tls_validation') local server = assert(fixture('CA', 'server.crt')) local CA = assert(fixture('CA', 'intermediate-ca.crt')) local client = assert(fixture('CA', 'client.crt')) +local revoked_client = assert(fixture('CA', 'revoked_client.crt')) +local ssl_helper = require 'ssl_helper' +local crl = assert(fixture('CA', 'crl.pem')) describe('tls_validation policy', function() describe('.new', function() @@ -38,7 +41,7 @@ describe('tls_validation policy', function() it('rejects certificates that are not valid yet', function() local policy = _M.new({ whitelist = { { pem_certificate = client }}}) - policy.x509_store:set_time(os.time{ year = 2000, month = 01, day = 01 }) + ssl_helper.set_time(policy.x509_store.ctx, os.time{ year = 2000, month = 01, day = 01 }) ngx.var = { ssl_client_raw_cert = client } policy:access() @@ -49,7 +52,7 @@ describe('tls_validation policy', function() it('rejects certificates that are not longer valid', function() local policy = _M.new({ whitelist = { { pem_certificate = client }}}) - policy.x509_store:set_time(os.time{ year = 2042, month = 01, day = 01 }) + ssl_helper.set_time(policy.x509_store.ctx, os.time{ year = 2042, month = 01, day = 01 }) ngx.var = { ssl_client_raw_cert = client } policy:access() @@ -73,5 +76,29 @@ describe('tls_validation policy', function() assert.is_true(policy:access()) end) + + it('accepts CRL', function() + ngx.var = { ssl_client_raw_cert = client } + + local policy = _M.new({ + whitelist = { { pem_certificate = CA }}, + revocation_check_type = "crl", + revoke_list = { { pem_certificate = crl }}}) + + assert.is_true(policy:access()) + end) + + it('reject revoked certificate', function() + ngx.var = { ssl_client_raw_cert = revoked_client } + + local policy = _M.new({ + whitelist = { { pem_certificate = CA }}, + revocation_check_type = "crl", + revoke_list = { { pem_certificate = crl }}}) + + policy:access() + assert.stub(ngx.exit).was_called_with(400) + assert.stub(ngx.say).was_called_with([[TLS certificate validation failed]]) + end) end) end) diff --git a/spec/resty/openssl/bio_spec.lua b/spec/resty/openssl/bio_spec.lua deleted file mode 100644 index 787004677..000000000 --- a/spec/resty/openssl/bio_spec.lua +++ /dev/null @@ -1,31 +0,0 @@ -local _M = require('resty.openssl.bio') - -describe('OpenSSL BIO', function() - describe('.new', function() - it('returns cdata', function() - assert.equal('cdata', type(_M.new())) - end) - end) - - describe(':write', function() - it('writes data to bio', function() - local bio = _M.new() - local str = 'foobar' - - assert(bio:write(str)) - assert.equal(str, bio:read()) - end) - - it('requires a string', function() - local bio = _M.new() - - assert.has_error(function () bio:write() end, 'expected string') - end) - - it('empty string return 0', function() - local bio = _M.new() - assert.same(bio:write(""), 0) - end) - end) - -end) diff --git a/spec/resty/openssl/x509/store_spec.lua b/spec/resty/openssl/x509/store_spec.lua deleted file mode 100644 index ccb136491..000000000 --- a/spec/resty/openssl/x509/store_spec.lua +++ /dev/null @@ -1,137 +0,0 @@ -local X509 = require('resty.openssl.x509') -local base = require('resty.openssl.base') - -local CA = X509.parse_pem_cert([[ ------BEGIN CERTIFICATE----- -MIICvDCCAaQCCQCep4rpEMmCcDANBgkqhkiG9w0BAQsFADAgMR4wHAYDVQQDDBVD -ZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMTgxMjA2MTcwNTQ3WhcNMjgxMjAzMTcw -NTQ3WjAgMR4wHAYDVQQDDBVDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG -SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqIfkTccBdVmBqoewL2gBnkDOwk9cKBcLn -uIPge4GfO1Vm4AhDFyZOH9gUmjRH+5Dfu/G+dq7U1jOxTQnvs5U/4857PCTc/rdf -TT/HcG8k6GhBMq6/+gwtT/nOxcFmDkyAOBR2DpvwOd1soOU7lokHkDYTv+kPKrRP -Gc6x7cl3NrsAK154u1xNAGDZiEeThBmi2EanTEZOx4dqkc5pD89P5A/vwjV5LJ+v -jtL+P1FOgK57B3fVFqTL1TNOQdH9BWRZ7z3ZPfSn1PokKA4fazTOZ0iXeQVSIqju -msRk91o+CFXNPJS8NRMsp6Nk6iClyXtaxBWzAcnAxSf9u/UZ6murAgMBAAEwDQYJ -KoZIhvcNAQELBQADggEBAIZo62o53KVLWnDCBxFHwhKVgPa95o1E3RJWuRTI8kdX -L8tehLHorqOCZ1zNIDv8l2QErVUvcxwL/lpuJWZLUvhHPYUg6FDKB+vapVd1yRgR -o4fWkEQkMiKZ4bsSmM00udS5pYGiMHc3vjBcmEPzACIfcv+K29F58Lb3v2ccIXh3 -5pQvDYhqaeivRK6JIDY/+1UnaQt65DeNDAfGeAdar6DbFW+gju9avYGINRJP+BGC -Wce2mRmiNUqt37UO1+NXSLa9+4By0j5I1dMqCRFjwQBUaDgrhQf1xpVbEQ30myyy -Ci818xLwDp7CENLKIBNtg88u9Z+ha81pscKiG9WXCLI= ------END CERTIFICATE----- -]]) - -local pem = [[ ------BEGIN CERTIFICATE----- -MIICvDCCAaQCCQDyra7VGipAyzANBgkqhkiG9w0BAQsFADAgMR4wHAYDVQQDDBVD -ZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMTgxMjA2MTcwOTA1WhcNMjgxMjAzMTcw -OTA1WjAgMQ8wDQYDVQQKDAZDbGllbnQxDTALBgNVBAMMBGN1cmwwggEiMA0GCSqG -SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDt9H6xhm0pGqARRGMaUrSbZvetrN1mo+O4 -KuqPRr8I/YhvOEPlc/8VMxF3nyETGjQ+khO9FJGDoDD2S3yGzt1FFiNI6AOPkmux -DZMUQ2alnS7fG0zBUlxRx9otoMx/vH4gnKTfmHofuwPwkLPSWoHf0ZmPLXbm19ds -aKvllOX8vjEjtNprtUzveeDOnuov2GXqo/w+FOnDxYhys1Oidx3LOje5izV7EX4+ -+HH+7EwRV7m4+s/G97z5soo1XIZHHQKKC0DONWTOdeLkqLlAqU0nuuRkFzmbrD4u -2haxqcuyficBgbFWZznLDxJ1fMJzen7YbYea1GycTKe6Wt4xviDDAgMBAAEwDQYJ -KoZIhvcNAQELBQADggEBADY5udciqAIAFtJWVQ+AT+5RAWClGlEfi7wAfsGWUIpi -1mQjkGSqbZ4DSEECsRNiokjSyA5Phi9REg8tDCVaovMANncptUX6PJzCkpkdD5Wo -cMWzF8dZpphyZH+RwGM7aTGmdz/mnxKtVoTt++wLNv2jardRKoFvyu+FBzpTbWBe -2EYaIlGHRrIMoU9ZK3D2rGHK3GsakZT3e76/P5KuyIp1+K7IEWmD4Fk3GM6uM+Rc -Q7zGkdX+LBr85p07DHTcDxAwIT6xXh2J1fhiyart5sHkMg6YZ5JpjitIOEypnyiq -KjTINz0a+0rohUDR6BWkdU5R8Bpbw1Pg7Owx9B51KQM= ------END CERTIFICATE----- -]] - -local client = X509.parse_pem_cert([[ ------BEGIN CERTIFICATE----- -MIICvDCCAaQCCQDyra7VGipAyzANBgkqhkiG9w0BAQsFADAgMR4wHAYDVQQDDBVD -ZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMTgxMjA2MTcwOTA1WhcNMjgxMjAzMTcw -OTA1WjAgMQ8wDQYDVQQKDAZDbGllbnQxDTALBgNVBAMMBGN1cmwwggEiMA0GCSqG -SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDt9H6xhm0pGqARRGMaUrSbZvetrN1mo+O4 -KuqPRr8I/YhvOEPlc/8VMxF3nyETGjQ+khO9FJGDoDD2S3yGzt1FFiNI6AOPkmux -DZMUQ2alnS7fG0zBUlxRx9otoMx/vH4gnKTfmHofuwPwkLPSWoHf0ZmPLXbm19ds -aKvllOX8vjEjtNprtUzveeDOnuov2GXqo/w+FOnDxYhys1Oidx3LOje5izV7EX4+ -+HH+7EwRV7m4+s/G97z5soo1XIZHHQKKC0DONWTOdeLkqLlAqU0nuuRkFzmbrD4u -2haxqcuyficBgbFWZznLDxJ1fMJzen7YbYea1GycTKe6Wt4xviDDAgMBAAEwDQYJ -KoZIhvcNAQELBQADggEBADY5udciqAIAFtJWVQ+AT+5RAWClGlEfi7wAfsGWUIpi -1mQjkGSqbZ4DSEECsRNiokjSyA5Phi9REg8tDCVaovMANncptUX6PJzCkpkdD5Wo -cMWzF8dZpphyZH+RwGM7aTGmdz/mnxKtVoTt++wLNv2jardRKoFvyu+FBzpTbWBe -2EYaIlGHRrIMoU9ZK3D2rGHK3GsakZT3e76/P5KuyIp1+K7IEWmD4Fk3GM6uM+Rc -Q7zGkdX+LBr85p07DHTcDxAwIT6xXh2J1fhiyart5sHkMg6YZ5JpjitIOEypnyiq -KjTINz0a+0rohUDR6BWkdU5R8Bpbw1Pg7Owx9B51KQM= ------END CERTIFICATE----- -]]) - -local _M = require('resty.openssl.x509.store') - -describe('OpenSSL X509 Store', function() - describe('.new', function() - it('returns cdata', function () - assert.equals('table', type(_M.new())) - assert.equals('cdata', type(_M.new().store)) - end) - end) - - describe(':add_cert', function() - it('has add_cert method', function() - assert(_M.new().add_cert) - end) - end) - - describe(':time', function() - it('can set and get time', function() - local store = _M.new() - - assert.equal(0, store:time()) - - store:set_time(100) - - assert.equal(100, store:time()) - end) - end) - - describe(':validate_cert', function() - after_each(function() - assert.is_nil(base.openssl_error()) - end) - - it('works with X509 object', function () - local x509 = X509.parse_pem_cert(pem) - local store = _M.new() - - assert.returns_error('unable to get local issuer certificate', store:validate_cert(x509)) - end) - - it('validates missing certificate', function() - local store = _M.new() - - assert.returns_error('Invalid certificate verification context', store:validate_cert()) - end) - - it('validates self signed certificate', function() - local store = _M.new() - - assert.returns_error('self signed certificate', store:validate_cert(CA)) - - store:add_cert(CA) - assert(store:validate_cert(CA)) - end) - - it('validates certificate by itself', function() - local store = _M.new() - - assert.returns_error('unable to get local issuer certificate', store:validate_cert(client)) - - store:add_cert(client) - assert(store:validate_cert(client)) - end) - - it('validates certificate by CA', function() - local store = _M.new() - - assert.returns_error('unable to get local issuer certificate', store:validate_cert(client)) - - store:add_cert(CA) - assert(store:validate_cert(client)) - end) - end) -end) diff --git a/spec/resty/openssl/x509_spec.lua b/spec/resty/openssl/x509_spec.lua deleted file mode 100644 index a6a674137..000000000 --- a/spec/resty/openssl/x509_spec.lua +++ /dev/null @@ -1,66 +0,0 @@ -local _M = require('resty.openssl.x509') - -local pem = [[ ------BEGIN CERTIFICATE----- -MIICvDCCAaQCCQCep4rpEMmCcDANBgkqhkiG9w0BAQsFADAgMR4wHAYDVQQDDBVD -ZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMTgxMjA2MTcwNTQ3WhcNMjgxMjAzMTcw -NTQ3WjAgMR4wHAYDVQQDDBVDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG -SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqIfkTccBdVmBqoewL2gBnkDOwk9cKBcLn -uIPge4GfO1Vm4AhDFyZOH9gUmjRH+5Dfu/G+dq7U1jOxTQnvs5U/4857PCTc/rdf -TT/HcG8k6GhBMq6/+gwtT/nOxcFmDkyAOBR2DpvwOd1soOU7lokHkDYTv+kPKrRP -Gc6x7cl3NrsAK154u1xNAGDZiEeThBmi2EanTEZOx4dqkc5pD89P5A/vwjV5LJ+v -jtL+P1FOgK57B3fVFqTL1TNOQdH9BWRZ7z3ZPfSn1PokKA4fazTOZ0iXeQVSIqju -msRk91o+CFXNPJS8NRMsp6Nk6iClyXtaxBWzAcnAxSf9u/UZ6murAgMBAAEwDQYJ -KoZIhvcNAQELBQADggEBAIZo62o53KVLWnDCBxFHwhKVgPa95o1E3RJWuRTI8kdX -L8tehLHorqOCZ1zNIDv8l2QErVUvcxwL/lpuJWZLUvhHPYUg6FDKB+vapVd1yRgR -o4fWkEQkMiKZ4bsSmM00udS5pYGiMHc3vjBcmEPzACIfcv+K29F58Lb3v2ccIXh3 -5pQvDYhqaeivRK6JIDY/+1UnaQt65DeNDAfGeAdar6DbFW+gju9avYGINRJP+BGC -Wce2mRmiNUqt37UO1+NXSLa9+4By0j5I1dMqCRFjwQBUaDgrhQf1xpVbEQ30myyy -Ci818xLwDp7CENLKIBNtg88u9Z+ha81pscKiG9WXCLI= ------END CERTIFICATE----- -]] - -local certificate = _M.parse_pem_cert(pem) - -describe('OpenSSL X509', function () - - describe('parse_pem_cert', function () - it('returns', function () - local crt = _M.parse_pem_cert(pem) - - assert(crt) - end) - - it('does not crash on invalid certificate', function() - assert.returns_error('no start line', _M.parse_pem_cert('garbage')) - assert.returns_error('bad end line', _M.parse_pem_cert('-----BEGIN CERTIFICATE-----')) - assert.returns_error('invalid certificate', _M.parse_pem_cert('')) - end) - - it('parses wrongly formatted newlines', function () - assert(_M.parse_pem_cert([[-----BEGIN CERTIFICATE----- MIICvDCCAaQCCQDyra7VGipAyzANBgkqhkiG9w0BAQsFADAgMR4wHAYDVQQDDBVD ZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMTgxMjA2MTcwOTA1WhcNMjgxMjAzMTcw OTA1WjAgMQ8wDQYDVQQKDAZDbGllbnQxDTALBgNVBAMMBGN1cmwwggEiMA0GCSqG SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDt9H6xhm0pGqARRGMaUrSbZvetrN1mo+O4 KuqPRr8I/YhvOEPlc/8VMxF3nyETGjQ+khO9FJGDoDD2S3yGzt1FFiNI6AOPkmux DZMUQ2alnS7fG0zBUlxRx9otoMx/vH4gnKTfmHofuwPwkLPSWoHf0ZmPLXbm19ds aKvllOX8vjEjtNprtUzveeDOnuov2GXqo/w+FOnDxYhys1Oidx3LOje5izV7EX4+ +HH+7EwRV7m4+s/G97z5soo1XIZHHQKKC0DONWTOdeLkqLlAqU0nuuRkFzmbrD4u 2haxqcuyficBgbFWZznLDxJ1fMJzen7YbYea1GycTKe6Wt4xviDDAgMBAAEwDQYJ KoZIhvcNAQELBQADggEBADY5udciqAIAFtJWVQ+AT+5RAWClGlEfi7wAfsGWUIpi 1mQjkGSqbZ4DSEECsRNiokjSyA5Phi9REg8tDCVaovMANncptUX6PJzCkpkdD5Wo cMWzF8dZpphyZH+RwGM7aTGmdz/mnxKtVoTt++wLNv2jardRKoFvyu+FBzpTbWBe 2EYaIlGHRrIMoU9ZK3D2rGHK3GsakZT3e76/P5KuyIp1+K7IEWmD4Fk3GM6uM+Rc Q7zGkdX+LBr85p07DHTcDxAwIT6xXh2J1fhiyart5sHkMg6YZ5JpjitIOEypnyiq KjTINz0a+0rohUDR6BWkdU5R8Bpbw1Pg7Owx9B51KQM= -----END CERTIFICATE-----]])) - end) - end) - - describe(':name()', function() - it('returns subject name', function() - assert.equal('CN = Certificate Authority', tostring(certificate:subject_name())) - end) - - it('returns issuer name', function() - assert.equal('CN = Certificate Authority', tostring(certificate:issuer_name())) - end) - end) - - describe(':digest', function () - it('returns a digest', function () - assert(certificate:digest('SHA256')) - end) - end) - - describe(':hexdigest', function () - it('returns a hex formatted digest', function () - assert.equal('874fd0756c3c36c78319ca6e484e670780b86146', certificate:hexdigest('SHA1')) - end) - end) -end) diff --git a/spec/ssl_helper.lua b/spec/ssl_helper.lua new file mode 100644 index 000000000..71c73f858 --- /dev/null +++ b/spec/ssl_helper.lua @@ -0,0 +1,19 @@ +local ffi = require "ffi" +local C = ffi.C + +ffi.cdef([[ + typedef long time_t; + typedef struct X509_VERIFY_PARAM_st X509_VERIFY_PARAM; + typedef struct x509_store_st X509_STORE; + X509_VERIFY_PARAM *X509_STORE_get0_param(X509_STORE *ctx); + void X509_VERIFY_PARAM_set_time(X509_VERIFY_PARAM *param, time_t t); +]]) + +local SSL = {} + +function SSL.set_time(store, seconds) + local verify_param = C.X509_STORE_get0_param(store) + C.X509_VERIFY_PARAM_set_time(verify_param, seconds) +end + +return SSL diff --git a/t/apicast-policy-tls_validation.t b/t/apicast-policy-tls_validation.t index 15b2c66ab..5e406a0e9 100644 --- a/t/apicast-policy-tls_validation.t +++ b/t/apicast-policy-tls_validation.t @@ -353,3 +353,126 @@ No required TLS certificate was sent --- no_error_log [error] --- user_files fixture=CA/files.pl eval + + + +=== TEST 9: TLS Client Certificate with intermediate certificate fails when +allow_partial_chain set to false +--- configuration eval +use JSON qw(to_json); +use File::Slurp qw(read_file); + +to_json({ + services => [{ + proxy => { + hosts => ['test'], + policy_chain => [ + { name => 'apicast.policy.tls_validation', + configuration => { + whitelist => [ + { pem_certificate => CORE::join('', read_file('t/fixtures/CA/client.crt')) } + ], + allow_partial_chain => JSON::false + } + }, + { name => 'apicast.policy.echo' }, + ] + } + }] +}); +--- test env +proxy_ssl_verify on; +proxy_ssl_trusted_certificate $TEST_NGINX_SERVER_ROOT/html/ca.crt; +proxy_ssl_certificate $TEST_NGINX_SERVER_ROOT/html/client.crt; +proxy_ssl_certificate_key $TEST_NGINX_SERVER_ROOT/html/client.key; +proxy_pass https://$server_addr:$apicast_port/t; +proxy_set_header Host test; +log_by_lua_block { collectgarbage() } +--- error_code: 400 +--- error_log +unable to get local issuer certificat +--- user_files fixture=CA/files.pl eval + + + +=== TEST 10: TLS Client Certificate with Certificate Revoke List (CRL) +--- configuration eval +use JSON qw(to_json); +use File::Slurp qw(read_file); + +to_json({ + services => [{ + proxy => { + hosts => ['test'], + policy_chain => [ + { name => 'apicast.policy.tls_validation', + configuration => { + whitelist => [ + { pem_certificate => CORE::join('', read_file('t/fixtures/CA/intermediate-ca.crt')) } + ], + revoke_list => [ + { pem_certificate => CORE::join('', read_file('t/fixtures/CA/crl.pem')) } + ], + revocation_check_type => 'crl' + } + }, + { name => 'apicast.policy.echo' }, + ] + } + }] +}); +--- test env +proxy_ssl_verify on; +proxy_ssl_trusted_certificate $TEST_NGINX_SERVER_ROOT/html/ca.crt; +proxy_ssl_certificate $TEST_NGINX_SERVER_ROOT/html/client.crt; +proxy_ssl_certificate_key $TEST_NGINX_SERVER_ROOT/html/client.key; +proxy_pass https://$server_addr:$apicast_port/t; +proxy_set_header Host test; +log_by_lua_block { collectgarbage() } +--- error_code: 200 +--- no_error_log +[error] +--- user_files fixture=CA/files.pl eval + + + +=== TEST 11: TLS Client Certificate with Certificate Revoke List (CRL) and +revoked certificate +--- ONLY +--- configuration eval +use JSON qw(to_json); +use File::Slurp qw(read_file); + +to_json({ + services => [{ + proxy => { + hosts => ['test'], + policy_chain => [ + { name => 'apicast.policy.tls_validation', + configuration => { + whitelist => [ + { pem_certificate => CORE::join('', read_file('t/fixtures/CA/intermediate-ca.crt')) } + ], + revoke_list => [ + { pem_certificate => CORE::join('', read_file('t/fixtures/CA/crl.pem')) } + ], + revocation_check_type => 'crl' + } + }, + { name => 'apicast.policy.echo' }, + ] + } + }] +}); +--- test env +proxy_ssl_verify on; +proxy_ssl_trusted_certificate $TEST_NGINX_SERVER_ROOT/html/ca.crt; +proxy_ssl_certificate $TEST_NGINX_SERVER_ROOT/html/revoked_client.crt; +proxy_ssl_certificate_key $TEST_NGINX_SERVER_ROOT/html/revoked_client.key; +proxy_pass https://$server_addr:$apicast_port/t; +proxy_set_header Host test; +log_by_lua_block { collectgarbage() } +--- error_code: 400 +--- error_log +TLS certificate validation failed, err: certificate revoked +--- user_files fixture=CA/files.pl eval diff --git a/t/fixtures/CA/Makefile b/t/fixtures/CA/Makefile index e4046bda0..c0ae7f937 100644 --- a/t/fixtures/CA/Makefile +++ b/t/fixtures/CA/Makefile @@ -1,4 +1,4 @@ -all: client.key client.crt server.key server.crt ca-bundle.crt: +all: client.key client.crt server.key server.crt ca-bundle.crt crl.pem EXPIRATION := 87600h # 10 years @@ -27,3 +27,12 @@ client.crt client.key: intermediate-ca.crt intermediate-ca.key step certificate create client client.crt client.key --profile leaf \ --ca ./intermediate-ca.crt --ca-key ./intermediate-ca.key \ --no-password --insecure --not-after=${EXPIRATION} + +revoke_client.crt revoke_client.key: + step certificate create client revoked_client.crt revoked_client.key --profile leaf \ + --ca ./intermediate-ca.crt --ca-key ./intermediate-ca.key \ + --no-password --insecure --not-after=${EXPIRATION} + +crl.pem: revoke_client.crt revoke_client.key + openssl ca --revoke revoked_client.crt -config ./crl_openssl.conf + openssl ca -config crl_openssl.conf -gencrl -out crl.pem -crldays 3650 diff --git a/t/fixtures/CA/crl.pem b/t/fixtures/CA/crl.pem new file mode 100644 index 000000000..f477d2880 --- /dev/null +++ b/t/fixtures/CA/crl.pem @@ -0,0 +1,9 @@ +-----BEGIN X509 CRL----- +MIIBRzCB7wIBATAKBggqhkjOPQQDAjAaMRgwFgYDVQQDEw9pbnRlcm1lZGlhdGUt +Y2EXDTI0MTAzMDA1NDQyNVoXDTM0MTAyODA1NDQyNVowRzAhAhAMx1ybLzQvkRKJ +0xUQrJteFw0yNDEwMzAwNTQ0MjVaMCICEQDD68D5lvxp5CXYFTVYWT0FFw0yNDEw +MzAwNTI0MjJaoFswWTBKBgNVHSMEQzBBgBTaEjeKcCSJEuUcnwUlddtXhKhMcaEW +pBQwEjEQMA4GA1UEAxMHcm9vdC1jYYIRAPvf2lJeYv9BaUhvVQzCD8wwCwYDVR0U +BAQCAhACMAoGCCqGSM49BAMCA0cAMEQCIHmFVLW2NnerBataPZSmGfyfQaXsjT1W +a4j7Hp+5IA2zAiBOXNqOIqElfShj6UVVc6j2geuUtkD6NLjOPRRqoEUw4A== +-----END X509 CRL----- diff --git a/t/fixtures/CA/crl/crlnumber b/t/fixtures/CA/crl/crlnumber new file mode 100644 index 000000000..baccd0398 --- /dev/null +++ b/t/fixtures/CA/crl/crlnumber @@ -0,0 +1 @@ +1003 diff --git a/t/fixtures/CA/crl/crlnumber.old b/t/fixtures/CA/crl/crlnumber.old new file mode 100644 index 000000000..7d802a3e7 --- /dev/null +++ b/t/fixtures/CA/crl/crlnumber.old @@ -0,0 +1 @@ +1002 diff --git a/t/fixtures/CA/crl/index.txt b/t/fixtures/CA/crl/index.txt new file mode 100644 index 000000000..3c1672366 --- /dev/null +++ b/t/fixtures/CA/crl/index.txt @@ -0,0 +1,2 @@ +R 341028052243Z 241030052422Z C3EBC0F996FC69E425D8153558593D05 unknown /CN=client +R 341028054420Z 241030054425Z 0CC75C9B2F342F911289D31510AC9B5E unknown /CN=client diff --git a/t/fixtures/CA/crl/index.txt.attr b/t/fixtures/CA/crl/index.txt.attr new file mode 100644 index 000000000..8f7e63a34 --- /dev/null +++ b/t/fixtures/CA/crl/index.txt.attr @@ -0,0 +1 @@ +unique_subject = yes diff --git a/t/fixtures/CA/crl/index.txt.attr.old b/t/fixtures/CA/crl/index.txt.attr.old new file mode 100644 index 000000000..8f7e63a34 --- /dev/null +++ b/t/fixtures/CA/crl/index.txt.attr.old @@ -0,0 +1 @@ +unique_subject = yes diff --git a/t/fixtures/CA/crl/index.txt.old b/t/fixtures/CA/crl/index.txt.old new file mode 100644 index 000000000..b9d17d432 --- /dev/null +++ b/t/fixtures/CA/crl/index.txt.old @@ -0,0 +1 @@ +R 341028052243Z 241030052422Z C3EBC0F996FC69E425D8153558593D05 unknown /CN=client diff --git a/t/fixtures/CA/crl/serial b/t/fixtures/CA/crl/serial new file mode 100644 index 000000000..83b33d238 --- /dev/null +++ b/t/fixtures/CA/crl/serial @@ -0,0 +1 @@ +1000 diff --git a/t/fixtures/CA/crl_openssl.conf b/t/fixtures/CA/crl_openssl.conf new file mode 100644 index 000000000..6871ddadb --- /dev/null +++ b/t/fixtures/CA/crl_openssl.conf @@ -0,0 +1,35 @@ +# OpenSSL configuration for CRL generation +# +[ ca ] +default_ca = CA_default # The default ca section + +[ CA_default ] +dir = ./ +certs = $dir/certs # Where the issued certs are kept +crl_dir = $dir/crl # Where the issued crl are kept +database = $dir/crl/index.txt # database index file. +new_certs_dir = $dir/newcerts # default place for new certs. +serial = $dir/crl/serial # The current serial number + +private_key = intermediate-ca.key +certificate = intermediate-ca.crt + +# Comment out the following two lines for the "traditional" +# (and highly broken) format. +name_opt = ca_default # Subject Name options +cert_opt = ca_default # Certificate field options + +# For certificate revocation lists. +crlnumber = $dir/crl/crlnumber +crl = $dir/crl/intermediate.crl.pem +crl_extensions = crl_ext +default_days = 365 # how long to certify for +default_crl_days = 30 # How long before the next CRL +default_md = sha256 # use public key default MD +preserve = no # keep passed DN ordering + +[ crl_ext ] +# CRL extensions. +# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. +# issuerAltName=issuer:copy +authorityKeyIdentifier=keyid:always,issuer:always diff --git a/t/fixtures/CA/files.pl b/t/fixtures/CA/files.pl index 64fa1b284..fd6f351d4 100644 --- a/t/fixtures/CA/files.pl +++ b/t/fixtures/CA/files.pl @@ -21,4 +21,6 @@ read_file('t/fixtures/CA/root-ca.crt'), ) ], [ "client.key" => CORE::join('', read_file('t/fixtures/CA/client.key')) ], + [ "revoked_client.crt" => CORE::join('',read_file('t/fixtures/CA/revoked_client.crt')) ], + [ "revoked_client.key" => CORE::join('', read_file('t/fixtures/CA/revoked_client.key')) ], ] diff --git a/t/fixtures/CA/revoked_client.crt b/t/fixtures/CA/revoked_client.crt new file mode 100644 index 000000000..f58f87b8c --- /dev/null +++ b/t/fixtures/CA/revoked_client.crt @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBrzCCAVWgAwIBAgIQDMdcmy80L5ESidMVEKybXjAKBggqhkjOPQQDAjAaMRgw +FgYDVQQDEw9pbnRlcm1lZGlhdGUtY2EwHhcNMjQxMDMwMDU0NDIwWhcNMzQxMDI4 +MDU0NDIwWjARMQ8wDQYDVQQDEwZjbGllbnQwWTATBgcqhkjOPQIBBggqhkjOPQMB +BwNCAAQcDPD//JMA0j46aYVdsTh5Eb5KIfNhoJcHkhVRjMbhhsZGcMD+pysHvUlQ +7lwprlYOMSXgnPJUM7IUPq9eCMvso4GFMIGCMA4GA1UdDwEB/wQEAwIHgDAdBgNV +HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHQYDVR0OBBYEFNPTMaJOoyQXFMEz +6Cf7WfWFLlXxMB8GA1UdIwQYMBaAFNoSN4pwJIkS5RyfBSV121eEqExxMBEGA1Ud +EQQKMAiCBmNsaWVudDAKBggqhkjOPQQDAgNIADBFAiAmcpvUCOettopMTnaCoIaX +k3/Xw2GpM5OB90YWvbs69wIhAJhhK6+73p3yMu6MvTn4bSLL8eLAWrDcAuSX17vM +OAOb +-----END CERTIFICATE----- diff --git a/t/fixtures/CA/revoked_client.key b/t/fixtures/CA/revoked_client.key new file mode 100644 index 000000000..86eb16452 --- /dev/null +++ b/t/fixtures/CA/revoked_client.key @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEINyI8SLVOG+ufV0+tAzwhRT9/Xe19z//O0xci/SqZJCAoAoGCCqGSM49 +AwEHoUQDQgAEHAzw//yTANI+OmmFXbE4eRG+SiHzYaCXB5IVUYzG4YbGRnDA/qcr +B71JUO5cKa5WDjEl4JzyVDOyFD6vXgjL7A== +-----END EC PRIVATE KEY-----