Certificado A1 (um .pfx PKCS#12) aberto nas peças em DER que :ssl e :public_key pedem,
mais o que a biblioteca precisa saber dele: CNPJ ou CPF, validade e clientAuth.
O OTP não tem leitor de PKCS#12 (:public_key não exporta um, e PFX é um tipo indefinido em
OTP-PUB-KEY e PKCS-FRAME), então from_pfx/3 chama openssl pkcs12. Duas regras tornam
isso seguro, e nenhuma é opcional:
- o bundle entra pelo stdin do processo filho, nunca por arquivo temporário, porque o arquivo guardaria a chave privada sem cifra;
- a senha entra por variável de ambiente do filho, nunca por
argv, porque oargvé legível por qualquer usuário nops.
A maior parte dos A1 da ICP-Brasil ainda é exportada com PBE-SHA1-RC2-40, que o OpenSSL 3
moveu para o provider legacy. A primeira tentativa vai sem -legacy e só então repete com ele:
um export moderno custa uma chamada e um antigo custa duas. O motivo da ordem é que o openssl
se recusa a rodar quando não consegue carregar um provider pedido na linha de comando, então
começar por -legacy perderia os bundles modernos em todo host sem o pacote.
Em produção esse provider vem no pacote openssl-provider-legacy. Sem ele, todo certificado
real falha ao abrir enquanto os fixtures no Mac do desenvolvedor continuam funcionando, por
isso :legacy_provider_missing é um erro próprio e não :invalid_file. Um subprocesso que
nunca responde recebe o mesmo tratamento e volta como :timeout, e um host sem o executável
openssl no PATH volta como :openssl_missing: nenhum dos três é problema do arquivo, e
mandar exportar o certificado de novo levaria a pessoa atrás da coisa errada.
Os erros são %Nfse.Error{kind: :certificate} e details é um destes:
details | Quando |
|---|---|
:invalid_password | a senha não abre o bundle |
:invalid_file | o arquivo não é um PKCS#12 completo, ou falta chave ou folha |
:legacy_provider_missing | o bundle usa cifra legada e o openssl não tem o provider |
:openssl_missing | o executável openssl não está no PATH |
:timeout | o openssl não respondeu dentro do prazo |
:unsupported_key | a chave não é RSA, e a assinatura da DPS exige RSA |
Summary
Functions
Diz se now já passou do fim da validade.
Monta o certificado a partir de peças em DER que o chamador já tem, sem passar pelo openssl.
Abre bundle com password.
Types
@type t() :: %Nfse.Certificate{ certificate: binary(), chain: [binary()], client_auth: boolean(), cnpj: String.t() | nil, cpf: String.t() | nil, key: {:RSAPrivateKey, binary()}, not_after: DateTime.t(), not_before: DateTime.t() }
Functions
@spec expired?(t(), DateTime.t()) :: boolean()
Diz se now já passou do fim da validade.
@spec from_der(binary(), {:RSAPrivateKey, binary()}, [binary()]) :: {:ok, t()} | {:error, Nfse.Error.t()}
Monta o certificado a partir de peças em DER que o chamador já tem, sem passar pelo openssl.
@spec from_pfx(binary(), String.t(), [{:timeout, pos_integer()}]) :: {:ok, t()} | {:error, Nfse.Error.t()}
Abre bundle com password.
A folha é escolhida casando a chave pública de cada certificado com a chave privada: a ordem
dentro de um .pfx não é especificada, e pegar o primeiro certificado devolve uma
intermediária com frequência suficiente para importar.
Opções
:timeout- prazo em milissegundos para a chamada inteira, incluindo a repetição com-legacy. Padrão:15_000.