Pular para conteúdo

openssl: Binding OpenSSL baseado em FFI para nginx-module-lua

Instalação

Se você ainda não configurou a assinatura do repositório RPM, cadastre-se. Em seguida, você pode prosseguir com as seguintes etapas.

CentOS/RHEL 7 ou Amazon Linux 2

yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install lua-resty-openssl

CentOS/RHEL 8+, Fedora Linux, Amazon Linux 2023

dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-openssl

Para usar esta biblioteca Lua com NGINX, certifique-se de que o nginx-module-lua esteja instalado.

Este documento descreve o lua-resty-openssl v1.9.0 lançado em 14 de agosto de 2026.


Binding OpenSSL baseado em FFI para LuaJIT, com suporte para OpenSSL 3, 4 e a série 1.1.1.

O suporte para OpenSSL 1.1.0, 1.0.2 e BoringSSL foi descontinuado, mas ainda está disponível no branch 0.x.

Build Status luarocks opm

Descrição

lua-resty-openssl é uma biblioteca de binding OpenSSL baseada em FFI, atualmente suporta OpenSSL 3.x, 4.x e a série 1.1.1.

Sinopse

Esta biblioteca é fortemente inspirada em luaossl e usa uma conversão de nomenclatura mais próxima da API OpenSSL original. Por exemplo, uma função chamada X509_set_pubkey na API C do OpenSSL é exposta como resty.openssl.x509:set_pubkey. CamelCases são substituídos por underscore_cases, por exemplo, X509_set_serialNumber torna-se resty.openssl.x509:set_serial_number. Outra diferença do luaossl é que erros nunca são lançados usando error(), mas sim retornados como último parâmetro.

Cada tabela Lua retornada por new() contém um objeto cdata ctx. Os usuários não devem definir manualmente ffi.gc ou chamar o destrutor correspondente da struct ctx (como funções *_free).

resty.openssl

Este módulo meta fornece uma verificação de sanidade de versão contra a biblioteca OpenSSL vinculada.

openssl.load_library

sintaxe: name, err = openssl.load_library()

Tenta carregar as bibliotecas compartilhadas OpenSSL. Esta função tenta alguns padrões de nome de biblioteca conhecidos e retorna o nome da biblioteca crypto quando é carregada com sucesso, ou um erro, se houver.

Quando executado dentro do CLI resty ou OpenResty com SSL habilitado, chamar esta função não é necessário.

openssl.load_modules

sintaxe: openssl.load_modules()

Carrega todos os submódulos disponíveis no módulo atual:

  bn = require("resty.openssl.bn"),
  cipher = require("resty.openssl.cipher"),
  digest = require("resty.openssl.digest"),
  hmac = require("resty.openssl.hmac"),
  kdf = require("resty.openssl.kdf"),
  pkey = require("resty.openssl.pkey"),
  objects = require("resty.openssl.objects"),
  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"),
  extensions = require("resty.openssl.x509.extensions"),
  name = require("resty.openssl.x509.name"),
  store = require("resty.openssl.x509.store"),
  ssl = require("resty.openssl.ssl"),
  ssl_ctx = require("resty.openssl.ssl_ctx"),

A partir do OpenSSL 3.0, provider, mac e ctx também estão disponíveis.

openssl.luaossl_compat

sintaxe: openssl.luaossl_compat()

Fornece uma API no estilo luaossl que usa nomenclatura camelCase; os usuários podem esperar uma substituição direta.

Por exemplo, pkey:get_parameters é mapeado para pkey:getParameters.

Observe que nem toda a API luaossl foi implementada; consulte o readme para obter a fonte da verdade.

openssl.get_fips_mode

sintaxe: enabled = openssl.get_fips_mode()

Retorna um booleano indicando se o modo FIPS está habilitado.

openssl.set_fips_mode

sintaxe: ok, err = openssl.set_fips_mode(enabled)

Ativa ou desativa o modo FIPS.

O lua-resty-openssl suporta os seguintes modos:

Série OpenSSL 1.0.2 com módulo fips 2.0

Compile o módulo de acordo com a política de segurança,

Provedor FIPS OpenSSL 3

