Solução de Problemas do OpenSSL: Corrigindo o erro unable to get local issuer certificate
Ao utilizar o OpenSSL para conexões seguras (por exemplo, ao executar requisições de rede em ambientes PHP, rodar comandos openssl
ou curl
, ou ao estabelecer conexões SSL/TLS em aplicações Node.js/Python), desenvolvedores podem se deparar com o erro 20:unable to get local issuer certificate
. Este é um problema comum, relacionado ao processo de verificação dos certificados pelo OpenSSL.
Por motivos de segurança, o OpenSSL exige que o conjunto de Autoridades Certificadoras (CA) confiáveis esteja explicitamente configurado para validar a cadeia de certificados. Se esses pontos de confiança não forem encontrados ou especificados, ele não consegue verificar a legitimidade do certificado do servidor, resultando nesse erro.
Este guia explica em detalhe as causas desse erro e apresenta métodos para resolvê-lo no ambiente ServBay, incluindo a configuração do armazenamento de certificados confiáveis para PHP, Python, Node.js e comandos curl
.
Mensagem de 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, constrói uma cadeia desde o certificado do servidor até uma CA raiz confiável. Se não encontrar algum dos certificados intermediários ou o certificado raiz necessários localmente, ou se não houver um armazenamento de confiança devidamente configurado (CAFile ou CAPath), a validação falha, retornando o erro 20:unable to get local issuer certificate
.
Em palavras simples, o OpenSSL não sabe em qual autoridade de certificação confiar e, dessa forma, não consegue garantir a identidade do servidor ao qual está conectando.
Versões do OpenSSL e Caminhos dos Certificados CA no ServBay
O ServBay, como ambiente integrado de desenvolvimento web local, já vem com pacotes OpenSSL e uma coleção de certificados raiz de CA reconhecidos, facilitando o uso para desenvolvedores. A versão do OpenSSL no ServBay depende do tipo de chip do seu dispositivo:
- ServBay para Apple Silicon (chips da Série M): usa o OpenSSL 3.2.1.
- ServBay para Intel: usa o OpenSSL 1.1.1u.
Os arquivos do OpenSSL (cacert.pem
) e o diretório de certificados (certs
) estão localizados no diretório de instalação do ServBay. Localize o caminho correto conforme sua plataforma e arquitetura:
ini
# Arquivo bundle contendo todas as raízes confiáveis
cafile=/Applications/ServBay/package/common/openssl/3.2/cacert.pem
# Diretório com arquivos de certificados individuais (geralmente só o cacert.pem já basta, mas alguns apps exigem capath)
capath=/Applications/ServBay/package/common/openssl/3.2/certs
1
2
3
4
2
3
4
ini
# Arquivo bundle contendo todas as raízes 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/certs
1
2
3
4
2
3
4
ini
# Arquivo bundle contendo todas as raízes confiáveis
cafile=C:\ServBay\package\common\openssl\3.3\cacert.pem
# Diretório com arquivos de certificados individuais
capath=C:\ServBay\package\common\openssl\3.3\certs
1
2
3
4
2
3
4
A solução para o erro unable to get local issuer certificate
é sempre indicar corretamente o caminho do cafile
ou capath
onde estão os certificados CA confiáveis.
Exemplos de Soluções
Veja como configurar o armazenamento de confiança da CA com OpenSSL em diferentes ferramentas e linguagens.
Exemplo 1: Testando Conexão com o comando openssl
Caso você execute o comando openssl s_client
e se depare com o erro abaixo:
bash
openssl s_client -quiet -connect gmail.com:443
1
A saída pode mostrar linhas como verify error:num=20:unable to get local issuer certificate
:
bash
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
# ... outras informações de conexão ...
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
Como resolver:
Use o parâmetro -CAfile
para indicar o arquivo de certificados bundle fornecido pelo ServBay:
bash
openssl s_client -quiet -connect gmail.com:443 -CAfile /Applications/ServBay/package/common/openssl/3.2/cacert.pem
1
bash
openssl s_client -quiet -connect gmail.com:443 -CAfile /Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem
1
Se a conexão for bem-sucedida, o valor em verify return
será 1
e a linha verify error:num=20
não aparecerá mais:
bash
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
# ... outras informações de conexão ...
1
2
3
4
5
6
7
2
3
4
5
6
7
Exemplo 2: Usando OpenSSL no PHP
Muitas funções de rede do PHP (como file_get_contents
para URLs HTTPS, stream_socket_client
para conexões SSL, cURL, etc) dependem do OpenSSL. Existem duas formas de configurar o armazenamento CA: pelo arquivo php.ini
ou diretamente no código.
Método A: Editando o php.ini
(recomendado)
Solução recomendada e global. Edite o arquivo php.ini
correspondente à sua versão do PHP (pode ser feito facilmente pelo painel de controle do ServBay), encontre a seção [openssl]
e adicione ou ajuste as linhas abaixo, conforme o seu chip.
ini
[openssl]
; Especifica o arquivo bundle confiável de certificados CA
openssl.cafile=/Applications/ServBay/package/common/openssl/3.2/cacert.pem
; Diretório de certificados CA (opcional, mas recomendável definir)
openssl.capath=/Applications/ServBay/package/common/openssl/3.2/certs
1
2
3
4
5
2
3
4
5
ini
[openssl]
; Especifica o arquivo bundle confiável de certificados CA
openssl.cafile=/Applications/ServBay/package/common/openssl/1.1.1u/cacert.pem
; Diretório de certificados CA (opcional, mas recomendável definir)
openssl.capath=/Applications/ServBay/package/common/openssl/1.1.1u/certs
1
2
3
4
5
2
3
4
5
Após a alteração, reinicie o serviço PHP no ServBay (ou todo o ServBay) para que a configuração entre em vigor.
Método B: Configurando no código (apenas para a conexão específica)
Se preferir não modificar o php.ini
, defina o CA na hora de criar o contexto da conexão SSL/TLS:
php
<?php
// Exemplo: conectar a um servidor SMTP que usa SSL/TLS
$server = 'ssl0.ovh.net';
$port = 465;
// Escolha o caminho correto do arquivo CA 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, // Ativa validação do certificado do servidor
'verify_peer_name' => true, // Verifica se o nome do host no certificado corresponde ao endereço
'allow_self_signed' => false, // Não permite certificados autoassinados, exceto se explicitamente confiado
'cafile' => $caCertFile, // Indica o arquivo de certificados CA
// 'capath' => '/Applications/ServBay/package/common/openssl/3.2/certs', // Opcional, diretório de certificados CA
],
];
$context = stream_context_create($contextOptions);
// Estabelece a conexão SSL/TLS usando o contexto criado
$connection = @stream_socket_client(
"ssl://$server:$port",
$errno,
$errstr,
30, // Tempo limite da conexão
STREAM_CLIENT_CONNECT,
$context // Opções de contexto
);
if ($connection) {
echo "Conexão estabelecida com $server:$port\n";
// Exemplo: envía comando EHLO
fwrite($connection, "EHLO servbay.demo\r\n"); // Utiliza domínio fictício relacionado ao ServBay
while (!feof($connection)) {
echo fgets($connection);
}
fclose($connection);
} else {
echo "Falha ao conectar a $server:$port. Erro: $errstr ($errno)\n";
}
?>
1
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
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
Exemplo 3: Usando OpenSSL no Python (módulo ssl)
O módulo ssl
do Python permite criar contextos SSL/TLS customizados, especificando o arquivo CA.
python
import ssl
import socket
server = 'ssl0.ovh.net'
port = 465
# Escolha o arquivo correto do CA 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'
# Cria o contexto SSL padrão e define o arquivo CA
context = ssl.create_default_context(cafile=ca_cert_file)
# Ou: context = ssl.create_default_context(capath='/Applications/ServBay/package/common/openssl/3.2/certs')
try:
# Estabelece conexão socket padrão
with socket.create_connection((server, port)) as sock:
# Envolve o socket comum em um socket SSL
with context.wrap_socket(sock, server_hostname=server) as ssock:
print(f"Conexão SSL estabelecida. Protocolo negociado: {ssock.version()}")
# Exemplo: envía comando EHLO
ssock.sendall(b"EHLO servbay.demo\r\n") # Domínio fictício 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}")
1
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
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
Exemplo 4: Usando OpenSSL no Node.js (módulo tls)
No Node.js, o módulo tls
estabelece conexões SSL/TLS e permite configurar o CA via opção ca
, que recebe o conteúdo do arquivo de certificados fornecido pelo ServBay.
javascript
const tls = require('tls');
const fs = require('fs');
const server = 'www.google.com'; // Utilize um site confiável padrão como exemplo
const port = 443;
// Escolha o arquivo correto do CA 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,
// Carrega o conteúdo do arquivo CA
ca: fs.readFileSync(caCertFile),
// O módulo tls do Node.js realiza validação de hostname automaticamente,
// Se o arquivo CA estiver correto e o certificado do servidor for válido, a validação ocorre normalmente.
// Não desabilite checkServerIdentity a menos que saiba exatamente o que está fazendo!
// checkServerIdentity: () => { return null; } // <-- Não utilize esta linha: desabilita verificação importante!
};
const socket = tls.connect(options, () => {
console.log('Conexão SSL estabelecida');
// Para HTTPS, normalmente se envia uma requisição HTTP, este é apenas 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 encerrada');
});
socket.on('error', (error) => {
console.error('Erro:', error.message); // Mensagem de erro
});
1
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
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
Observação: O exemplo do Node.js NÃO utiliza checkServerIdentity: () => { return null; }
, pois essa opção desativaria a verificação do nome do servidor, tornando a conexão insegura. O erro unable to get local issuer certificate
refere-se à confiança da raiz CA, enquanto validação de hostname é outro aspecto. Certifique-se de que a opção ca
esteja correta e que o certificado do servidor seja válido; o Node.js fará a verificação de forma segura por padrão.
Exemplo 5: Usando OpenSSL com o comando curl
O comando curl
também utiliza OpenSSL (ou outras bibliotecas SSL) para requisições HTTPS. Você pode configurar o arquivo CA diretamente pelo parâmetro --cacert
.
bash
# Use o arquivo CA fornecido pelo ServBay para acessar um site HTTPS
# Escolha o caminho correto conforme sua versão 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.com
1
2
3
4
5
6
7
2
3
4
5
6
7
Estando o arquivo CA correto e o certificado do servidor válido, o curl
conseguirá acessar o conteúdo sem reportar erros de validação.
Resumo
O erro 20:unable to get local issuer certificate
é muito comum ao trabalhar com conexões SSL/TLS pelo OpenSSL, e ocorre porque ele exige a indicação explícita de um conjunto de certificados confiáveis para validar o servidor. O ServBay facilita o processo oferecendo um arquivo bundle (cacert.pem
) com as raízes CA mais comuns do mercado.
A solução adequada é especificar esse arquivo no seu ambiente de desenvolvimento: seja no php.ini
, nas opções de contexto SSL no código, ou nos parâmetros dos comandos (-CAfile
para openssl
, --cacert
para curl
). Certifique-se de escolher o caminho correto para seu tipo de chip (Apple Silicon ou Intel) e versão do OpenSSL do ServBay. Configurando corretamente o armazenamento CA, seu ambiente local pode se comunicar de forma segura com serviços SSL/TLS externos.