Decoding SSL3_READ_BYTES:sslv3 Alert Bad Certificate Error Despite Verify Return Code 0


2 views

When running openssl s_client -host example.xyz -port 9093, seeing both "bad certificate" alerts and "Verify return code: 0 (ok)" creates confusion. Let's dissect what's actually happening under the hood.

139810559764296:error:14094412:SSL routines:SSL3_READ_BYTES:sslv3 alert bad certificate:s3_pkt.c:1259:SSL alert number 42
39810559764296:error:140790E5:SSL routines:SSL23_WRITE:ssl handshake failure:s23_lib.c:184:

This indicates the server rejected your client certificate, not that the server's certificate is invalid. The key points:

  • Alert 42 specifically means "bad certificate" in SSL/TLS
  • This is a unilateral rejection from the server side
  • The handshake failure occurs after certificate exchange

The "Verify return code: 0 (ok)" only means the client successfully verified the server's certificate. It doesn't indicate whether the server accepted your client certificate (if present).

Here are typical situations where this occurs:

1. Mutual TLS (mTLS) Configuration Issues

# Force client certificate requirement (server side)
openssl s_server -accept 443 -cert server.pem -key server.key -verify 1

If your client doesn't provide a certificate or provides an invalid one, you'll see this error.

2. Certificate Chain Problems

Debug with:

openssl s_client -host example.xyz -port 9093 -showcerts -debug

3. Protocol Version Mismatch

Try forcing TLS version:

openssl s_client -host example.xyz -port 9093 -tls1_2

For proper debugging:

  1. Check server requirements: Does it expect client certificates?
  2. Verify certificate format: PEM vs DER, proper chain
  3. Test with minimal configuration: Start with basic connection then add complexity

Here's how to properly test mTLS:

# Server side
openssl s_server -accept 9093 \
  -cert server.crt -key server.key \
  -CAfile ca.crt -verify 1

# Client side (with valid cert)
openssl s_client -connect localhost:9093 \
  -cert client.crt -key client.key \
  -CAfile ca.crt

The complete session output should show successful verification on both sides.


When running openssl s_client -host example.xyz -port 9093, you might encounter this confusing scenario:

139810559764296:error:14094412:SSL routines:SSL3_READ_BYTES:sslv3 alert bad certificate:s3_pkt.c:1259:SSL alert number 42
39810559764296:error:140790E5:SSL routines:SSL23_WRITE:ssl handshake failure:s23_lib.c:184:
...
Verify return code: 0 (ok)

The apparent contradiction occurs because:

  • The remote server accepted your certificate (hence Verify return code 0)
  • But the server's certificate failed validation on your client side
# Check certificate chain completeness
openssl s_client -showcerts -connect example.xyz:9093

# Verify against specific CA store
openssl verify -CAfile /path/to/cacert.pem server_cert.pem

Typical triggers include:

  • Expired or not-yet-valid server certificate
  • Missing intermediate certificates
  • Hostname mismatch (CN/SAN doesn't match requested host)
  • Revoked certificate (CRL/OCSP check failure)

For comprehensive debugging:

# Full connection test with debug
openssl s_client -connect example.xyz:9093 -debug -state -tlsextdebug

# Check certificate expiration
openssl x509 -in cert.pem -noout -dates

# Verify certificate chain
openssl verify -untrusted intermediate.pem -CAfile root.pem server.pem

This commonly occurs with certificates from commercial CAs:

# Without intermediate (fails)
openssl s_client -connect api.example.com:443 -CAfile root.crt

# With intermediate (works)
openssl s_client -connect api.example.com:443 -CAfile root.crt -cert_chain intermediate.crt

When implementing SSL in code, handle verification properly:

// Python example with certificate verification
import ssl
context = ssl.create_default_context()
context.verify_mode = ssl.CERT_REQUIRED
context.load_verify_locations('/etc/ssl/certs/ca-certificates.crt')

try:
    with socket.create_connection(('example.xyz', 9093)) as sock:
        with context.wrap_socket(sock, server_hostname='example.xyz') as ssock:
            print(ssock.version())
except ssl.SSLError as e:
    print(f"SSL Error: {e}")

If you control the server, verify:

  • SSL protocols offered (avoid SSLv3)
  • Certificate chain is properly configured
  • Correct private key is loaded
# Apache SSL config example
SSLEngine on
SSLCertificateFile /path/to/cert.pem
SSLCertificateKeyFile /path/to/key.key
SSLCertificateChainFile /path/to/chain.pem
SSLProtocol all -SSLv2 -SSLv3