Bài TLS trước, server gửi một Certificate và openssl báo Verify return code: 0 (ok). Nhưng dựa vào đâu client tin cái chứng chỉ đó? Vì sao trình duyệt tin coffeecode.vn nhưng cảnh báo đỏ với một cert tự ký? Câu trả lời là chuỗi tin cậy X.509. Cách hiểu nhanh nhất là tự tay dựng một CA, ký một cert, rồi xác thực nó — thấy chuỗi hoạt động và thấy nó gãy khi CA sai. Đo thật bằng openssl 3.0.

Một chứng chỉ X.509 chứa gì

Chứng chỉ không phải "mật khẩu bí mật" — nó là một tài liệu công khai gói khóa công khai kèm danh tính, được một bên thứ ba ký. Các trường chính:

  • Subject: chứng chỉ cấp cho ai (ví dụ CN=web.local).
  • Issuer: ai ký nó — một Certificate Authority (CA).
  • Validity: notBefore / notAfter — thời hạn hiệu lực.
  • Public Key: khóa công khai của chủ thể (dùng trong bắt tay TLS).
  • SAN (Subject Alternative Name): danh sách tên miền/IP mà cert này hợp lệ — trình duyệt hiện đại kiểm SAN, không còn kiểm CN.
  • Signature: CA ký toàn bộ phần trên bằng khóa riêng của CA. Ai có khóa công khai của CA đều kiểm được chữ ký này.

Chuỗi tin cậy: lá → trung gian → gốc

cert web.local  --(ký bởi)-->  Intermediate CA  --(ký bởi)-->  Root CA

Trình duyệt và hệ điều hành cài sẵn một kho khóa công khai của vài trăm Root CA tin cậy. Xác thực một cert là lần theo chữ ký từ cert lá lên qua các cert trung gian, tới khi chạm một Root CA có trong kho — nếu tới được và mọi chữ ký hợp lệ, cert được tin. Vì vậy server phải gửi cả cert lá lẫn các cert trung gian (như chuỗi 3 tầng của coffeecode.vn ở bài trước); thiếu trung gian là lỗi "incomplete chain" kinh điển.

openssl req -x509 -key ca.key -out ca.crt ...    # CA tự ký (root)
openssl req -new -key srv.key -out srv.csr ...   # CSR: đơn xin cấp cert
openssl x509 -req -in srv.csr -CA ca.crt ...     # CA ký -> cert lá
openssl x509 -in srv.crt -noout -text            # đọc cert
openssl verify -CAfile ca.crt srv.crt            # kiểm chuỗi

Ảnh chụp đoạn mã nền tối giải thích chứng chỉ X.509 và chuỗi tin cậy vì sao trình duyệt tin server, một chứng chỉ X.509 chứa gì Subject chứng chỉ này cấp cho ai CN web.local cộng SAN, Issuer ai ký nó một CA, Validity notBefore notAfter thời hạn, Public Key khóa công khai của chủ thể, SAN danh sách tên miền IP hợp lệ thay CN cũ, Signature CA ký toàn bộ phần trên bằng khóa riêng của CA, chuỗi tin cậy lá tới trung gian tới gốc cert web.local ký bởi Intermediate CA ký bởi Root CA trình duyệt chỉ tin sẵn Root CA kho tin cậy hệ điều hành xác thực là lần theo chữ ký từ lá lên tới một root đã tin server phải gửi cả cert lá cộng trung gian đủ chuỗi, openssl tạo CA ký cert xác thực req -x509 CA tự ký req -new CSR xin cấp x509 -req CA ký cert lá x509 -text đọc cert verify -CAfile kiểm chuỗi

Hình 1: Cấu trúc một cert X.509 và chuỗi tin cậy lá → trung gian → gốc. Trình duyệt chỉ tin sẵn Root CA; xác thực là lần theo chữ ký lên tới một root đã tin. openssl tạo CA, ký cert và kiểm chuỗi.

Đo thật: dựng CA và ký cert

Tạo một CA gốc (CN=CaPhe Root CA), rồi ký một cert cho web.local, kèm SAN. Đọc cert và xác thực:

