Usando openssl para obtener el certificado de un servidor

354

Estoy tratando de obtener el certificado de un servidor remoto, que luego puedo usar para agregar a mi almacén de claves y usar dentro de mi aplicación Java.

Un desarrollador senior (que está de vacaciones :() me informó que puedo ejecutar esto:

openssl s_client -connect host.host:9999

Para deshacerse de un certificado sin procesar, que luego puedo copiar y exportar. Recibo el siguiente resultado:

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:

También he probado con esta opción

-showcerts 

y este (corriendo en Debian te importa)

-CApath /etc/ssl/certs/ 

Pero obtén el mismo error.

Esta fuente dice que puedo usar esa bandera de CApath pero no parece ayudar. Intenté múltiples caminos en vano.

Por favor, hágame saber a dónde voy mal.

pastosa desagradable
fuente

Respuestas:

464

Con SNI

Si el servidor remoto está utilizando SNI (es decir, compartiendo múltiples hosts SSL en una sola dirección IP), deberá enviar el nombre de host correcto para obtener el certificado correcto.

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

Sin SNI

Si el servidor remoto no está utilizando SNI, puede omitir el -servernameparámetro:

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


Para ver los detalles completos del certificado de un sitio, también puede usar esta cadena de comandos:

$ echo | \
    openssl s_client -servername www.example.com -connect www.example.com:443 2>/dev/null | \
    openssl x509 -text
Ari Maniatis
fuente
3
Hmm Todavía recibo el mismo error cuando intento ese comando. Noté que mi versión de Openssl es 'OpenSSL 0.9.8g 19 Oct 2007'. ¿Tienes alguna idea?
desagradable pastilla
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
Script útil alternativo, de madboa.com :echo | openssl s_client -connect server:port 2>&1 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > cert.pem
rmeakins
99
Para hacer esto un poco más conciso, puede reemplazar el sedconopenssl x509 , y leerlo usando un sub-shell:openssl x509 -in <(openssl s_client -connect server:port -prexit 2>/dev/null)
Gabe Martin-Dempesy
27
Tambiénecho | openssl s_client -connect google.com:443 2>/dev/null | openssl x509
MattSizzle
68

Si bien estoy de acuerdo con la respuesta de Ari (y la voté :), tuve que hacer un paso adicional para que funcione con Java en Windows (donde debía implementarse):

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

Antes de agregar la openssl x509 -outform DERconversión, recibí un error de keytool en Windows quejándose del formato del certificado. Importar el archivo .der funcionó bien.

David Jaquay
fuente
Impar. He estado usando certificados PEM con keytool en Windows desde Java 6 y nunca tuve problemas.
imgx64
39

Resulta que aquí hay más complejidad: necesitaba proporcionar muchos más detalles para poner esto en marcha. Creo que tiene algo que ver con el hecho de que es una conexión que necesita autenticación del cliente, y el shakshake necesitaba más información para continuar a la etapa donde se arrojaron los certificados.

Aquí está mi comando de trabajo:

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

Esperemos que esto sea un empujón en la dirección correcta para cualquiera que pueda obtener más información.

pastosa desagradable
fuente
66
Lo siento, pero tu respuesta no tiene mucho sentido. ¿Necesitaba pasar el certificado al servidor para obtener el certificado?
Ari Maniatis
2
Sí. Autenticación del cliente AFAIK.
pastoso desagradable el
11
Resulta que '-prexit' también devolverá esos datos. P.ej; openssl s_client -connect host: port -prexit
Robert
39

Una línea para extraer el certificado de un servidor remoto en formato PEM, esta 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
fuente
2
Este es casi perfecto para extraer el certificado, solo falta la -servernameopción, no sé por qué, pero tuve que usarlo para obtener el certificado completo.
Karl.S
-servername es necesario para la indicación del nombre del servidor (SNI). La búsqueda en la web puede expandirse en el resto.
Sam Gleske
32

La línea de comando más fácil para esto, que incluye la salida PEM para agregarla al almacén de claves, así como una salida legible por humanos y también admite SNI, lo cual es importante si está trabajando con un servidor HTTP es:

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

La opción -servername es habilitar la compatibilidad con SNI y el openssl x509 -text imprime el certificado en formato legible para humanos.

Florian
fuente
Puede agregar a su -servername su subdominio, por ejemplo ws.example.com en lugar de example.com (aplique esto también al parámetro -connect).
russellhoff
24

Para obtener el certificado de servidor remoto, puede usar la opensslherramienta y puede encontrarla entre BEGIN CERTIFICATEyEND CERTIFICATE el que tiene que copiar y pegar en el archivo de certificado (CRT).

Aquí está el comando que lo demuestra:

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

Para devolver todos los certificados de la cadena, simplemente agregue g(global) como:

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

Luego, simplemente puede importar su archivo de certificado (file.crt ) en su llavero y hacer que sea confiable, para que Java no se queje.

En OS X, puede hacer doble clic en el archivo o arrastrar y soltar su Acceso a Llavero, para que aparezca en inicio de sesión / Certificados. Luego, haga doble clic en el certificado importado y hágalo siempre Trust for SSL .

En CentOS 5 puede agregarlos a un /etc/pki/tls/certs/ca-bundle.crtarchivo (y ejecutar sudo update-ca-trust force-enable:), o en CentOS 6 copiarlos /etc/pki/ca-trust/source/anchors/y ejecutarlos sudo update-ca-trust extract.

En Ubuntu, cópielos /usr/local/share/ca-certificatesy ejecútelos sudo update-ca-certificates.

kenorb
fuente
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
fuente
6

para imprimir solo la cadena de certificados y no el certificado del 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}'

actualizar CA trust en 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

en 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
fuente
Exactamente lo que necesitaba en CentOS7. ¡Gracias!
Arthur Hebert
5

Puede obtener y almacenar el certificado raíz del servidor utilizando el siguiente 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'") }'

Simplemente sobrescriba las variables requeridas.

Andrei Aleksandrov
fuente
4

Si su servidor es un servidor de correo electrónico (MS Exchange o Zimbra), tal vez necesite agregar las banderas starttlsy 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

Dónde,

  • HOST_EMAIL es el dominio del servidor, por ejemplo, mail-server.com.

  • SECURE_PORT es el puerto de comunicación, por ejemplo, 587 o 465

  • Nombre de archivo de salida de CERTIFICATE_NAME (formato BASE 64 / PEM)

SHoko
fuente