Consulte https://wiki.openssl.org/index.php/OpenSSL_3.0 Seção 7 Compile o provedor de acordo com o guia, instale o fipsmodule.cnf que corresponde ao hash do provedor FIPS fips.so.

A partir do OpenSSL 3.0, esta função também ativa e desativa as propriedades padrão para funções EVP. Quando ativado, todos os aplicativos que usam a API EVP_* serão redirecionados para implementações compatíveis com FIPS e não terão acesso a algoritmos não compatíveis com FIPS.

Chamar esta função é equivalente a carregar o provedor fips e chamar openssl.set_default_properties("fips=yes").

Se o provedor FIPS estiver carregado, mas as propriedades padrão não estiverem definidas, use o seguinte para buscar explicitamente a implementação FIPS.

local provider = require "resty.openssl.provider"
assert(provider.load("fips"))
local cipher = require "resty.openssl.cipher"
local c = assert(cipher.new("aes256"))
print(c:get_provider_name()) -- imprime "default"
local c = assert(cipher.new("aes256", "fips=yes"))
print(c:get_provider_name()) -- imprime "fips"

openssl.get_fips_version_text

sintaxe: text, err = openssl.get_fips_version_text()

Retorna o texto da versão do módulo FIPS, disponível no OpenSSL 3.0 ou posterior.

openssl.set_default_properties

sintaxe: ok, err = openssl.set_default_properties(props)

Define as propriedades padrão para todas as futuras buscas de algoritmos EVP, implícitas e explícitas. Consulte "ALGORITHM FETCHING" em crypto(7) para obter informações sobre busca implícita e explícita.

openssl.list_cipher_algorithms

sintaxe: ret = openssl.list_cipher_algorithms(hide_provider?)

Retorna os algoritmos de cifra disponíveis em um array. Defina hide_provider como true para ocultar o nome do provedor do resultado.

openssl.list_digest_algorithms

sintaxe: ret = openssl.list_digest_algorithms(hide_provider?)

Retorna os algoritmos de digest disponíveis em um array. Defina hide_provider como true para ocultar o nome do provedor do resultado.

openssl.list_mac_algorithms

sintaxe: ret = openssl.list_mac_algorithms(hide_provider?)

Retorna os algoritmos MAC disponíveis em um array. Defina hide_provider como true para ocultar o nome do provedor do resultado.

openssl.list_kdf_algorithms

sintaxe: ret = openssl.list_kdf_algorithms(hide_provider?)

Retorna os algoritmos KDF disponíveis em um array. Defina hide_provider como true para ocultar o nome do provedor do resultado.

openssl.list_ssl_ciphers

sintaxe: cipher_string, err = openssl.list_ssl_ciphers(cipher_list?, ciphersuites?, protocol?)

Retorna os ciphers SSL padrão como uma string. cipher_list (anterior ao TLSv1.3) e ciphersuites (TLSv1.3) podem ser usados para expandir as configurações de cipher que correspondem ao protocol. O OpenSSL 4.x rejeita "SSLv3" porque o suporte a SSLv3 foi removido.

openssl.list_ssl_ciphers()
openssl.list_ssl_ciphers("ECDHE-ECDSA-AES128-SHA")
openssl.list_ssl_ciphers("ECDHE-ECDSA-AES128-SHA", nil, "TLSv1.2")
openssl.list_ssl_ciphers("ECDHE-ECDSA-AES128-SHA", "TLS_CHACHA20_POLY1305_SHA256", "TLSv1.3")

resty.openssl.ctx

Um módulo para fornecer alternâncias de contexto OSSL_LIB_CTX.

OSSL_LIB_CTX é um tipo de contexto de biblioteca OpenSSL interno. Os aplicativos podem alocar o seu próprio, mas também podem usar NULL para usar um contexto padrão com funções que recebem um argumento OSSL_LIB_CTX.

Consulte OSSL_LIB_CTX.3 para leitura mais aprofundada.

O contexto é atualmente efetivo nos seguintes módulos:

Este módulo está disponível no OpenSSL 3.0 ou posterior.

ctx.new

