Solução de Problemas OpenSSL: Corrigindo o erro unable to get local issuer certificate
Ao utilizar o OpenSSL para conexões seguras (por exemplo, realizando requisições de rede em ambientes PHP, executando comandos openssl ou curl, ou estabelecendo conexões SSL/TLS em aplicações Node.js/Python), desenvolvedores podem se deparar com o erro 20:unable to get local issuer certificate. Esse é um problema recorrente, herdado de como o OpenSSL valida os certificados das partes remotas.
Por motivos de segurança, o OpenSSL exige saber exatamente quais autoridades certificadoras (CAs) são confiáveis ao verificar a cadeia de certificados. Se não conseguir localizar ou identificar essas âncoras de confiança, não será possível validar a legitimidade do certificado do servidor, resultando nesse erro.
Neste guia, você entenderá a origem desse erro e como resolvê-lo no ambiente ServBay, incluindo como configurar o armazenamento de confiança do OpenSSL para PHP, Python, Node.js e comandos curl.
Entendendo o erro 20:unable to get local issuer certificate
Descrição do Problema
Quando o OpenSSL tenta validar o certificado SSL/TLS de um servidor remoto, ele constrói uma cadeia de confiança do certificado do servidor até uma CA raiz confiável. Caso o OpenSSL não encontre localmente algum dos certificados intermediários ou da raiz, ou se um armazenamento apropriado de confiança (CAFile ou CAPath) não estiver configurado, a validação falha e retorna o erro 20:unable to get local issuer certificate.
Resumindo: o OpenSSL não sabe em qual autoridade certificadora confiar e, por isso, não pode garantir que a identidade do servidor é legítima.
Versão do OpenSSL e Caminhos dos Certificados CA no ServBay
O ServBay, como um ambiente local completo para desenvolvimento web, já inclui o pacote OpenSSL e um conjunto das principais CAs públicas para facilidade de uso. A versão do OpenSSL utilizada depende do tipo de chip do seu computador:
- ServBay para Apple Silicon (chips série M): utiliza OpenSSL versão 3.2.1.
- ServBay para Intel: utiliza OpenSSL versão 1.1.1u.
Os arquivos de certificados CA (cacert.pem) e o diretório de certificados (certs) correspondentes ficam na pasta de instalação dos pacotes do ServBay. Use o caminho correto conforme seu caso:
# Arquivo bundle com todos os certificados raiz confiáveis
cafile=/Applications/ServBay/package/common/openssl/3.2/cacert.pem
# Diretório com arquivos de certificados individuais (geralmente o cacert.pem já é suficiente, mas alguns aplicativos podem exigir capath)
capath=/Applications/ServBay/package/common/openssl/3.2/certs2
3
4
# Arquivo bundle com todos os certificados raiz confiáveis
cafile=/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem
# Diretório com arquivos de certificados individuais
capath=/Applications/ServBay/package/common/openssl/1.1.1u/certs2
3
4
O segredo para eliminar o erro unable to get local issuer certificate é informar ao OpenSSL o caminho do cafile ou capath, para que ele saiba onde buscar as CAs confiáveis.
Exemplos de Solução
Abaixo, veja como especificar o armazenamento CA do OpenSSL em diferentes ferramentas e linguagens.
Exemplo 1: Testando conexão via comando openssl
Se ao usar o comando openssl s_client para testar uma conexão você encontrar o erro:
openssl s_client -quiet -connect gmail.com:443Você verá uma saída semelhante a esta, contendo verify error:num=20:unable to get local issuer certificate:
depth=2 C=US, O=Google Trust Services LLC, CN=GTS Root R1
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=1 C=US, O=Google Trust Services, CN=WR2
verify return:1
depth=0 CN=gmail.com
verify return:1
# ... demais informações de conexão ...2
3
4
5
6
7
8
Como corrigir:
Use o parâmetro -CAfile para indicar explicitamente o caminho do arquivo CA fornecido pelo ServBay:
openssl s_client -quiet -connect gmail.com:443 -CAfile /Applications/ServBay/package/common/openssl/3.2/cacert.pemopenssl s_client -quiet -connect gmail.com:443 -CAfile /Applications/ServBay/package/common/openssl/1.1.1u/cacert.pemSe a verificação for bem-sucedida, o valor verify return será 1 e a linha verify error:num=20 não aparecerá:
depth=2 C=US, O=Google Trust Services LLC, CN=GTS Root R1
verify return:1
depth=1 C=US, O=Google Trust Services, CN=WR2
verify return:1
depth=0 CN=gmail.com
verify return:1
# ... demais informações de conexão ...2
3
4
5
6
7
Exemplo 2: Usando OpenSSL no PHP
Várias funções de rede do PHP (por exemplo, file_get_contents para URLs HTTPS, stream_socket_client para conexões SSL, extensão cURL etc.) dependem do OpenSSL. Para corrigir o erro, ajuste o arquivo php.ini ou informe o caminho do CA diretamente no código.
Método A: Editando o php.ini (Recomendado)
Solução global mais prática. Abra o arquivo php.ini da versão PHP ativa no ServBay (acesse pelo Painel de Controle do ServBay), localize a seção [openssl] e adicione/ajuste os parâmetros abaixo, escolhendo o caminho de acordo com seu chip:
[openssl]
; Informar o arquivo bundle de CA confiável
openssl.cafile=/Applications/ServBay/package/common/openssl/3.2/cacert.pem
; Informar o diretório de certificados CA (opcional, mas recomendado)
openssl.capath=/Applications/ServBay/package/common/openssl/3.2/certs2
3
4
5
[openssl]
; Informar o arquivo bundle de CA confiável
openssl.cafile=/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem
; Informar o diretório de certificados CA (opcional, mas recomendado)
openssl.capath=/Applications/ServBay/package/common/openssl/1.1.1u/certs2
3
4
5
Após salvar o php.ini, reinicie o serviço PHP (ou todo o ServBay) para que as alterações tenham efeito.
Método B: Definindo no código (Impacto somente na conexão atual)
Caso não queira modificar o php.ini, defina o caminho do CA ao criar o contexto SSL na função stream_context_create:
<?php
// Exemplo: conectando a um servidor SMTP via SSL/TLS
$server = 'ssl0.ovh.net';
$port = 465;
// Escolha o caminho correto conforme sua versão ServBay
// Para Apple Silicon:
$caCertFile = '/Applications/ServBay/package/common/openssl/3.2/cacert.pem';
// Para Intel:
// $caCertFile = '/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem';
$contextOptions = [
'ssl' => [
'verify_peer' => true, // Ativar verificação do certificado do peer
'verify_peer_name' => true, // Checar se o hostname do certificado bate com o host conectado
'allow_self_signed' => false, // Não permitir certificados autoassinados, a não ser que seja algo seguro para você
'cafile' => $caCertFile, // Caminho do bundle de CA
// 'capath' => '/Applications/ServBay/package/common/openssl/3.2/certs', // Opcional, diretório de CAs
],
];
$context = stream_context_create($contextOptions);
// Estabelece a conexão SSL/TLS com o contexto criado
$connection = @stream_socket_client(
"ssl://$server:$port",
$errno,
$errstr,
30, // tempo limite
STREAM_CLIENT_CONNECT,
$context // passa as opções do contexto
);
if ($connection) {
echo "Conexão estabelecida com $server:$port\n";
// Exemplo: enviar comando EHLO
fwrite($connection, "EHLO servbay.demo\r\n"); // Domínio demonstrativo ServBay
while (!feof($connection)) {
echo fgets($connection);
}
fclose($connection);
} else {
echo "Falha ao conectar em $server:$port. Erro: $errstr ($errno)\n";
}
?>2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
Exemplo 3: Usando OpenSSL no Python (módulo ssl)
Com o módulo ssl do Python, basta criar um contexto SSL personalizado e informar o caminho do CA:
import ssl
import socket
server = 'ssl0.ovh.net'
port = 465
# Escolha o caminho correto conforme sua versão ServBay
# Para Apple Silicon:
ca_cert_file = '/Applications/ServBay/package/common/openssl/3.2/cacert.pem'
# Para Intel:
# ca_cert_file = '/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem'
# Crie o contexto SSL indicando o arquivo CA
context = ssl.create_default_context(cafile=ca_cert_file)
# Também pode usar capath: context = ssl.create_default_context(capath='/Applications/ServBay/package/common/openssl/3.2/certs')
try:
# Criar conexão socket padrão
with socket.create_connection((server, port)) as sock:
# Transformar em socket SSL
with context.wrap_socket(sock, server_hostname=server) as ssock:
print(f"Conexão SSL estabelecida. Protocolo negociado: {ssock.version()}")
# Exemplo: enviar comando EHLO
ssock.sendall(b"EHLO servbay.demo\r\n") # Domínio demostrativo ServBay
while True:
data = ssock.recv(4096)
if not data:
break
print(data.decode())
except Exception as e:
print(f"Falha na conexão ou erro SSL: {e}")2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Exemplo 4: Usando OpenSSL no Node.js (módulo tls)
O módulo tls do Node.js permite passar o CA diretamente nas opções de conexão. O parâmetro ca pode ser uma string ou Buffer contendo um ou vários certificados — o método mais simples é ler o arquivo cacert.pem do ServBay:
const tls = require('tls');
const fs = require('fs');
const server = 'www.google.com'; // Use um site padrão e confiável para exemplo
const port = 443;
// Escolha o caminho correto conforme sua versão ServBay
// Para Apple Silicon:
const caCertFile = '/Applications/ServBay/package/common/openssl/3.2/cacert.pem';
// Para Intel:
// const caCertFile = '/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem';
const options = {
host: server,
port: port,
// Lê o conteúdo do arquivo CA
ca: fs.readFileSync(caCertFile),
// O Node.js por padrão verifica o hostname (checkServerIdentity),
// se o CA está correto e o certificado do servidor é válido, a conexão será estabelecida.
// Não desative checkServerIdentity a menos que esteja ciente dos riscos!
// checkServerIdentity: () => { return null; } // <-- Não utilize esta linha, pois desativa validação crucial de segurança!
};
const socket = tls.connect(options, () => {
console.log('Conexão SSL estabelecida');
// Normalmente, para HTTPS, deve-se enviar uma requisição HTTP. Aqui é apenas um exemplo de conexão.
// socket.write('GET / HTTP/1.1\r\nHost: ' + server + '\r\n\r\n');
});
socket.on('data', (data) => {
console.log(data.toString());
});
socket.on('close', () => {
console.log('Conexão fechada');
});
socket.on('error', (error) => {
console.error('Erro:', error.message); // Exibe mensagem de erro
});2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
Atenção: O exemplo do Node.js não inclui mais a opção checkServerIdentity: () => { return null; }. Essa opção desabilita a verificação do hostname do servidor, resultando em um cenário inseguro. O erro OpenSSL unable to get local issuer certificate refere-se ao armazenamento de confiança, não à verificação de hostname (verify_peer_name). Informe corretamente o parâmetro ca e mantenha a validação padrão do Node.js para garantir segurança. Se houver erro de hostname, possivelmente o certificado está incorreto — não é problema do CA.
Exemplo 5: Usando OpenSSL com o comando curl
O comando curl utiliza OpenSSL (ou outra biblioteca SSL) para requisições HTTPS. Informe o arquivo CA via parâmetro --cacert:
# Acessando site HTTPS com o arquivo CA do ServBay
# Use o caminho correto conforme sua versão do ServBay
# Para Apple Silicon:
curl --cacert /Applications/ServBay/package/common/openssl/3.2/cacert.pem https://example.com
# Para Intel:
# curl --cacert /Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem https://example.com2
3
4
5
6
7
Com as configurações certas e um certificado válido no servidor, o curl conseguirá acessar o conteúdo sem reportar erros de validação.
Conclusão
O erro 20:unable to get local issuer certificate é frequente ao se conectar via SSL/TLS utilizando OpenSSL e acontece quando não se especifica um arquivo de confiança para validação do certificado do servidor. O ServBay oferece o arquivo cacert.pem com as CAs públicas mais comuns já pré-configuradas.
Basta apontar o caminho do cacert.pem do ServBay no ambiente de desenvolvimento (seja no php.ini, nas opções do contexto SSL em código, ou via parâmetro em comandos como openssl ou curl). Atenção para selecionar o arquivo correto conforme seu processador (Apple Silicon ou Intel) e versão correspondente do OpenSSL. Com o CA corretamente configurado, será possível estabelecer conexões seguras entre o ambiente local e serviços SSL/TLS externos.
