Para verificar una conexión con TLS/SSL publicada con un certificado de servidor firmado por una CA interna se debe incorporar el certificado de esta CA en el parámetro -CAfile

pablo@notebook:~$ echo -e "GET /index.html HTTP/1.1\r\nHost:$(hostname)\r\n\r\n" | openssl s_client -connect vm-linux.local:443 -CAfile ca.pem -quiet 

depth=1 C = AR, ST = CABA, L = CABA, O = Mi Empresa SA, OU = IT, emailAddress = admin@miDominio.com.ar, CN = miDominio-CA
verify return:1
depth=0 C = AR, ST = CABA, L = CABA, O = Mi Organizacion SA, OU = IT, emailAddress = admin@miorganizacion.com.ar, CN = vm-linux.local
verify return:1
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)

Si no definimos dicho parámetro con la cadena certificante obtendríamos un 200 OK con los siguientes errores

pablo@notebook:~$ echo -e "GET /index.html HTTP/1.1\r\nHost:$(hostname)\r\n\r\n" | openssl s_client -connect vm-linux.local:443  -quiet 

depth=0 C = AR, ST = CABA, L = CABA, O = Mi Organizacion SA, OU = IT, emailAddress = admin@miorganizacion.com.ar, CN = vm-linux.local
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 C = AR, ST = CABA, L = CABA, O = Mi Organizacion SA, OU = IT, emailAddress = admin@miorganizacion.com.ar, CN = vm-linux.local
verify error:num=21:unable to verify the first certificate
verify return:1
HTTP/1.1 200 OK

Ahora el mismo ejemplo pero con CURL

pablo@notebook:~$ curl --cacert ca.pem  https://vm-linux.local:443

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>

y el error correspondiente si no enviamos la cadena en el parámetro –cacert

pablo@notebook:~$ curl https://vm-linux.local:443

curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.haxx.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

Ahora verificamos una conexión incorporando autenticación con mutual SSL (certificado del lado del cliente). Para ello se debe definir el certificado de cliente en el parámetro -cert y la clave privada del cliente en el parámetro -key

pablo@notebook:~$ echo -e "GET /index.html HTTP/1.1\r\nHost:$(hostname)\r\n\r\n" | openssl s_client -connect vm-linux.local:443 -cert cliente1.pem -key cliente1.key -CAfile ca.pem -quiet 

depth=1 C = AR, ST = CABA, L = CABA, O = Mi Empresa SA, OU = IT, emailAddress = admin@miDominio.com.ar, CN = miDominio-CA
verify return:1
depth=0 C = AR, ST = CABA, L = CABA, O = Mi Organizacion SA, OU = IT, emailAddress = admin@miorganizacion.com.ar, CN = vm-linux.local
verify return:1
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Fri, 24 Jun 2022 00:00:26 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Sat, 18 Jun 2022 03:00:21 GMT
Connection: keep-alive
ETag: "62ad3fc5-264"
Accept-Ranges: bytes

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>

Si no se envían estos parámetros el error de autenticación se vería de la siguiente manera

pablo@notebook:~$ echo -e "GET /index.html HTTP/1.1\r\nHost:$(hostname)\r\n\r\n" | openssl s_client -connect vm-linux.local:443  -CAfile ca.pem -quiet 

depth=1 C = AR, ST = CABA, L = CABA, O = Mi Empresa SA, OU = IT, emailAddress = admin@miDominio.com.ar, CN = miDominio-CA
verify return:1
depth=0 C = AR, ST = CABA, L = CABA, O = Mi Organizacion SA, OU = IT, emailAddress = admin@miorganizacion.com.ar, CN = vm-linux.local
verify return:1
HTTP/1.1 400 Bad Request
Server: nginx/1.18.0 (Ubuntu)
Date: Fri, 24 Jun 2022 00:05:51 GMT
Content-Type: text/html
Content-Length: 246
Connection: close

<html>
<head><title>400 No required SSL certificate was sent</title></head>

Mismo caso pero con CURL, definiendo el certificado de cliente en el parámetro –cert y la clave privada en el parámetro –key

pablo@notebook:~$ curl --cacert ca.pem --cert cliente1.pem --key cliente1.key https://vm-linux.local:443

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>

Y el siguiente es el resultado que obtendríamos si se intenta acceder a una publicación con MUTUAL sin los certificados correspondientes

pablo@notebook:~$ curl --cacert ca.pem https://vm-linux.local:443

<html>
<head><title>400 No required SSL certificate was sent</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<center>No required SSL certificate was sent</center>
<hr><center>nginx/1.18.0 (Ubuntu)</center>
</body>
</html>

ca.pem contiene la cadena certificadora asociada a los certificados de servidor con los que se publican los servicios web.

cliente1.key es la clave privada generada por el cliente que se quiere autenticar al servicio publicado con Mutual SSL.

cliente1.pem es un certificado firmado por la CA definida en el servidor para autenticar a los clientes.

TIP: Verificar que el CSR y el certificado firmado por la CA pertenecen a la misma clave privada comparando los hash SHA256 de cada uno

pablo@notebook:~$ openssl pkey -in cliente1.key -pubout -outform pem | sha256sum
528c0019e27100d2879759fdb3072951e555f1ef9a1c61f98e3dca747c092eed  -
pablo@notebook:~$ openssl x509 -in cliente1.pem -pubkey -noout -outform pem | sha256sum 
528c0019e27100d2879759fdb3072951e555f1ef9a1c61f98e3dca747c092eed  -
pablo@notebook:~$ openssl req -in cliente1.csr -pubkey -noout -outform pem | sha256sum
528c0019e27100d2879759fdb3072951e555f1ef9a1c61f98e3dca747c092eed  -