Usando o openssl para obter o certificado de um servidor

354

Estou tentando obter o certificado de um servidor remoto, que posso usar para adicionar ao meu keystore e usá-lo no meu aplicativo java.

Um desenvolvedor sênior (que está de férias :() me informou que posso executar isso:

openssl s_client -connect host.host:9999

Para obter um certificado bruto despejado, que eu posso copiar e exportar. Eu recebo a seguinte saída:

depth=1 /C=NZ/ST=Test State or Province/O=Organization Name/OU=Organizational Unit Name/CN=Test CA
verify error:num=19:self signed certificate in certificate chain
verify return:0
23177:error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure:s3_pkt.c:1086:SSL alert number 40
23177:error:140790E5:SSL routines:SSL23_WRITE:ssl handshake failure:s23_lib.c:188:

Eu também tentei com esta opção

-showcerts 

e este (rodando no debian)

-CApath /etc/ssl/certs/ 

Mas obtenha o mesmo erro.

Esta fonte diz que posso usar esse sinalizador CApath, mas parece não ajudar. Eu tentei vários caminhos sem sucesso.

Por favor, deixe-me saber onde estou errado.

pastoso desagradável
fonte

Respostas:

463

Com SNI

Se o servidor remoto estiver usando SNI (ou seja, compartilhando vários hosts SSL em um único endereço IP), será necessário enviar o nome do host correto para obter o certificado correto.

openssl s_client -showcerts -servername www.example.com -connect www.example.com:443 </dev/null

Sem SNI

Se o servidor remoto não estiver usando SNI, você poderá pular o -servernameparâmetro:

openssl s_client -showcerts -connect www.example.com:443 </dev/null


Para visualizar os detalhes completos do certificado de um site, você também pode usar esta cadeia de comandos:

$ echo | \
    openssl s_client -servername www.example.com -connect www.example.com:443 2>/dev/null | \
    openssl x509 -text
Ari Maniatis
fonte
3
Hmm. Ainda recebo o mesmo erro ao tentar esse comando. Percebi que minha versão do Openssl é 'OpenSSL 0.9.8g 19 Out 2007'. Você tem alguma ideia?
pastosa desagradável
39
Útil: echo "" | openssl s_client -connect server:port -prexit 2>/dev/null | sed -n -e '/BEGIN\ CERTIFICATE/,/END\ CERTIFICATE/ p' stackoverflow.com/a/12918442/843000
mbrownnyc
16
Roteiro útil alternativa, a partir madboa.com :echo | openssl s_client -connect server:port 2>&1 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > cert.pem
rmeakins
9
Para tornar isto um pouco mais conciso, você pode substituir o sedcom openssl x509, e lê-lo no uso de uma sub-shell:openssl x509 -in <(openssl s_client -connect server:port -prexit 2>/dev/null)
Gabe Martin-Dempesy
27
Tambémecho | openssl s_client -connect google.com:443 2>/dev/null | openssl x509
MattSizzle
68

Embora eu concorde com a resposta de Ari (e a votei :): eu precisava fazer uma etapa extra para que ele funcionasse com o Java no Windows (onde precisava ser implantado):

openssl s_client -showcerts -connect www.example.com:443 < /dev/null | openssl x509 -outform DER > derp.der

Antes de adicionar a openssl x509 -outform DERconversão, eu estava recebendo um erro do keytool no Windows, reclamando do formato do certificado. A importação do arquivo .der funcionou bem.

David Jaquay
fonte
Ímpar. Eu tenho usado certificados PEM com keytool no Windows desde o Java 6 e nunca enfrentou um problema.
Imgx64 #
39

Acontece que há mais complexidade aqui: eu precisava fornecer muitos mais detalhes para fazer isso acontecer. Eu acho que tem algo a ver com o fato de ser uma conexão que precisa de autenticação do cliente, e o hankshake precisava de mais informações para continuar no estágio em que os certificados foram descartados.

Aqui está o meu comando de trabalho:

openssl s_client -connect host:port -key our_private_key.pem -showcerts \
                 -cert our_server-signed_cert.pem

Espero que este seja um empurrão na direção certa para qualquer um que possa fazer com mais algumas informações.

pastoso desagradável
fonte
6
Sinto muito, mas sua resposta não faz muito sentido. Você precisava passar o certificado para o servidor para obter o certificado?
Ari Maniatis
2
Sim. Autenticação de cliente AFAIK.
Novell '15:
11
Acontece que '-prexit' retornará esses dados também. Por exemplo; openssl s_client -connect host: port -prexit
Robert
39

Uma linha para extrair o certificado de um servidor remoto no formato PEM, desta vez usando sed:

openssl s_client -connect www.google.com:443 2>/dev/null </dev/null |  sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p'
André Fernandes
fonte
2
Este é quase perfeito para extrair o certificado, faltando apenas a -servernameopção, não sei por que, mas eu tive que usá-lo para obter o certificado completo.
Karl.S
-servername é necessário para a indicação do nome do servidor (SNI). A pesquisa na Web pode se expandir.
Sam Gleske 6/06
32

A linha de comando mais fácil para isso, que inclui a saída PEM para adicioná-la ao keystore, além de uma saída legível por humanos e também suporta SNI, que é importante se você estiver trabalhando com um servidor HTTP:

openssl s_client -servername example.com -connect example.com:443 \
    </dev/null 2>/dev/null | openssl x509 -text

A opção -servername é ativar o suporte SNI e o openssl x509 -text imprime o certificado em formato legível por humanos.

Florian
fonte
Você pode adicionar ao seu nome de servidor seu subdomínio, por exemplo, ws.example.com em vez de example.com (aplique isso também ao parâmetro -connect).
russellhoff
24

Para obter o certificado do servidor remoto, você pode usar a opensslferramenta e pode encontrá-lo entreBEGIN CERTIFICATE eEND CERTIFICATE que você precisa copiar e colar em seu arquivo de certificado (CRT).

Aqui está o comando demonstrando:

ex +'/BEGIN CERTIFICATE/,/END CERTIFICATE/p' <(echo | openssl s_client -showcerts -connect example.com:443) -scq > file.crt

Para retornar todos os certificados da cadeia, basta adicionar g(global) como:

ex +'g/BEGIN CERTIFICATE/,/END CERTIFICATE/p' <(echo | openssl s_client -showcerts -connect example.com:443) -scq

Depois, você pode simplesmente importar seu arquivo de certificado ( file.crt) para seu chaveiro e torná-lo confiável, para que o Java não se queixe.

No OS X, você pode clicar duas vezes no arquivo ou arrastar e soltar no seu Keychain Access, para que ele apareça no login / Certificados. Em seguida, clique duas vezes no certificado importado e faça-o Sempre Confiar em SSL .

No CentOS 5, você pode anexá-los ao /etc/pki/tls/certs/ca-bundle.crtarquivo (e executar sudo update-ca-trust force-enable:) ou no CentOS 6 copiá-los /etc/pki/ca-trust/source/anchors/e executá- los sudo update-ca-trust extract.

No Ubuntu, copie-os /usr/local/share/ca-certificatese execute sudo update-ca-certificates.

kenorb
fonte
12
HOST=gmail-pop.l.google.com
PORT=995

openssl s_client -servername $HOST -connect $HOST:$PORT < /dev/null 2>/dev/null | openssl x509 -outform pem
akond
fonte
6

para imprimir apenas a cadeia de certificados e não o certificado do servidor:

# MYHOST=myhost.com
# MYPORT=443
# openssl s_client -connect ${MYHOST}:${MYPORT} -showcerts 2>/dev/null </dev/null | awk '/^.*'"${MYHOST}"'/,/-----END CERTIFICATE-----/{next;}/-----BEGIN/,/-----END CERTIFICATE-----/{print}'

para atualizar o CA trust no CentOS / RHEL 6/7:

# update-ca-trust enable
# openssl s_client -connect ${MYHOST}:${MYPORT} -showcerts 2>/dev/null </dev/null | awk '/^.*'"${MYHOST}"'/,/-----END CERTIFICATE-----/{next;}/-----BEGIN/,/-----END CERTIFICATE-----/{print}' >/etc/pki/ca-trust/source/anchors/myca.cert
# update-ca-trust extract

no CentOS / RHEL 5:

# openssl s_client -connect ${MYHOST}:${MYPORT} -showcerts 2>/dev/null </dev/null | awk '/^.*'"${MYHOST}"'/,/-----END CERTIFICATE-----/{next;}/-----BEGIN/,/-----END CERTIFICATE-----/{print}' >>/etc/pki/tls/certs/ca-bundle.crt
Mathieu CARBONNEAUX
fonte
Exatamente o que eu precisava no CentOS7. Obrigado!
Arthur Hebert
5

Você pode obter e armazenar o certificado raiz do servidor usando o próximo script bash:

CERTS=$(echo -n | openssl s_client -connect $HOST_NAME:$PORT -showcerts | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p')
echo "$CERTS" | awk -v RS="-----BEGIN CERTIFICATE-----" 'NR > 1 { printf RS $0 > "'$SERVER_ROOT_CERTIFICATE'"; close("'$SERVER_ROOT_CERTIFICATE'") }'

Basta substituir as variáveis ​​necessárias.

Andrei Aleksandrov
fonte
4

Se o seu servidor for um servidor de email (MS Exchange ou Zimbra), talvez você precise adicionar os sinalizadores starttlse smtp:

openssl s_client -starttls smtp -connect HOST_EMAIL:SECURE_PORT 2>/dev/null </dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > CERTIFICATE_NAME.pem

Onde,

  • HOST_EMAIL é o domínio do servidor, por exemplo, mail-server.com.

  • SECURE_PORT é a porta de comunicação, por exemplo, 587 ou 465

  • Nome do arquivo da saída CERTIFICATE_NAME (formato BASE 64 / PEM)

SHoko
fonte