sintaxe: ok, err = ctx.new(request_context_only?, conf_file?)

Cria um novo contexto e o usa como contexto padrão para este módulo. Quando request_context_only é definido como true, o contexto é usado apenas dentro do contexto da requisição atual. conf_file pode opcionalmente especificar um arquivo de configuração OpenSSL para criar o contexto.

O contexto criado é automaticamente liberado com seu ciclo de vida determinado.

-- inicializa uma instância de cifra AES a partir da implementação do provedor fornecido apenas
-- para a requisição atual, sem interferir em outras partes do código
-- ou em requisições futuras de usar o mesmo algoritmo.
assert(require("resty.openssl.ctx").new(true))
local p = assert(require("resty.openssl.provider").load("myprovider"))
local c = require("resty.openssl.cipher").new("aes256")
print(c:encrypt(string.rep("0", 32), string.rep("0", 16), "🦢"))
-- não precisa liberar provider e ctx, eles são coletados automaticamente pelo GC

ctx.free

sintaxe: ctx.free(request_context_only?)

Libera o contexto que foi criado anteriormente por ctx.new.

resty.openssl.err

Um módulo para fornecer mensagens de erro.

err.format_error

sintaxe: msg = err.format_error(ctx_msg?, return_code?, all_errors?)

sintaxe: msg = err.format_all_errors(ctx_msg?, return_code?)

Retorna a mensagem de erro mais recente do último código de erro. Os erros são formatados como:

[ctx_msg]: https://github.com/fffonion/lua-resty-openssl/blob/1.9.0/code: [return_code]: error:[error code]:[library name]:[func name]:[reason string]:[file name]:[line number]:

Para versões do OpenSSL anteriores à 3.0, os erros são formatados como:

[ctx_msg]: https://github.com/fffonion/lua-resty-openssl/blob/1.9.0/code: [return_code]: [file name]:[line number]:error:[error code]:[library name]:[func name]:[reason string]:

Se all_errors for definido como true, todos os erros, não apenas o mais recente, serão retornados em uma única string. Todos os erros lançados por esta biblioteca internamente lançam apenas o erro mais recente.

Por exemplo:

local f = io.open("t/fixtures/ec_key_encrypted.pem"):read("*a")
local privkey, err = require("resty.openssl.pkey").new(f, {
    format = "PEM",
    type = "pr",
    passphrase = "wrongpasswrod",
})
ngx.say(err)
-- pkey.new:load_key: error:4800065:PEM routines:PEM_do_header:bad decrypt:crypto/pem/pem_lib.c:467:

err.get_last_error_code

sintaxe: code = err.get_last_error_code()

Retorna o último código de erro.

err.get_lib_error_string

sintaxe: lib_error_message = err.get_lib_error_string(code?)

Retorna o nome da biblioteca do último código de erro como string. Se code for definido, retorna o nome da biblioteca correspondente ao código de erro fornecido.

err.get_reason_error_string

sintaxe: reason_error_message = err.get_reason_error_string(code?)

Retorna o motivo do último código de erro como string. Se code for definido, retorna o motivo correspondente ao código de erro fornecido.

resty.openssl.version

Um módulo para fornecer informações de versão.

resty.openssl.provider

Módulo para interagir com provedores. Este módulo funciona apenas no OpenSSL 3.0 ou posterior.

provider.load

sintaxe: pro, err = provider.load(name, try?)

Carrega o provedor com o name. Se try for definido como true, o OpenSSL não desabilitará os provedores de fallback se o provedor não puder ser carregado e inicializado. Se o provedor carregar com sucesso, no entanto, os provedores de fallback são desabilitados.

Por padrão, esta função carrega o provedor no contexto padrão, o que significa que afetará outros aplicativos no mesmo processo que usam o contexto padrão também. Se tal comportamento não for desejado, considere usar ctx para carregar o provedor apenas em um escopo limitado.

provider.istype

sintaxe: ok = pkey.provider(table)

Retorna true se a tabela for uma instância de provider. Retorna false caso contrário.

provider.is_available

sintaxe: ok, err = provider.is_available(name)

Verifica se um provedor nomeado está disponível para uso.

