Triệu chứng chính: curl: (60) SSL certificate problem: unable to get local issuer certificate, certificate has expired, no alternative certificate subject name matches target host name, tlsv1 alert protocol version, handshake failure, Python CERTIFICATE_VERIFY_FAILED, Go x509: certificate signed by unknown authority.
Lỗi thường gặp
| Triệu chứng | Nguyên nhân | Cách xử lý |
|---|---|---|
unable to get local issuer certificate (trình duyệt vẫn chạy) |
Server chỉ gửi leaf, thiếu intermediate | Cấu hình fullchain.pem thay cho cert.pem; kiểm tra bằng -showcerts |
self-signed certificate in certificate chain / unknown authority |
Cert do CA nội bộ cấp, client chưa tin root | Thêm root CA vào trust store, hoặc trỏ --cacert / -CAfile |
certificate has expired |
Cert hết hạn, hoặc intermediate/root cũ hết hạn trong chain | Renew; xem -dates của từng cert trong chain |
certificate is not yet valid |
Đồng hồ client lệch | Kiểm tra NTP: timedatectl, chronyc tracking |
no alternative certificate subject name matches |
Hostname không có trong SAN (CN không còn được xét) | Cấp lại cert có đúng DNS/IP trong SAN |
| Đúng domain nhưng nhận cert của domain khác | Client không gửi SNI (gọi bằng IP, client cũ), server trả cert mặc định | Gọi bằng hostname; với curl dùng --resolve thay vì sửa URL thành IP |
tlsv1 alert protocol version |
Client chỉ hỗ trợ TLS 1.0/1.1 hoặc server tắt TLS 1.2 | Bật TLS 1.2 + 1.3 phía server; nâng cấp client |
handshake failure / no shared cipher |
Không có cipher chung (thiết bị cũ, FIPS, cert RSA vs ECDSA) | So danh sách cipher hai bên; thử -tls1_2 và cipher cụ thể |
key values mismatch khi Nginx start |
Key không khớp cert | So hash public key, xem OpenSSL cheatsheet |
wrong version number |
Nói TLS với cổng plain HTTP (hoặc ngược lại) | Kiểm tra cổng/scheme, proxy có terminate TLS chưa |
mTLS: certificate required / bad certificate |
Client không gửi cert, hoặc cert client không do CA server tin cấp | Gửi -cert/-key; kiểm tra CA cấu hình phía server, EKU clientAuth |
| Java/thiết bị mạng báo lỗi, curl thì OK | Trust store riêng (JKS, cert store của router) không có root/intermediate | Import CA vào keystore/trustpoint riêng của ứng dụng |
Các bước khoanh vùng
- DNS và cổng đúng chưa?
dig +short api.example.com,nc -vz api.example.com 443. - Server gửi gì?
openssl s_client ... -showcerts: đếm số cert, xems:(subject) vài:(issuer) từng cấp nối nhau đến root. - Cert đúng tên, đúng hạn?
-dates,-ext subjectAltName. - Client tin CA không? Thử
-CAfilevới root nội bộ; nếu chạy được thì lỗi nằm ở trust store client. - Phiên bản/cipher? Thử lần lượt
-tls1_3,-tls1_2; đọc dòngProtocolvàCipher. - Có proxy ở giữa? Load balancer, Cloudflare, proxy công ty có thể terminate TLS và trả cert khác: so fingerprint với cert trên origin.
Cấu hình đúng
Nginx tối thiểu, chạy được với mọi client hiện đại:
server {
listen 443 ssl;
http2 on;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
}Thêm root CA nội bộ vào trust store:
# Debian/Ubuntu (file phải có đuôi .crt, định dạng PEM)
sudo cp lab-root-ca.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
# RHEL/Rocky
sudo cp lab-root-ca.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trustĐừng "sửa" bằng curl -k, verify=False hay insecure: true trong tool automation. Lỗi sẽ biến mất cùng với toàn bộ lợi ích của TLS. Trỏ đúng CA bundle (REQUESTS_CA_BUNDLE, SSL_CERT_FILE, --cacert).
Kiểm tra nhanh
H=api.example.com
# Chain server gửi: dòng s:/i: phải nối nhau
openssl s_client -connect $H:443 -servername $H -showcerts </dev/null 2>/dev/null | grep -E '^ *[0-9] s:|^ *i:'
# Thời hạn + SAN của cert đang chạy
openssl s_client -connect $H:443 -servername $H </dev/null 2>/dev/null | openssl x509 -noout -dates -ext subjectAltName
# Kết quả verify (0 = ok)
openssl s_client -connect $H:443 -servername $H </dev/null 2>/dev/null | grep 'Verify return code'
# Protocol + cipher thương lượng được
openssl s_client -connect $H:443 -servername $H -brief </dev/null
# curl chi tiết handshake; ép IP mà vẫn gửi SNI đúng
curl -vI https://$H/ 2>&1 | grep -E 'SSL|TLS|subject|issuer|expire'
curl -vI --resolve $H:443:203.0.113.20 https://$H/
# Python dùng CA nào
python3 -c "import ssl; print(ssl.get_default_verify_paths())"Liên quan: TLS và PKI, Let's Encrypt với certbot.