Mensaje de error
verify error:num=20:unable to get local issuer certificate
También se muestra como
- Verify return code: 20 (unable to get local issuer certificate)
- error 20 at 0 depth lookup: unable to get local issuer certificate
s_client imprime «verify error:num=20:…» durante el handshake y «Verify return code: 20 (…)» en su informe final; openssl verify imprime «error 20 at <depth> depth lookup: …». De forma predeterminada, s_client continúa tras los errores de verificación; añade -verify_return_error para que falle.
OpenSSL CLI (openssl s_client / openssl verify) — X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
Qué significa
Error 20 de OpenSSL (X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY): al construir la cadena, OpenSSL llegó a un certificado cuyo emisor no pudo encontrar, ni entre los certificados que envió el servidor ni en el almacén de confianza. La línea de profundidad que se imprime justo antes indica el certificado cuyo emisor falta. Es el mismo código que hay detrás del (60) de curl, del CERTIFICATE_VERIFY_FAILED de Python y del UNABLE_TO_GET_ISSUER_CERT_LOCALLY de Node.js.
Por qué ocurre
La cadena termina en una raíz que no está en el almacén
frecuenteUna raíz privada o de la empresa (o un proxy que inspecciona TLS y solo envía su intermedio). La línea de mayor profundidad indica un intermedio cuyo emisor no tienes.
El servidor no envía su intermedio
frecuenteSolo aparece depth=0, y su emisor es una CA intermedia. OpenSSL nunca descarga los intermedios que faltan, así que el servidor debe enviarlos.
La prueba no usó ningún almacén de confianza
ocasionalopenssl verify con un certificado suelto, o s_client en una imagen sin ca-certificates, no tiene raíces con las que completar la cadena, ni siquiera para sitios públicos.
Cómo solucionarlo
- 1.
Leer las líneas de profundidad
La profundidad 0 es el certificado del servidor; los números más altos son los intermedios que envió. La última línea de profundidad antes del error corresponde al certificado cuyo emisor falta.
bash echo | openssl s_client -connect api.example.com:443 -servername api.example.com 2>&1 | grep -E '^depth|verify' # depth=1 CN=Example Corp Issuing CA <- the highest certificate received # verify error:num=20:... <- its issuer is not in the trust store - 2.
Probar con la raíz esperada
-CAfile con la raíz correcta (y -verify_return_error para que los fallos sean fatales) muestra si esa raíz completa la cadena; «Verify return code: 0 (ok)» significa que sí.
bash # Verify against a specific root (exit code and "Verify return code: 0 (ok)" confirm it) echo | openssl s_client -connect api.example.com:443 -servername api.example.com \ -CAfile corp-root-ca.pem -verify_return_error 2>&1 | grep 'Verify return code' - 3.
Verificar archivos de certificado sin conexión
Pasa a openssl verify la raíz como -CAfile y los intermedios como -untrusted; un certificado hoja por sí solo no se puede verificar.
bash # Offline: root as trust anchor, intermediate(s) as untrusted helpers openssl verify -CAfile corp-root-ca.pem -untrusted intermediate.pem server.pem - 4.
Instalar la raíz en el almacén del sistema
Cuando la cadena termina legítimamente en una raíz de la empresa, añádela al almacén de la distribución; a partir de ese momento, las herramientas del equipo basadas en OpenSSL confían en ella.
bash sudo cp corp-root-ca.pem /usr/local/share/ca-certificates/corp-root-ca.crt sudo update-ca-certificates # openssl reads /etc/ssl/certs by default on Debian/Ubuntu
Errores relacionados
verify error:num=21:unable to verify the first certificateOpenSSL CLI (openssl s_client)verify error:num=19:self-signed certificate in certificate chainOpenSSL CLI (openssl s_client / openssl verify)curl: (60) SSL certificate problem: unable to get local issuer certificatecurl (OpenSSL backend)[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local…Python ssl module (OpenSSL)