provider.set_default_search_path

sintaxe: ok, err = provider.set_default_search_path(name)

Especifica o caminho de pesquisa padrão a ser usado para procurar provedores.

provider:unload

sintaxe: ok, err = pro:unload(name)

Descarrega um provedor que foi carregado anteriormente por provider.load.

provider:self_test

sintaxe: ok, err = pro:self_test(name)

Executa os autotestes de um provedor sob demanda. Se os autotestes falharem, o provedor falhará em fornecer quaisquer serviços e algoritmos adicionais.

provider:get_params

sintaxe: ok, err = pro:get_params(key1, key2?...)

Retorna um ou mais valores de parâmetros do provedor.

local pro = require "resty.openssl.provider"

local p = pro.load("default")

local name = assert(p:get_params("name"))
print(name)
-- imprime "OpenSSL Default Provider"

local result = assert(p:get_params("name", "version", "buildinfo", "status"))
print(require("cjson").encode(result))
-- imprime metadados do provedor; versão e buildinfo variam conforme a versão do OpenSSL

resty.openssl.pkey

Módulo para interagir com chaves privadas e públicas (EVP_PKEY).

Cada tipo de chave pode suportar apenas parte das operações:

Tipo de Chave Carregar chave existente Geração de chave Criptografar/Descriptografar Assinar/Verificar Troca de Chaves Encapsular/Decapsular
RSA Y Y Y Y Y (RSASVE, OpenSSL 3.5+)
DH Y Y Y
EC Y Y Y (ECDSA) Y (ECDH) Y (DHKEM, OpenSSL 3.5+)
Ed25519 Y Y Y (PureEdDSA)
X25519 Y Y Y (ECDH) Y (DHKEM, OpenSSL 3.5+)
Ed448 Y Y Y (PureEdDSA)
X448 Y Y Y (ECDH) Y (DHKEM, OpenSSL 3.5+)
ML-DSA (OpenSSL 3.5+) Y Y Y
SLH-DSA (OpenSSL 3.5+) Y Y Y
ML-KEM (OpenSSL 3.5+) Y Y Y
ML-KEM TLS hybrid (OpenSSL 3.5+) Dependente do provedor Y Y

Não existe suporte direto para criptografia e descriptografia para EC e ECX, mas processos como ECIES são possíveis com pkey:derive, kdf e cipher

pkey.new

Carregar chave existente

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

Suporta o carregamento de uma chave privada ou pública nos formatos PEM, DER ou JWK passada como primeiro argumento string.

O segundo parâmetro opts aceita uma tabela opcional para restringir o comportamento de carregamento de chave.

  • opts.format: defina explicitamente como "PEM", "DER", "JWK" para carregar um formato específico ou defina como "*" para detecção automática
  • opts.type: defina explicitamente como "pr" para chave privada, "pu" para chave pública; defina como "*" para detecção automática

Ao carregar uma chave RSA codificada em PEM, ela pode ser uma SubjectPublicKeyInfo/PrivateKeyInfo codificada em PKCS#8 ou uma RSAPublicKey/RSAPrivateKey codificada em PKCS#1.

Ao carregar uma chave codificada em PEM criptografada, a passphrase para descriptografá-la pode ser definida em opts.passphrase ou opts.passphrase_cb:

