Skip to content
FFI-based OpenSSL binding for LuaJIT
Lua Perl Python Makefile
Branch: master
Clone or download
This branch is 19 commits behind fffonion:master.

Latest commit

Fetching latest commit…
Cannot retrieve the latest commit at this time.

Files

Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.chglog
lib/resty
scripts
t
.gitignore
.travis.yml
CHANGELOG.md
Makefile
README.md
dist.ini
lua-resty-openssl-0.5.2-1.rockspec
valgrind.suppress

README.md

lua-resty-openssl

FFI-based OpenSSL binding for LuaJIT, supporting OpenSSL 1.1 and 1.0.2 series

Build Status luarocks

Table of Contents

Description

lua-resty-openssl is a FFI-based OpenSSL binding library, currently supports OpenSSL 1.1.1, 1.1.0 and 1.0.2 series.

Back to TOC

Status

Production.

Synopsis

This library is greatly inspired by luaossl, while uses the naming conversion closer to original OpenSSL API. For example, a function called X509_set_pubkey in OpenSSL C API will expect to exist as resty.openssl.x509:set_pubkey. CamelCases are replaced to underscore_cases, for exmaple X509_set_serialNumber becomes resty.openssl.x509:set_serial_number. Another difference than luaossl is that errors are never thrown using error() but instead return as last parameter.

Each Lua table returned by new() contains a cdata object ctx. User are not supposed to manully setting ffi.gc or calling corresponding destructor of the ctx struct (like *_free functions).

Back to TOC

resty.openssl

This meta module provides a version sanity check against linked OpenSSL library and returns all exported modules to a table.

return {
  _VERSION = '0.1.0',
  bn = require("resty.openssl.bn"),
  cipher = require("resty.openssl.cipher"),
  digest = require("resty.openssl.digest"),
  hmac = require("resty.openssl.hmac"),
  pkey = require("resty.openssl.pkey"),
  rand = require("resty.openssl.rand"),
  version = require("resty.openssl.version"),
  x509 = require("resty.openssl.x509"),
  altname = require("resty.openssl.x509.altname"),
  chain = require("resty.openssl.x509.chain"),
  csr = require("resty.openssl.x509.csr"),
  crl = require("resty.openssl.x509.crl"),
  extension = require("resty.openssl.x509.extension"),
  name = require("resty.openssl.x509.name"),
  store = require("resty.openssl.x509.store"),
}

Back to TOC

openssl.luaossl_compat

syntax: openssl.luaossl_compat()

Provides luaossl flavored API which uses camelCase naming; user can expect drop in replacement.

For example, pkey:get_parameters is mapped to pkey:getParameters.

Note that not all luaossl API has been implemented, please check readme for source of truth.

Back to TOC

resty.openssl.version

A module to provide version info.

Back to TOC

version_num

The OpenSSL version number.

Back to TOC

version_text

The OpenSSL version text.

Back to TOC

version.version

syntax: text = version.version(types)

Returns various OpenSSL information. Available values for types are:

VERSION
CFLAGS
BUILT_ON
PLATFORM
DIR
ENGINES_DIR
VERSION_STRING
FULL_VERSION_STRING
MODULES_DIR
CPU_INFO

For OpenSSL prior to 1.1.x, only VERSION, CFLAGS, BUILT_ON, PLATFORM and DIR are supported.

local version = require("resty.openssl.version")
ngx.say(string.format("%x", version.version_num))
-- outputs "101000bf"
ngx.say(version.version_text)
-- outputs "OpenSSL 1.1.0k  28 May 2019"
ngx.say(version.version(version.PLATFORM))
-- outputs "darwin64-x86_64-cc"

Back to TOC

OPENSSL_11

A boolean indicates whether the linked OpenSSL is 1.1 series.

Back to TOC

OPENSSL_10

A boolean indicates whether the linked OpenSSL is 1.0 series.

Back to TOC

resty.openssl.pkey

Module to interact with private keys and public keys (EVP_PKEY).

Back to TOC

pkey.new

syntax: pk, err = pkey.new(config)

syntax: pk, err = pkey.new(string, opts?)

syntax: pk, err = pkey.new()

Creates a new pkey instance. The first argument can be:

  1. A config table to create a new PKEY pair. Which defaults to:
pkey.new({
  type = 'RSA',
  bits = 2048,
  exp = 65537
})

to create EC private key:

pkey.new({
  type = 'EC',
  curve = 'primve196v1',
})
  1. A string of private or public key in PEM or DER format; optionally accpet a table opts to explictly load format and key type. When loading a key in PEM format, passphrase or passphrase_cb may be provided to decrypt the key.
