OpenSSL作為工業級加密庫,其內部數據結構設計體現了高度的模塊化與擴展性。本文系統梳理X509、SSL_CTX、SSL_METHOD、X509_STORE等核心結構體,揭示它們在TLS/SSL協議處理、證書驗證、會話管理中的角色與關係。
SSL_CTX (全局上下文) ├── method: SSL_METHOD* → 協議版本實現虛表 ├── cert_store: X509_STORE* → 信任錨存儲 ├── cipher_list: STACK_OF(SSL_CIPHER) → 密碼套件列表 ├── sessions: LHASH_OF(SSL_SESSION) → 會話緩存哈希表 └── cert: CERT* → 證書/私鑰管理 SSL (連接上下文,由SSL_CTX派生) ├── ctx: SSL_CTX* → 回指全局上下文 ├── method: SSL_METHOD* → 具體協議實現 ├── session: SSL_SESSION* → 當前會話參數 ├── s3: SSL3_STATE* → TLS 1.2狀態機 └── rlayer/wlayer: RECORD_LAYER* → 記錄層收發狀態 X509 (證書對象) ├── cert_info: X509_CINF* → TBSCertificate核心字段 ├── sig_alg: X509_ALGOR* → 簽名算法標識 ├── signature: ASN1_BIT_STRING* → 簽名值 └── 扩展字段: altname, crldp, policy_cache...
struct ssl_method_st {
int version; // 協議版本標識
/* 連接生命週期 */
int (*ssl_new)(SSL *s); // 創建SSL對象
void (*ssl_free)(SSL *s); // 銷毀
int (*ssl_accept)(SSL *s); // 服務端握手
int (*ssl_connect)(SSL *s); // 客戶端握手
/* 數據收發 */
int (*ssl_read)(SSL *s, void *buf, int len);
int (*ssl_write)(SSL *s, const void *buf, int len);
int (*ssl_peek)(SSL *s, void *buf, int len); // 偷看不消費
/* 關閉與重協商 */
int (*ssl_shutdown)(SSL *s);
int (*ssl_renegotiate)(SSL *s);
/* 控制接口 */
long (*ssl_ctrl)(SSL *s, int cmd, long larg, void *parg);
long (*ssl_ctx_ctrl)(SSL_CTX *ctx, int cmd, long larg, void *parg);
/* 密碼套件管理 */
int (*num_ciphers)(void);
const SSL_CIPHER *(*get_cipher)(unsigned ncipher);
const struct ssl_method_st *(*get_ssl_method)(int version);
/* 內部編碼方法 */
struct ssl3_enc_method *ssl3_enc; // TLS 1.2加密/解密/摘要方法
};
設計精髓:面向對象的C語言實現,每個協議版本(SSLv3、TLS1.0/1.1/1.2/1.3、DTLS)對應一個SSL_METHOD實例,運行時通過函數指針動態分發,實現協議版本協商與無縫切換。
struct ssl_ctx_st {
const SSL_METHOD *method; // 協議方法
/* 密碼套件 */
STACK_OF(SSL_CIPHER) *cipher_list; // 排序後用於匹配
STACK_OF(SSL_CIPHER) *cipher_list_by_id; // 按ID查找
/* 證書驗證 */
struct x509_store_st *cert_store; // X509_STORE信任庫
/* 會話緩存 */
LHASH_OF(SSL_SESSION) *sessions; // 哈希表存儲
unsigned long session_cache_size; // 最大緩存數
int session_cache_mode; // CLIENT/SERVER/BOTH
long session_timeout; // 會話超時秒數
/* 回調函數族 */
int (*new_session_cb)(SSL *ssl, SSL_SESSION *sess);
void (*remove_session_cb)(SSL_CTX *ctx, SSL_SESSION *sess);
SSL_SESSION *(*get_session_cb)(SSL *ssl, ...);
/* 證書/私鑰 */
struct cert_st *cert; // 證書鏈與私鑰
pem_password_cb *default_passwd_callback; // PEM密碼回調
/* 客戶端證書驗證 */
int verify_mode; // VERIFY_NONE/PEER/FAIL_IF_NO_PEER_CERT
int (*default_verify_callback)(int ok, X509_STORE_CTX *ctx);
X509_VERIFY_PARAM *param; // 驗證參數(深度、時間、策略)
/* TLS擴展 */
STACK_OF(X509_NAME) *client_CA; // 發送給客戶端的CA列表
int (*tlsext_servername_callback)(SSL*, int*, void*); // SNI回調
/* ... 票據、NPN、ALPN、SRTP、PSK等擴展字段 ... */
};
struct x509_store_st {
/* 緩存 */
int cache; // 是否緩存查找結果
STACK_OF(X509_OBJECT) *objs; // 對象緩存(證書/CRL)
/* 查找方法鏈 */
STACK_OF(X509_LOOKUP) *get_cert_methods; // 文件/目錄/LDAP/引擎等
/* 驗證回調鏈 */
int (*verify)(X509_STORE_CTX *ctx); // 主驗證邏輯
int (*verify_cb)(int ok, X509_STORE_CTX *ctx); // 錯誤回調
int (*get_issuer)(X509 **issuer, X509_STORE_CTX*, X509*); // 尋找簽發者
int (*check_issued)(X509_STORE_CTX*, X509*, X509*); // 核對簽發關係
int (*check_revocation)(X509_STORE_CTX*); // 撤銷檢查
int (*get_crl)(X509_STORE_CTX*, X509_CRL**, X509*); // 獲取CRL
int (*check_crl)(X509_STORE_CTX*, X509_CRL*); // CRL有效性
int (*cert_crl)(X509_STORE_CTX*, X509_CRL*, X509*); // 證書對照CRL
/* 查找接口 */
STACK_OF(X509) * (*lookup_certs)(X509_STORE_CTX*, X509_NAME*);
STACK_OF(X509_CRL) * (*lookup_crls)(X509_STORE_CTX*, X509_NAME*);
int (*cleanup)(X509_STORE_CTX*); // 上下文清理
CRYPTO_EX_DATA ex_data; // 擴展數據掛載點
int references; // 引用計數
};
驗證流程:X509_STORE_CTX作為一次性驗證上下文,持有待驗證鏈、信任錨、驗證參數、錯誤狀態,調用verify回調執行標準驗證邏輯(鏈構建→簽名驗證→有效期→撤銷→策略→名稱約束)。
struct x509_st {
X509_CINF *cert_info; // TBSCertificate (簽名保護部分)
X509_ALGOR *sig_alg; // 簽名算法
ASN1_BIT_STRING *signature; // 簽名值
int valid; // 緩存驗證結果
int references; // 引用計數
char *name; // 友好名稱
CRYPTO_EX_DATA ex_data; // 應用擴展數據
/* 預解析緩存字段(避免重複ASN.1解析) */
long ex_pathlen; // 基本約束路徑長度
unsigned long ex_flags; // 擴展標記位
unsigned long ex_kusage; // 密鑰用法
unsigned long ex_xkusage; // 擴展密鑰用法
ASN1_OCTET_STRING *skid; // Subject Key Identifier
AUTHORITY_KEYID *akid; // Authority Key Identifier
X509_POLICY_CACHE *policy_cache; // 策略緩存
STACK_OF(DIST_POINT) *crldp; // CRL分發點
STACK_OF(GENERAL_NAME) *altname; // Subject Alternative Name
NAME_CONSTRAINTS *nc; // 名稱約束
/* ... RFC3779 IP/AS擴展 ... */
};
struct x509_cinf_st {
ASN1_INTEGER *version; // v1/v2/v3
ASN1_INTEGER *serialNumber; // 序列號
X509_ALGOR *signature; // 簽名算法(與外層一致)
X509_NAME *issuer; // 簽發者DN
X509_VAL *validity; // 有效期(notBefore/notAfter)
X509_NAME *subject; // 主體DN
X509_PUBKEY *key; // 公鑰信息
ASN1_BIT_STRING *issuerUID; // v2 簽發者唯一標識
ASN1_BIT_STRING *subjectUID; // v2 主體唯一標識
STACK_OF(X509_EXTENSION) *extensions; // v3 擴展
ASN1_ENCODING enc; // DER編碼緩存
};
struct X509_name_st {
STACK_OF(X509_NAME_ENTRY) *entries; // RDN序列
int modified; // bytes緩存是否失效
BUF_MEM *bytes; // DER編碼緩存
unsigned char *canon_enc; // 規範編碼(用於比較)
int canon_enclen;
};
DN比較邏輯:先比canon_enc(規範編碼),效率遠高於逐項比較entries。
struct stack_st {
int num; // 元素數量
char **data; // 元素指針數組
int sorted; // 是否已排序
int num_alloc; // 分配容量
int (*comp)(const void*, const void*); // 比較函數(用於bsearch/qsort)
};
STACK_OF(T)通過宏展開為struct stack_st*,配合sk_TYPE_*系列函數實現類型安全操作。
| 模式 | 體現結構體 | 作用 |
|---|---|---|
| 虛函數表 | SSL_METHOD, X509_LOOKUP_METHOD | 運行時多態,協議版本/存儲後端可插拔 |
| 引用計數 | SSL_CTX, X509, SSL_SESSION等 | 共享所有權,線程安全釋放 |
| 上下文分離 | SSL_CTX vs SSL, X509_STORE vs X509_STORE_CTX | 配置與運行狀態分離,支持並發 |
| 緩存預計算 | X509.ex_*, X509_NAME.canon_enc | 避免重複ASN.1解析,提升驗證性能 |
| 回調擴展點 | SSL_CTX.*_cb, X509_STORE.*_cb | 業務邏輯注入,支持自定義驗證/密鑰管理 |
| 通用容器 | _STACK, LHASH | 類型擦除實現泛型集合 |
SSL_new從同一SSL_CTX派生SSL是線程安全的SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_cb),在verify_cb中打印X509_STORE_CTX_get_error(ctx)與X509_verify_cert_error_string(err)SSL_CTX_set_session_cache_mode)、會話票據(SSL_CTX_set_tlsext_ticket_key_cb)、OCSP Stapling_up_ref/_free配對,使用CRYPTO_EX_DATA掛載應用數據時註冊析構函數OpenSSL結構體設計是C語言系統級編程的教科書級範例:在無繼承、無泛型、無GC的語言約束下,通過函數指針、不透明指針、引用計數、上下文分離等模式,構建了支撐全球HTTPS生態的核心基礎設施。深入理解這些結構體,是從「調用API」進階為「駕馭OpenSSL」的關鍵一步。