pkey.new(pem_or_der_text, {
  format = "*", -- escolha de "PEM", "DER", "JWK" ou "*" para detecção automática
  type = "*", -- escolha de "pr" para chave privada, "pu" para chave pública e "*" para detecção automática
  passphrase = "senha secreta", -- a passphrase de criptografia PEM
  passphrase_cb = function()
    return "senha secreta"
  end, -- a função de callback da passphrase de criptografia PEM
}

Ao carregar JWK, há algumas ressalvas: - Certifique-se de que o texto JSON codificado seja passado; ele deve ter sido decodificado em base64. - Restringir opts.type para chaves JWK requer OpenSSL 3.0 ou posterior e lua-resty-openssl 1.6.0 ou posterior. Com OpenSSL 1.1.1 ou versões mais antigas do lua-resty-openssl, os parâmetros no JSON fornecido decidirão se uma chave privada ou pública é carregada; especificar type resultará em um erro; também a parte da chave pública para chaves OKP (o parâmetro x) não é considerada e é derivada da parte da chave privada (o parâmetro d) se for especificada. - Apenas os tipos de chave RSA, P-256, P-384 e P-512 EC, Ed25519, X25519, Ed448 e X448 OKP são suportados. - Assinaturas e verificação devem usar a opção ecdsa_use_raw para funcionar com os padrões JWS para chaves EC. Consulte pkey:sign e pkey.verify para detalhes. - Ao executar fora do OpenResty, é necessário instalar uma biblioteca JSON (cjson ou dkjson) e basexx.

Geração de chave

sintaxe: pk, err = pkey.new(config?)

Gera uma nova chave pública ou privada.

Para gerar uma chave RSA, a tabela config pode ter os campos bits e exp para controlar a geração de chave. Quando config é omitido, esta função gera uma chave RSA de 2048 bits com exponent de 65537, que é equivalente a:

local key, err = pkey.new({
  type = 'RSA',
  bits = 2048,
  exp = 65537
})

Para gerar chave EC ou DH, consulte pkey.paramgen para os valores possíveis da tabela config. Por exemplo:

local key, err = pkey.new({
  type = 'EC',
  curve = 'prime256v1',
})

No OpenSSL 3.0 ou posterior, qualquer tipo de chave implementado por um provedor carregado pode ser solicitado pelo nome. Por exemplo, o OpenSSL 3.5 ou posterior fornece tipos de chave pós-quânticos incluindo:

local ml_dsa = assert(pkey.new({ type = "ML-DSA-44" }))
local ml_kem = assert(pkey.new({ type = "ML-KEM-768" }))
local slh_dsa = assert(pkey.new({ type = "SLH-DSA-SHA2-128s" }))

config.properties pode ser usado para selecionar uma implementação de provedor para esses tipos de chave nativos do provedor.

Também é possível passar parâmetros EC ou DH codificados em PEM para config.param para geração de chave:

local dhparam = pkey.paramgen({
  type = 'DH',
  group = 'dh_1024_160'
})
-- OU
-- local dhparam = io.read("dhparams.pem"):read("*a")

local key, err = pkey.new({
  type = 'DH',
  param = dhparam,
}) 

Também é possível passar strings de controle pkeyopt brutas na tabela config como usado no programa CLI genpkey. Consulte openssl-genpkey(1) para uma lista de opções.

Por exemplo:

pkey.new({
  type = 'RSA',
  bits = 2048,
  exp = 65537,
})
-- é o mesmo que
pkey.new({
  type = 'RSA',
  exp = 65537,
  "rsa_keygen_bits:4096",
})

Composição de chave

sintaxe: pk, err = pkey.new(config?)

Compõe uma chave pública ou privada usando parâmetros existentes. Para ver a lista de parâmetros para cada chave, consulte pkey:set_parameters.

Apenas type e params devem existir na tabela config, todas as outras chaves serão ignoradas.

local private_bn = require "resty.openssl.bn".new("7F48282CCA4C1A65D589C06DBE9C42AE50FBFFDF3A18CBB48498E1DE47F11BE1A3486CD8FA950D68F111970F922279D8", 16)
local p_384, err = assert(require("resty.openssl.pkey").new({
    type = "EC",
    params = {
        private = private_bn,
        group = "secp384r1",
    }
}))

pkey.istype

sintaxe: ok = pkey.istype(table)

Retorna true se a tabela for uma instância de pkey. Retorna false caso contrário.

pkey.paramgen

sintaxe: pem_txt, err = pk.paramgen(config)

Gera parâmetros para chave EC ou DH e os gera como texto codificado em PEM.

Para chave EC:

Parâmetro Descrição
type "EC"
curve Curvas EC. Se omitido, o padrão é "prime192v1". Para ver a lista de curvas EC suportadas, use openssl ecparam -list_curves.

Para chave DH:

Parâmetro Descrição
type "DH"
bits Gera um novo parâmetro DH com primo de bits de comprimento. Se omitido, o padrão é 2048. A partir do OpenSSL 3.0, apenas bits iguais a 2048 são permitidos.
group Usa grupos predefinidos em vez de gerar um novo. bit será ignorado se group for definido.

Os valores possíveis para group são: - RFC7919 "ffdhe2048", "ffdhe3072", "ffdhe4096", "ffdhe6144", "ffdhe8192" - RFC5114 "dh_1024_160", "dh_2048_224", "dh_2048_256" - RFC3526 "modp_1536", "modp_2048", "modp_3072", "modp_4096", "modp_6144", "modp_8192"

local pem, err = pkey.paramgen({
  type = 'EC',
  curve = 'prime192v1',
})

local pem, err = pkey.paramgen({
  type = 'DH',
  group = 'ffdhe4096',
})

Também é possível passar strings de controle pkeyopt brutas na tabela config como usado no programa CLI genpkey. Consulte openssl-genpkey(1) para uma lista de opções.

pkey:get_provider_name

sintaxe: name = pkey:get_provider_name()

Retorna o nome do provedor de pkey.

Esta função está disponível no OpenSSL 3.0 ou posterior.

pkey:gettable_params, pkey:settable_params, pkey:get_param, pkey:set_params

Consulta parâmetros configuráveis ou obtíveis e define ou obtém parâmetros. Consulte Getter/setter genérico de parâmetros EVP.

pkey:get_parameters

sintaxe: parameters, err = pk:get_parameters()

Retorna uma tabela contendo os parameters da instância pkey.

Para chaves ECX e, separadamente, chaves nativas do provedor OpenSSL 3, como ML-KEM, ML-DSA, SLH-DSA e chaves híbridas ML-KEM TLS, a tabela expõe campos binários public e private quando o provedor permite que esses componentes sejam exportados. Alguns provedores intencionalmente não exportam um componente privado.

pkey:set_parameters

sintaxe: ok, err = pk:set_parameters(params)

Define os parâmetros da pkey a partir de uma tabela params. Se o parâmetro não estiver definido na tabela params, ele permanece inalterado na instância pkey.

Para chaves ECX e, separadamente, chaves nativas do provedor OpenSSL 3, public e private contêm os componentes binários brutos da chave. Definir qualquer componente substitui a chave imutável do provedor subjacente, preservando o objeto pkey.

local pk, err = require("resty.openssl.pkey").new()
local parameters, err = pk:get_parameters()
local e = parameters.e
ngx.say(e:to_number())
-- imprime 65537

local ok, err = pk:set_parameters({
  e = require("resty.openssl.bn").from_hex("100001")
})

local ok, err = pk:set_parameters(parameters)

Parâmetros para chave RSA:

Parâmetro Descrição Tipo
n módulo comum à chave pública e privada bn
e expoente público bn
d expoente privado bn
p primeiro fator de n bn
q segundo fator de n bn
dmp1 d mod (p - 1), expoente1 bn
dmq1 d mod (q - 1), expoente2 bn
iqmp (InverseQ)(q) = 1 mod p, coeficiente bn

Parâmetros para chave EC:

Parâmetro Descrição Tipo
private chave privada bn
public chave pública bn
x coordenada x da chave pública bn
y coordenada y da chave pública bn
group o grupo de curva nomeado [NID] como um número, quando passado em set_parameters(), também é possível usar a representação em texto. Isso é diferente do luaossl, onde uma instância EC_GROUP é retornada.

Não é possível definir x, y com public ao mesmo tempo, pois x e y são basicamente outra representação de public. Além disso, atualmente só é possível definir x e y ao mesmo tempo.

Parâmetros para chave DH:

Parâmetro Descrição Tipo
private chave privada bn
public chave pública bn
p módulo primo bn
q posição de referência bn
g gerador base bn

Parâmetros para chaves Curve25519 e Curve448:

Parâmetro Descrição Tipo
private chave privada bruta representada como bytes string
public chave pública bruta representada como bytes string

pkey:is_private

sintaxe: ok = pk:is_private()

Verifica se pk é uma chave privada. Retorna true se for uma chave privada, retorna false se for uma chave pública.

pkey:get