Ảnh chụp bảng kết quả đo thật nền tối chạy openssl 3.0 tự dựng CA rồi ký cert cho web.local, phần một đọc cert lá openssl x509 subject issuer dates subject C VN O Blog CN web.local issuer C VN O CaPheCA CN CaPhe Root CA ai ký notBefore Sep 30 2026 notAfter Sep 30 2027, phần hai SAN tên miền IP hợp lệ của cert DNS web.local DNS www.web.local IP Address 127.0.0.1 trình duyệt so tên miền bạn gõ với SAN không so CN nữa, phần ba xác thực chuỗi bằng CA đúng openssl verify -CAfile ca.crt srv.crt srv.crt OK chữ ký khớp tin được, phần bốn xác thực bằng CA sai thất bại openssl verify -CAfile other.crt srv.crt error 20 unable to get local issuer certificate cert không do CA này ký không lần được chuỗi tin cậy

Hình 2: Cert lá có subject=CN=web.local, issuer=CN=CaPhe Root CA, hạn 1 năm; SAN liệt kê web.local, www.web.local, 127.0.0.1. openssl verify với CA đúng → OK; với CA sai → error 20: unable to get local issuer certificate.

Kết quả cho thấy toàn bộ cơ chế:

  • Đọc cert: subject=CN=web.local (cấp cho ai), issuer=CN=CaPhe Root CA (ai ký), hạn notBefore/notAfter một năm. issuer của cert lá chính là subject của CA — đó là mắt xích của chuỗi.
  • SAN: DNS:web.local, DNS:www.web.local, IP Address:127.0.0.1. Đây là danh sách tên hợp lệ; nếu bạn truy cập web.local thì khớp, nhưng khac.local sẽ bị trình duyệt từ chối dù cert vẫn còn hạn và do CA tin cậy ký — tên phải khớp SAN.
  • Xác thực đúng: openssl verify -CAfile ca.crt srv.crt → srv.crt: OK. Chữ ký của CA trên cert lá kiểm được bằng khóa công khai của CA → tin được.
  • Xác thực sai: dùng một CA khác (other.crt) → error 20: unable to get local issuer certificate. Cert lá không do CA này ký nên không lần được chuỗi — đây chính xác là điều xảy ra với cert tự ký hoặc CA nội bộ chưa được cài vào kho tin cậy: trình duyệt báo "not trusted".

Đánh đổi và lưu ý

Cert tự ký không "kém an toàn" về mã hóa — chỉ thiếu xác thực. Một cert tự ký vẫn mã hóa kênh y hệt (bí mật + toàn vẹn). Cái nó thiếu là bên thứ ba chứng nhận danh tính, nên trình duyệt không biết có phải kẻ giữa đường tự cấp cert hay không → cảnh báo. Trong nội bộ (dev, service-to-service), cài CA riêng vào kho tin cậy là cách hợp lệ để dùng cert tự quản mà không cảnh báo.

Hạn cert ngày càng ngắn. Cert công khai từng có hạn 1–2 năm, nay xu hướng rút xuống (Let's Encrypt 90 ngày, và ngành đang tiến tới ~47 ngày). Ngắn hạn giảm rủi ro khi khóa lộ, nhưng buộc phải tự động gia hạn (ACME/certbot) — gia hạn tay là quên và sập site. notAfter là thứ cần giám sát.

SAN, không phải CN. Nhiều lỗi "cert không hợp lệ" đến từ việc cert cũ chỉ có CN mà thiếu SAN, hoặc SAN thiếu biến thể (www vs không www). Khi tạo cert, luôn khai đủ SAN cho mọi tên sẽ dùng.

Ba ý mang về

  1. Chứng chỉ X.509 là tài liệu công khai gói khóa công khai + danh tính (subject/issuer/validity/SAN) được CA ký; xác thực là kiểm chữ ký của CA — đo thật, cert web.local do CaPhe Root CA ký, đọc được đủ các trường bằng openssl x509.
  2. Chuỗi tin cậy đi từ lá → trung gian → gốc: trình duyệt chỉ tin sẵn Root CA và lần theo chữ ký lên tới đó — openssl verify với CA đúng cho OK, với CA sai cho error 20: unable to get local issuer certificate (đúng như cert tự ký/incomplete chain gặp trên thực tế).
  3. Nhớ SAN, hạn, và bản chất tự ký: trình duyệt kiểm tên theo SAN không phải CN; hạn cert ngày càng ngắn nên phải tự động gia hạn; cert tự ký vẫn mã hóa tốt, chỉ thiếu chứng nhận danh tính (cài CA riêng vào kho tin cậy để dùng nội bộ).

Nguồn

Phần sau ta chuyển sang tầng vận hành: nginx làm reverse proxy — proxy_pass, các header X-Forwarded-*, và đo thật việc cân bằng tải giữa nhiều backend.