pkey.new(pem_or_der_text, {
  format = "*", -- choice of "PEM", "DER" or "*" for auto detect
  type = "*", -- choice of "p"r for privatekey, "pu" for public key and "*" for auto detect
  passphrase = "secret password", -- the PEM encryption passphrase
  passphrase_cb = function()
    return "secret password"
  end, -- the PEM encryption passphrase callback function
}
  1. nil to create a 2048 bits RSA key.
  2. A EVP_PKEY* pointer, to return a wrapped pkey instance. Normally user won't use this approach. User shouldn't free the pointer on their own, since the pointer is not copied.

Back to TOC

pkey.istype

syntax: ok = pkey.istype(table)

Returns true if table is an instance of pkey. Returns false otherwise.

Back to TOC

pkey:get_parameters

syntax: parameters, err = pk:get_parameters()

Returns a table containing the parameters of pkey instance. Currently only n, e and d parameter of RSA key is supported. Each value of the returned table is a resty.openssl.bn instance.

local pk, err = require("resty.openssl.pkey").new()
local parameters, err = pk:get_parameters()
local e = parameters.e
ngx.say(ngx.encode_base64(e:to_binary()))
-- outputs 'AQAB' (65537) by default

Back to TOC

pkey:sign

syntax: signature, err = pk:sign(digest)

Sign a digest using the private key defined in pkey instance. The digest parameter must be a resty.openssl.digest instance. Returns the signed raw binary and error if any.

local pk, err = require("resty.openssl.pkey").new()
local digest, err = require("resty.openssl.digest").new("SHA256")
digest:update("dog")
local signature, err = pk:sign(digest)
ngx.say(ngx.encode_base64(signature))

Back to TOC

pkey:verify

syntax: ok, err = pk:verify(signature, digest)

Verify a signture (which can be generated by pkey:sign). The second argument must be a resty.openssl.digest instance that uses the same digest algorithm as used in sign. ok returns true if verficiation is successful and false otherwise. Note when verfication failed err will not be set.

Back to TOC

pkey:encrypt

syntax: cipher_txt, err = pk:encrypt(txt, padding?)

Encrypts plain text txt with pkey instance, which must loaded a public key.

When key is a RSA key, the function accepts an optional second argument padding which can be:

  pkey.PADDINGS = {
    RSA_PKCS1_PADDING       = 1,
    RSA_SSLV23_PADDING      = 2,
    RSA_NO_PADDING          = 3,
    RSA_PKCS1_OAEP_PADDING  = 4,
    RSA_X931_PADDING        = 5,
    RSA_PKCS1_PSS_PADDING   = 6,
  }

If omitted, padding is default to pkey.PADDINGS.RSA_PKCS1_PADDING.

Back to TOC

pkey:decrypt

syntax: txt, err = pk:decrypt(cipher_txt, padding?)

Decrypts cipher text cipher_txt with pkey instance, which must loaded a private key.

The optional second argument padding has same meaning in pkey:encrypt.

local pkey = require("resty.openssl.pkey")
local privkey, err = pkey.new()
local pub_pem = privkey:to_PEM("public")
local pubkey, err = pkey.new(pub_pem)
local s, err = pubkey:encrypt("🦢", pkey.PADDINGS.RSA_PKCS1_PADDING)
ngx.say(#s)
-- Outputs 256
local decrypted, err = privkey:decrypt(s)
ngx.say(decrypted)
-- Outputs "🦢"

Back to TOC

pkey:to_PEM

syntax: pem, err = pk:to_PEM(private_or_public?)

Outputs private key or public key of pkey instance in PEM-formatted text. The first argument must be a choice of public, PublicKey, private, PrivateKey or nil. By default, it returns the public key.

Back to TOC

resty.openssl.bn

Module to expose BIGNUM structure.

Back to TOC

bn.new

syntax: b, err = bn.new(number?)

Creates a bn instance. The first argument can be a Lua number or nil to creates an empty instance.

Back to TOC

bn.dup

syntax: b, err = bn.dup(bn_ptr_cdata)

Duplicates a BIGNUM* to create a new bn instance.

Back to TOC

bn.istype

syntax: ok = bn.istype(table)

Returns true if table is an instance of bn. Returns false otherwise.

Back to TOC

bn.from_binary

Creates a bn instance from binary string.

local b, err = require("resty.openssl.bn").from_binary(ngx.decode_base64("WyU="))
local bin, err = b:to_binary()
ngx.say(ngx.encode_base64(bin))
-- outputs "WyU="

Back to TOC

bn:to_binary

syntax: bin, err = bn:to_binary()

Export the BIGNUM value in binary string.

Back to TOC

bn:to_hex

syntax: hex, err = bn:to_hex()

Export the BIGNUM value in hex encoded string.

local b, err = require("resty.openssl.bn").new(23333)
local bin, err = b:to_binary()
ngx.say(ngx.encode_base64(bin))
-- outputs "WyU="
local hex, err = b:to_hex()
ngx.say(hex)
-- outputs "5B25"

Back to TOC

resty.openssl.cipher

Module to interact with symmetric cryptography (EVP_CIPHER).

Back to TOC

cipher.new

syntax: d, err = cipher.new(cipher_name)

Creates a cipher instance. cipher_name is a case-insensitive string of cipher algorithm name. To view a list of cipher algorithms implemented, use openssl list -cipher-algorithms.

Back to TOC

cipher.istype

syntax: ok = cipher.istype(table)

Returns true if table is an instance of cipher. Returns false otherwise.

Back to TOC

cipher:encrypt

syntax: s, err = cipher:encrypt(key, iv?, s, no_padding?)

Encrypt the text s with key key and IV iv. Returns the encrypted text in raw binary string and error if any. Optionally accepts a boolean no_padding which tells the cipher to enable or disable padding and default to false (enable padding). If no_padding is true, the length of s must then be a multiple of the block size or an error will occur.

This function is a shorthand of cipher:init plus cipher:final.

Back to TOC

cipher:decrypt

syntax: s, err = cipher:decrypt(key, iv?, s, no_padding?)

Decrypt the text s with key key and IV iv. Returns the decrypted text in raw binary string and error if any. Optionally accepts a boolean no_padding which tells the cipher to enable or disable padding and default to false (enable padding). If no_padding is true, the length of s must then be a multiple of the block size or an error will occur; also, padding in the decrypted text will not be removed.

This function is a shorthand of cipher:init plus cipher:final.

Back to TOC

cipher:init

syntax: ok, err = cipher:init(key, iv?, opts?)

Initialize the cipher with key key and IV iv. The optional third argument is a table consists of:

{
    is_encrypt = false,
    no_padding = false,
}

Calling function is needed before cipher:update and cipher:final but not cipher:encrypt or cipher:decrypt.

Back to TOC

cipher:update

syntax: s, err = cipher:update(partial, ...)

Updates the cipher with one or more strings. If the cipher has larger than block size of data to flush, the function will return a non-empty string as first argument. This function can be used in a streaming fashion to encrypt or decrypt continous data stream.

Back to TOC

cipher:final

syntax: s, err = cipher:final(partial?)

Returns the encrypted or decrypted text in raw binary string, optionally accept one string to encrypt or decrypt.

-- encryption
local c, err = require("resty.openssl.cipher").new("aes256")
c:init(string.rep("0", 32), string.rep("0", 16), {
    is_encrypt = true,
})
c:update("🦢")
local cipher, err = c:final()
ngx.say(ngx.encode_base64(cipher))
-- outputs "vGJRHufPYrbbnYYC0+BnwQ=="
-- OR:
local c, err = require("resty.openssl.cipher").new("aes256")
local cipher, err = c:encrypt(string.rep("0", 32), string.rep("0", 16), "🦢")
ngx.say(ngx.encode_base64(cipher))
-- outputs "vGJRHufPYrbbnYYC0+BnwQ=="

-- decryption
local encrypted = ngx.decode_base64("vGJRHufPYrbbnYYC0+BnwQ==")
local c, err = require("resty.openssl.cipher").new("aes256")
c:init(string.rep("0", 32), string.rep("0", 16), {
    is_encrypt = false,
})
c:update(encrypted)
local cipher, err = c:final()
ngx.say(cipher)
-- outputs "🦢"
-- OR:
local c, err = require("resty.openssl.cipher").new("aes256")
local cipher, err = c:decrypt(string.rep("0", 32), string.rep("0", 16), encrypted)
ngx.say(cipher)
-- outputs "🦢"

Back to TOC

resty.openssl.digest

Module to interact with message digest (EVP_MD_CTX).

Back to TOC

digest.new

syntax: d, err = digest.new(digest_name?)

Creates a digest instance. digest_name is a case-insensitive string of digest algorithm name. To view a list of digest algorithms implemented, use openssl list -digest-algorithms.

If digest_name is omitted, it's default to sha1.

Back to TOC

digest.istype

syntax: ok = digest.istype(table)

Returns true if table is an instance of digest. Returns false otherwise.

Back to TOC

digest:update

syntax: ok, err = digest:update(partial, ...)

Updates the digest with one or more strings.

Back to TOC

digest:final

syntax: str, err = digest:final(partial?)

Returns the digest in raw binary string, optionally accept one string to digest.

local d, err = require("resty.openssl.digest").new("sha256")
d:update("🦢")
local digest, err = d:final()
ngx.say(ngx.encode_base64(digest))
-- outputs "tWW/2P/uOa/yIV1gRJySJLsHq1xwg0E1RWCvEUDlla0="
-- OR:
local d, err = require("resty.openssl.digest").new("sha256")
local digest, err = d:final("🦢")
ngx.say(ngx.encode_base64(digest))
-- outputs "tWW/2P/uOa/yIV1gRJySJLsHq1xwg0E1RWCvEUDlla0="

Back to TOC

resty.openssl.hmac

Module to interact with hash-based message authentication code (HMAC_CTX).

Back to TOC

hmac.new

syntax: h, err = hmac.new(key, digest_name?)

Creates a hmac instance. digest_name is a case-insensitive string of digest algorithm name. To view a list of digest algorithms implemented, use openssl list -digest-algorithms.

If digest_name is omitted, it's default to sha1.

Back to TOC

hmac.istype

syntax: ok = hmac.istype(table)

Returns true if table is an instance of hmac. Returns false otherwise.

Back to TOC

hmac:update

syntax: ok, err = hmac:update(partial, ...)

Updates the HMAC with one or more strings.

Back to TOC

hmac:final

syntax: str, err = hmac:final(partial?)

Returns the HMAC in raw binary string, optionally accept one string to digest.

local d, err = require("resty.openssl.hmac").new("goose", "sha256")
d:update("🦢")
local hmac, err = d:final()
ngx.say(ngx.encode_base64(hmac))
-- outputs "k2UcrRp25tj1Spff89mJF3fAVQ0lodq/tJT53EYXp0c="
-- OR:
local d, err = require("resty.openssl.hmac").new("goose", "sha256")
local hmac, err = d:final("🦢")
ngx.say(ngx.encode_base64(hmac))
-- outputs "k2UcrRp25tj1Spff89mJF3fAVQ0lodq/tJT53EYXp0c="

Back to TOC

resty.openssl.objects

Helpfer module on ASN1_OBJECT.

Back to TOC

objects.obj2table

syntax: tbl = objects.bytes(asn1_obj)

Convert a ASN1_OBJECT pointer to a Lua table where

{
  id: OID of the object,
  nid: NID of the object,
  sn: short name of the object,
  ln: long name of the object,
}

Back to TOC

objects.nid2table

syntax: tbl, err = objects.nid2table(nid)

Convert a NID to a Lua table, returns the same format as objects.obj2table

Back to TOC

objects.txt2nid

syntax: nid, err = objects.txt2nid(txt)

Convert a text representation to NID.

Back to TOC

resty.openssl.rand

Module to interact with random number generator.

Back to TOC

rand.bytes

syntax: str, err = rand.bytes(length)

Generate random bytes with length of length.

Back to TOC

resty.openssl.x509

Module to interact with X.509 certificates.

Back to TOC

x509.new

syntax: crt, err = x509.new(txt?, fmt?)

Creates a x509 instance. txt can be PEM or DER formatted text; fmt is a choice of PEM, DER to load specific format, or * for auto detect.

When txt is omitted, new() creates an empty x509 instance.

Back to TOC

x509.dup

syntax: x509, err = x509.dup(x509_ptr_cdata)

Duplicates a X509* to create a new x509 instance.

Back to TOC

x509.istype

syntax: ok = x509.istype(table)

Returns true if table is an instance of x509. Returns false otherwise.

Back to TOC

x509:digest

syntax: d, err = x509:digest(digest_name?)

Returns a digest of the DER representation of the X509 certificate object in raw binary text.

digest_name is a case-insensitive string of digest algorithm name. To view a list of digest algorithms implemented, use openssl list -digest-algorithms.

If digest_name is omitted, it's default to sha1.

Back to TOC

x509:pubkey_digest

syntax: d, err = x509:pubkey_digest(digest_name?)

Returns a digest of the DER representation of the pubkey in the X509 object in raw binary text.

digest_name is a case-insensitive string of digest algorithm name. To view a list of digest algorithms implemented, use openssl list -digest-algorithms.

If digest_name is omitted, it's default to sha1.

Back to TOC

x509:get_, x509:set_

syntax: ok, err = x509:set_attribute(instance)

syntax: instance, err = x509:get_attribute()

Setters and getters for x509 attributes share the same syntax.

Attribute name Type Description
issuer_name x509.name Issuer of the certificate
not_before number Unix timestamp when certificate is not valid before
not_after number Unix timestamp when certificate is not valid after
pubkey pkey Public key of the certificate
serial_number bn Serial number of the certficate
subject_name x509.name Subject of the certificate
version number Version of the certificate, value is one less than version. For example, 2 represents version 3

Additionally, getters and setters for extensions are also available:

Extension name Type Description
subject_alt_name x509.altname Subject Alternative Name of the certificate, SANs are usually used to define "additional Common Names"
issuer_alt_name x509.altname Issuer Alternative Name of the certificate
basic_constraints table, { ca = bool, pathlen = int} Basic Constriants of the certificate
info_access x509.extension.info_access Authority Information Access of the certificate, contains information like OCSP reponder URL.
crl_distribution_points x509.extension.dist_points CRL Distribution Points of the certificate, contains information like Certificate Revocation List(CRL) URLs.

For all extensions, get_{extension}_critical and set_{extension}_critical is also supported to access the critical flag of the extension.

local x509, err = require("resty.openssl.x509").new()
err = x509:set_not_before(ngx.time())
local not_before, err = x509:get_not_before()
ngx.say(not_before)
-- outputs 1571875065

err = x509:set_basic_constraints_critical(true)

If type is a table, setter requires a table with case-insensitive keys to set; getter returns the value of the given case-insensitive key or a table of all keys if no key provided.

local x509, err = require("resty.openssl.x509").new()
err = x509:set_basic_constraints({
  cA = false,
  pathlen = 0,
})

ngx.say(x509:get_basic_constraints("pathlen"))
-- outputs 0

ngx.say(x509:get_basic_constraints())
-- outputs '{"ca":false,"pathlen":0}'

Note that user may also access the certain extension by x509:get_extension and x509:set_extension, while the later two function returns or requires extension instead. User may use getter and setters listed here if modification of current extensions is needed; use x509:get_extension or x509:set_extension if user are adding or replacing the whole extension or getters/setters are not implemented. If the getter returned a type of x509.* instance, it can be converted to a extension instance by extension:from_data, and thus used by x509:get_extension and x509:set_extension

Back to TOC

x509:get_lifetime

syntax: not_before, not_after, err = x509:get_lifetime()

A shortcut of x509:get_not_before plus x509:get_not_after

Back to TOC

x509:set_lifetime

syntax: ok, err = x509:set_lifetime(not_before, not_after)

A shortcut of x509:set_not_before plus x509:set_not_after.

Back to TOC

x509:get_extension

syntax: extension, pos, err = x509:get_extension(nid_or_txt, last_pos?)

Get X.509 extension matching the given NID to certificate, returns a resty.openssl.x509.extension instance and the found position.

If last_pos is defined, the function searchs from that position; otherwise it finds from beginning. Index is 1-based.

local ext, pos, err = x509:get_extension("keyUsage")

Back to TOC

x509:add_extension

syntax: ok, err = x509:add_extension(extension)

Adds an X.509 extension to certificate, the first argument must be a resty.openssl.x509.extension instance.

local extension, err = require("resty.openssl.extension").new({
    "keyUsage", "critical,keyCertSign,cRLSign",
})
local x509, err = require("resty.openssl.x509").new()
local ok, err = x509:add_extension(extension)

Back to TOC

x509:set_extension

syntax: ok, err = x509:set_extension(extension, last_pos?)

Adds an X.509 extension to certificate, the first argument must be a resty.openssl.x509.extension instance. The difference from x509:add_extension is that in this function if a extension with same type already exists, the old extension will be replaced.

If last_pos is defined, the function replaces the same extension from that position; otherwise it finds from beginning. Index is 1-based. Returns nil, nil if not found.

Note this function is not thread-safe.

Back to TOC

x509:get_critical

syntax: ok, err = x509:get_critical(nid_or_txt)

Get critical flag of the X.509 extension matching the given NID from certificate.

Back to TOC

x509:set_critical

syntax: ok, err = x509:set_critical(nid_or_txt, crit?)

Set critical flag of the X.509 extension matching the given NID to certificate.

Back to TOC

x509:get_ocsp_url

syntax: url_or_urls, err = x509:get_ocsp_url(return_all?)

Get OCSP URL(s) of the X.509 object. If return_all is set to true, returns a table containing all OCSP URLs; otherwise returns a string with first OCSP URL found. Returns nil if the extension is not found.

Back to TOC

x509:get_crl_url

syntax: url_or_urls, err = x509:get_crl_url(return_all?)

Get CRL URL(s) of the X.509 object. If return_all is set to true, returns a table containing all CRL URLs; otherwise returns a string with first CRL URL found. Returns nil if the extension is not found.

Back to TOC

x509:sign

syntax: ok, err = x509:sign(pkey, digest?)

Sign the certificate using the private key specified by pkey, which must be a resty.openssl.pkey that stores private key. Optionally accept digest parameter to set digest method, whichmust be a resty.openssl.digest instance. Returns a boolean indicating if signing is successful and error if any.

Back to TOC

x509:verify

syntax: ok, err = x509:verify(pkey)

Verify the certificate signature using the public key specified by pkey, which must be a resty.openssl.pkey. Returns a boolean indicating if verification is successful and error if any.

Back to TOC

x509:tostring

syntax: str, err = x509:tostring(fmt?)

Outputs certificate in PEM-formatted text or DER-formatted binary. The first argument can be a choice of PEM or DER; when omitted, this function outputs PEM by default.

Back to TOC

x509:to_PEM

syntax: pem, err = x509:to_PEM()

Outputs the certificate in PEM-formatted text.

Back to TOC

resty.openssl.x509.csr

Module to interact with certificate signing request (X509_REQ).

Back to TOC

csr.new

syntax: csr, err = csr.new(txt?, fmt?)

Create an empty csr instance. txt can be PEM or DER formatted text; fmt is a choice of PEM, DER to load specific format, or * for auto detect.

When txt is omitted, new() creates an empty csr instance.

Back to TOC

csr.istype

syntax: ok = csr.istype(table)

Returns true if table is an instance of csr. Returns false otherwise.

Back to TOC

csr:get_, csr:set_

syntax: ok, err = csr:set_attribute(instance)

syntax: instance, err = csr:get_attribute()

Setters and getters for x509 attributes share the same syntax.

Attribute name Type Description
pubkey pkey Public key of the certificate request
subject_name x509.name Subject of the certificate request
version number Version of the certificate request, value is one less than version. For example, 2 represents version 3

Additionally, getters and setters for extensions are also available:

Extension name Type Description
subject_alt_name x509.altname Subject Alternative Name of the certificate request, SANs are usually used to define "additional Common Names"
local csr, err = require("resty.openssl.csr").new()
err = csr:set_version(3)
local version, err = csr:get_version()
ngx.say(version)
-- outputs 3

Back to TOC

csr:set_subject_alt

Same as csr:set_subject_alt_name, this function is deprecated to align with naming convension with other functions.

Back to TOC

csr:sign

syntax: ok, err = csr:sign(pkey, digest?)

Sign the certificate request using the private key specified by pkey, which must be a resty.openssl.pkey that stores private key. Optionally accept digest parameter to set digest method, whichmust be a resty.openssl.digest instance. Returns a boolean indicating if signing is successful and error if any.

Back to TOC

csr:verify

syntax: ok, err = csr:verify(pkey)

Verify the CSR signature using the public key specified by pkey, which must be a resty.openssl.pkey. Returns a boolean indicating if verification is successful and error if any.

Back to TOC

csr:tostring

syntax: str, err = csr:tostring(fmt?)

Outputs certificate request in PEM-formatted text or DER-formatted binary. The first argument can be a choice of PEM or DER; when omitted, this function outputs PEM by default.

Back to TOC

csr:to_PEM

syntax: pem, err = csr:to_PEM(?)

Outputs CSR in PEM-formatted text.

Back to TOC

resty.openssl.crl

Module to interact with X509_CRL(certificate revocation list).

Back to TOC

crl.new

syntax: crt, err = crl.new(txt?, fmt?)

Creates a crl instance. txt can be PEM or DER formatted text; fmt is a choice of PEM, DER to load specific format, or * for auto detect.

When txt is omitted, new() creates an empty crl instance.

Back to TOC

crl.dup

syntax: crl, err = crl.dup(crl_ptr_cdata)

Duplicates a X509_CRL* to create a new crl instance.

Back to TOC

crl.istype

syntax: ok = crl.istype(table)

Returns true if table is an instance of crl. Returns false otherwise.

Back to TOC

crl:get_, crl:set_

syntax: ok, err = crl:set_attribute(instance)

syntax: instance, err = crl:get_attribute()

Setters and getters for crl attributes share the same syntax.

Attribute name Type Description
issuer_name x509.name Issuer of the CRL
last_update number Unix timestamp when CRL is not valid before
next_update number Unix timestamp when CRL is not valid after
version number Version of the certificate, value is one less than version. For example, 2 represents version 3

Additionally, getters and setters for extensions are also available:

Extension name Type Description

For all extensions, get_{extension}_critical and set_{extension}_critical is also supported to access the critical flag of the extension.

local crl, err = require("resty.openssl.crl").new()
err = crl:set_next_update(ngx.time())
local not_before, err = crl:get_next_update()
ngx.say(not_before)
-- outputs 1571875065

Note that user may also access the certain extension by crl:get_extension and crl:set_extension, while the later two function returns or requires extension instead. User may use getter and setters listed here if modification of current extensions is needed; use crl:get_extension or crl:set_extension if user are adding or replacing the whole extension or getters/setters are not implemented. If the getter returned a type of crl.* instance, it can be converted to a extension instance by extension:from_data, and thus used by crl:get_extension and crl:set_extension

Back to TOC

crl:get_extension

syntax: extension, pos, err = crl:get_extension(nid_or_txt, last_pos?)

Get X.509 extension matching the given NID to CRL, returns a resty.openssl.x509.extension instance and the found position.

If last_pos is defined, the function searchs from that position; otherwise it finds from beginning. Index is 1-based.

Back to TOC

crl:add_extension

syntax: ok, err = crl:add_extension(extension)

Adds an X.509 extension to CRL, the first argument must be a resty.openssl.x509.extension instance.

Back to TOC

crl:set_extension

syntax: ok, err = crl:set_extension(extension, last_pos?)

Adds an X.509 extension to CRL, the first argument must be a resty.openssl.x509.extension instance. The difference from crl:add_extension is that in this function if a extension with same type already exists, the old extension will be replaced.

If last_pos is defined, the function replaces the same extension from that position; otherwise it finds from beginning. Index is 1-based. Returns nil, nil if not found.

Note this function is not thread-safe.

Back to TOC

crl:get_critical

syntax: ok, err = crl:get_critical(nid_or_txt)

Get critical flag of the X.509 extension matching the given NID from CRL.

Back to TOC

crl:set_critical

syntax: ok, err = crl:set_critical(nid_or_txt, crit?)

Set critical flag of the X.509 extension matching the given NID to CRL.

Back to TOC

crl:sign

syntax: ok, err = crl:sign(pkey, digest?)

Sign the CRL using the private key specified by pkey, which must be a resty.openssl.pkey that stores private key. Optionally accept digest parameter to set digest method, whichmust be a resty.openssl.digest instance. Returns a boolean indicating if signing is successful and error if any.

Back to TOC

crl:verify

syntax: ok, err = crl:verify(pkey)

Verify the CRL signature using the public key specified by pkey, which must be a resty.openssl.pkey. Returns a boolean indicating if verification is successful and error if any.

Back to TOC

crl:tostring

syntax: str, err = crl:tostring(fmt?)

Outputs CRL in PEM-formatted text or DER-formatted binary. The first argument can be a choice of PEM or DER; when omitted, this function outputs PEM by default.

Back to TOC

crl:to_PEM

syntax: pem, err = crl:to_PEM()

Outputs the CRL in PEM-formatted text.

Back to TOC

resty.openssl.x509.name

Module to interact with X.509 names.

Back to TOC

name.new

syntax: name, err = name.new()

Creates an empty name instance.

Back to TOC

name.dup

syntax: name, err = name.dup(name_ptr_cdata)

Duplicates a X509_NAME* to create a new name instance.

Back to TOC

name.istype

syntax: ok = name.istype(table)

Returns true if table is an instance of name. Returns false otherwise.

Back to TOC

name:add

syntax: name, err = name:add(nid_text, txt)

Adds an ASN.1 object to name. First arguments in the text representation of NID. Second argument is the plain text value for the ASN.1 object.

Returns the name instance itself on success, or nil and an error on failure.

This function can be called multiple times in a chained fashion.

local name, err = require("resty.openssl.x509.name").new()
local _, err = name:add("CN", "example.com")

_, err = name
    :add("C", "US")
    :add("ST", "California")
    :add("L", "San Francisco")

Back to TOC

name:find

syntax: obj, pos, err = name:find(nid_text, last_pos?)

Finds the ASN.1 object with the given text representation of NID from the postition of last_pos. By omitting the last_pos parameter, find finds from the beginning.

Returns the object in a table as same format as decribed here, the position of the found object and error if any. Index is 1-based. Returns nil, nil if not found.

local name, err = require("resty.openssl.x509.name").new()
local _, err = name:add("CN", "example.com")
                    :add("CN", "example2.com")

local obj, pos, err = name:find("CN")
ngx.say(obj.blob, " at ", pos)
-- outputs "example.com at 1"
local obj, pos, err = name:find("2.5.4.3", 1)
ngx.say(obj.blob, " at ", pos)
-- outputs "example2.com at 2"

Back to TOC

name:__metamethods

syntax: for k, obj in pairs(name)

syntax: len = #name

syntax: k, v = name[i]

Access the underlying objects as it's a Lua table. Make sure your LuaJIT compiled with -DLUAJIT_ENABLE_LUA52COMPAT flag; otherwise use all, each, index and count instead.

See also functions for stack-like objects.

Each returned object is a table where:

{
  id: OID of the object,
  nid: NID of the object,
  sn: short name of the object,
  ln: long name of the object,
  blob: value of the object,
}
local name, err = require("resty.openssl.x509.name").new()
local _, err = name:add("CN", "example.com")

for k, obj in pairs(name) do
  ngx.say(k, ":", require("cjson").encode(obj))
end
-- outputs 'CN: {"sn":"CN","id":"2.5.4.3","nid":13,"blob":"3.example.com","ln":"commonName"}'

Back to TOC

resty.openssl.x509.altname

Module to interact with GENERAL_NAMES, an extension to X.509 names.

Back to TOC

altname.new

syntax: altname, err = altname.new()

Creates an empty altname instance.

Back to TOC

altname.istype

syntax: altname = digest.istype(table)

Returns true if table is an instance of altname. Returns false otherwise.

Back to TOC

altname:add

syntax: altname, err = altname:add(key, value)

Adds a name to altname stack, first argument is case-insensitive and can be one of

RFC822Name
RFC822
RFC822
UniformResourceIdentifier
URI
DNSName
DNS
IPAddress
IP
DirName

This function can be called multiple times in a chained fashion.

local altname, err = require("resty.openssl.x509").new()
local _, err = altname:add("DNS", "example.com")

_, err = altname
    :add("DNS", "2.example.com")
    :add("DnS", "3.example.com")

Back to TOC

altname:__metamethods

syntax: for k, obj in pairs(altname)

syntax: len = #altname

syntax: k, v = altname[i]

Access the underlying objects as it's a Lua table. Make sure your LuaJIT compiled with -DLUAJIT_ENABLE_LUA52COMPAT flag; otherwise use all, each, index and count instead.

See also functions for stack-like objects.

Back to TOC

resty.openssl.x509.extension

Module to interact with X.509 extensions.

Back to TOC

extension.new

syntax: ext, err = extension.new(name, value, data)

Creates a new extension instance. name and value are strings in OpenSSL arbitrary extension format.

data can be a table or nil. Where data is a table, the following key will be looked up:

data = {
    issuer = resty.openssl.x509 instance,
    subject = resty.openssl.x509 instance,
    request = resty.opensl.csr instance,
}

Example:

local x509, err = require("resty.openssl.x509").new()
local extension = require("resty.openssl.x509.extension")
local ext, err = extension.new("extendedKeyUsage", "serverAuth,clientAuth")
ext, err =  extension.new("subjectKeyIdentifier", "hash", {
    subject = crt
})

Back to TOC

extension.dup

syntax: ext, err = extension.dup(extension_ptr_cdata)

Creates a new extension instance from X509_EXTENSION* pointer.

Back to TOC

extension.from_data

syntax: ext, ok = extension.from_data(table, nid, crit?)

Creates a new extension instance. table can be instance of:

nid is a number of NID and crit is the critical flag of the extension.

Back to TOC

extension.istype

syntax: ok = extension.istype(table)

Returns true if table is an instance of extension. Returns false otherwise.

Back to TOC

extension:get_critical

syntax: crit, err = extension:get_critical()

Returns true if extension is critical. Returns false otherwise.

Back to TOC

extension:set_critical

syntax: ok, err = extension:set_critical(crit)

Set the critical flag of the extension.

Back to TOC

extension:get_object

syntax: obj = extension:get_object()

Returns the name of extension as ASN.1 Object. User can further use helper functions in resty.openssl.objects to print human readable texts.

Back to TOC

extension:text

syntax: txt, err = extension:text()

Returns the text representation of extension

local objects = require "resty.openssl.objects"
ngx.say(cjson.encode(objects.obj2table(extension:get_object())))
-- outputs {"ln":"X509v3 Subject Key Identifier","nid":82,"sn":"subjectKeyIdentifier","id":"2.5.29.14"}
ngx.say(extension:text())
-- outputs C9:C2:53:61:66:9D:5F:AB:25:F4:26:CD:0F:38:9A:A8:49:EA:48:A9

Back to TOC

extension:tostring

syntax: txt, err = extension:tostring()

Same as extension:text.

Back to TOC

resty.openssl.x509.extension.dist_points

Module to interact with CRL Distribution Points(DIST_POINT stack).

Back to TOC

dist_points.new

syntax: dp, err = dist_points.new()

Creates a new dist_points instance.

Back to TOC

dist_points.dup

syntax: dp, err = dist_points.dup(dist_points_ptr_cdata)

Duplicates a STACK_OF(DIST_POINT) to create a new dist_points instance. The function creates a new stack and increases reference count for all elements by 1. But it won't duplicate the elements themselves.

Back to TOC

dist_points.istype

syntax: ok = dist_points.istype(table)

Returns true if table is an instance of dist_points. Returns false otherwise.

Back to TOC

dist_points:__metamethods

syntax: for i, obj in ipairs(dist_points)

syntax: len = #dist_points

syntax: obj = dist_points[i]

Access the underlying objects as it's a Lua table. Make sure your LuaJIT compiled with -DLUAJIT_ENABLE_LUA52COMPAT flag; otherwise use all, each, index and count instead.

See also functions for stack-like objects.

Back to TOC

resty.openssl.x509.extension.info_access

Module to interact with Authority Information Access data (AUTHORITY_INFO_ACCESS, ACCESS_DESCRIPTION stack).

Back to TOC

info_access.new

syntax: aia, err = info_access.new()

Creates a new info_access instance.

Back to TOC

info_access.dup

syntax: aia, err = info_access.dup(info_access_ptr_cdata)

Duplicates a AUTHORITY_INFO_ACCESS to create a new info_access instance. The function creates a new stack and increases reference count for all elements by 1. But it won't duplicate the elements themselves.

Back to TOC

info_access.istype

syntax: ok = info_access.istype(table)

Returns true if table is an instance of info_access. Returns false otherwise.

Back to TOC

info_access:add

syntax: ok, err = info_access:add(x509)

Add a x509 object to the info_access. The first argument must be a resty.openssl.x509 instance.

Back to TOC

info_access:__metamethods

syntax: for i, obj in ipairs(info_access)

syntax: len = #info_access

syntax: obj = info_access[i]

Access the underlying objects as it's a Lua table. Make sure your LuaJIT compiled with -DLUAJIT_ENABLE_LUA52COMPAT flag; otherwise use all, each, index and count instead.

See also functions for stack-like objects.

Back to TOC

resty.openssl.x509.chain

Module to interact with X.509 stack.

Back to TOC

chain.new

syntax: ch, err = chain.new()

Creates a new chain instance.

Back to TOC

chain.dup

syntax: ch, err = chain.dup(chain_ptr_cdata)

Duplicates a STACK_OF(X509) to create a new chain instance. The function creates a new stack and increases reference count for all elements by 1. But it won't duplicate the elements themselves.

Back to TOC

chain.istype

syntax: ok = chain.istype(table)

Returns true if table is an instance of chain. Returns false otherwise.

Back to TOC

chain:add

syntax: ok, err = chain:add(x509)

Add a x509 object to the chain. The first argument must be a resty.openssl.x509 instance.

Back to TOC

chain:__metamethods

syntax: for i, obj in ipairs(chain)

syntax: len = #chain

syntax: obj = chain[i]

Access the underlying objects as it's a Lua table. Make sure your LuaJIT compiled with -DLUAJIT_ENABLE_LUA52COMPAT flag; otherwise use all, each, index and count instead.

See also functions for stack-like objects.

Back to TOC

resty.openssl.x509.store

Module to interact with X.509 certificate store (X509_STORE).

Back to TOC

store.new

syntax: st, err = store.new()

Creates a new store instance.

Back to TOC

store.istype

syntax: ok = store.istype(table)

Returns true if table is an instance of store. Returns false otherwise.

Back to TOC

store:use_default

syntax: ok, err = store:use_default()

Loads certificates into the X509_STORE from the hardcoded default paths.

Note that to load "default" CAs correctly, usually you need to install a CA certificates bundle. For example, the package in Debian/Ubuntu is called ca-certificates.

Back to TOC

store:add

syntax: ok, err = store:add(x509_or_crl)

Adds a X.509 or a CRL object into store. The argument must be a resty.openssl.x509 instance or a resty.openssl.x509.store instance.

Back to TOC

store:load_file

syntax: ok, err = store:load_file(path)

Loads a X.509 certificate on file system into store.

Back to TOC

store:load_directory

syntax: ok, err = store:load_directory(path)

Loads a directory of X.509 certificates on file system into store. The certificates in the directory must be in hashed form, as documented in X509_LOOKUP_hash_dir(3).

Back to TOC

store:verify

syntax: chain, err = store:verify(x509, chain?, return_chain?)

Verifies a X.509 object with the store. The first argument must be resty.openssl.x509 instance. Optionally accept a validation chain as second argument, which must be a resty.openssl.x509.chain instance.

If verification succeed, and return_chain is set to true, returns the proof of validation as a resty.openssl.x509.chain; otherwise returns true only. If verification failed, returns nil and error explaining the reason.

Back to TOC

Functions for stack-like objects

Back to TOC

metamethods

syntax: for k, obj in pairs(x)

syntax: for k, obj in ipairs(x)

syntax: len = #x

syntax: obj = x[i]

Access the underlying objects as it's a Lua table. Make sure your LuaJIT compiled with -DLUAJIT_ENABLE_LUA52COMPAT flag.

Each object may only support either pairs or ipairs. Index is 1-based.

local name, err = require("resty.openssl.x509.name").new()
local _, err = name:add("CN", "example.com")

for k, obj in pairs(name) do
  ngx.say(k, ":", require("cjson").encode(obj))
end
-- outputs 'CN: {"sn":"CN","id":"2.5.4.3","nid":13,"blob":"3.example.com","ln":"commonName"}'

Back to TOC

each

syntax: iter = x:each()

Return an iterator to traverse objects. Use this while LUAJIT_ENABLE_LUA52COMPAT is not enabled.

local name, err = require("resty.openssl.x509.name").new()
local _, err = name:add("CN", "example.com")

local iter = name:each()
while true do
  local k, obj = iter()
  if not k then
    break
  end
end

Back to TOC

all

syntax: objs, err = x:all()

Returns all objects in the table. Use this while LUAJIT_ENABLE_LUA52COMPAT is not enabled.

Back to TOC

count

syntax: len = x:count()

Returns count of objects of the table. Use this while LUAJIT_ENABLE_LUA52COMPAT is not enabled.

Back to TOC

index

syntax: obj = x:index(i)

Returns objects at index of i of the table, index is 1-based. If index is out of bound, nil is returned.

Back to TOC

General rules on garbage collection

  • The creator set the GC handler; the user must not free it.
  • For a stack:
    • If it's created by new(): set GC handler to sk_TYPE_pop_free
      • The gc handler for elements being added to stack should not be set. Instead, rely on the gc handler of the stack to free each individual elements.
    • If it's created by dup() (shallow copy):
      • If elements support reference counter (like X509): increase ref count for all elements by 1; set GC handler to sk_TYPE_pop_free.
      • If not, set GC handler to sk_free
        • Additionally, the stack duplicates the element when it's added to stack, a GC handler for the duplicate must be set. But a reference should be kept in Lua land to prevent premature gc of individual elements. (See x509.altname)

Compatibility

Although only a small combinations of CPU arch and OpenSSL version are tested, the library should function well as long as the linked OpenSSL library is API compatible. This means the same name of functions are exported with same argument types.

For OpenSSL 1.0.2 series however, binary/ABI compatibility must be ensured as some struct members are accessed directly. They are accessed by memory offset in assembly.

OpenSSL keeps ABI/binary compatibility with minor releases or letter releases. So all structs offsets and macro constants are kept same.

If you plan to use this library on an untested version of OpenSSL (like custom builds or pre releases), this can be a good source to consult.

Back to TOC

Copyright and License

This module is licensed under the BSD license.

Copyright (C) 2019-2020, by fffonion fffonion@gmail.com.

All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Back to TOC

See Also

Back to TOC

You can’t perform that action at this time.