文件

187 行
8.2 KiB
C

/* x509.h —— X.509 v3 证书最小解析与签名验证
*
* 范围(满足 TLS 证书链验证):
* - 解析 DER 编码的 X.509 v3 证书,提取关键字段
* - 支持的签名算法:
* sha1WithRSAEncryption / sha256WithRSA / sha384WithRSA / sha512WithRSA
* ecdsa-with-SHA256 (P-256)
* - 支持的公钥: RSA, ECDSA P-256 (prime256v1 / secp256r1)
* - 证书签名验证(用颁发者公钥验签 tbsCertificate)
* - 有效期检查、Name 原始 DER 比较、自签名判定
* - SAN dNSName 提取与主机名匹配(单层通配符)
* - 简单链验证: leaf -> (可选中间证书) -> 信任锚
*
* 不实现: CRL/OCSP、策略与名称约束、多值 RDN 排序规范化、Ed25519 证书
* (Name 比较按 DER 字节,RFC 5280 允许的实现级选择)
*
* 内存约定: paze_x509_cert_t 内部 issuer_dn/subject_dn/tbs_der/signature
* 指向证书 DER 缓冲。parse_der 时该缓冲由调用者拥有(须在 cert 使用期间有效);
* load_pem 内部 malloc 一份 DER,由 paze_x509_free 释放。
*/
#ifndef PAZE_X509_H
#define PAZE_X509_H
#include "paze/paze_types.h"
#include "paze/rsa.h"
#include "paze/ecdsa.h"
#ifdef __cplusplus
extern "C" {
#endif
/* 公钥类型 */
typedef enum {
PAZE_X509_PK_UNKNOWN = 0,
PAZE_X509_PK_RSA = 1,
PAZE_X509_PK_ECDSA_P256 = 2,
PAZE_X509_PK_ECDSA_P384 = 3,
PAZE_X509_PK_ECDSA_P521 = 4
} paze_x509_pk_kind_t;
/* 证书签名算法 */
typedef enum {
PAZE_X509_SIG_UNKNOWN = 0,
PAZE_X509_SIG_SHA1_RSA = 1,
PAZE_X509_SIG_SHA256_RSA = 2,
PAZE_X509_SIG_SHA384_RSA = 3,
PAZE_X509_SIG_SHA512_RSA = 4,
PAZE_X509_SIG_ECDSA_SHA256 = 5, /* P-256 */
PAZE_X509_SIG_ECDSA_SHA384 = 6, /* P-384 */
PAZE_X509_SIG_ECDSA_SHA512 = 7 /* P-521 */
} paze_x509_sig_alg_t;
#define PAZE_X509_SAN_MAX 8
#define PAZE_X509_DNS_MAX 255
typedef struct {
int version; /* 0=v1, 1=v2, 2=v3 */
uint8_t serial[64]; /* 大端序列号(去前导零) */
size_t serial_len;
paze_x509_sig_alg_t sig_alg; /* 证书签名算法(tbs 内 signature 与外层应一致) */
/* 颁发者/主体:原始 DER(指向 cert der,便于链匹配比较) */
const uint8_t *issuer_dn; size_t issuer_dn_len;
const uint8_t *subject_dn; size_t subject_dn_len;
int64_t not_before, not_after; /* Unix 秒 */
paze_x509_pk_kind_t pk_kind; /* 主体公钥类型 */
paze_rsa_pub_t rsa_pub; /* pk_kind==RSA 时有效 */
paze_ecdsa_pub_t ecdsa_pub; /* pk_kind==ECDSA_P256 时有效 */
/* pk_kind==ECDSA_P384 时,主体公钥为未压缩点 0x04||X||Y (97 字节) */
uint8_t ecdsa_p384_pub[97];
/* pk_kind==ECDSA_P521 时,主体公钥为未压缩点 0x04||X||Y (133 字节) */
uint8_t ecdsa_p521_pub[133];
/* 签名值(BIT STRING 内容,已去掉 unused-bits 前导 0x00) */
const uint8_t *signature; size_t signature_len;
/* tbsCertificate 原始 DER(验签时对此哈希) */
const uint8_t *tbs_der; size_t tbs_der_len;
/* SAN dNSName 列表 */
char san_dns[PAZE_X509_SAN_MAX][PAZE_X509_DNS_MAX + 1];
int san_dns_count;
/* 内部拥有的 DER 缓冲(load_pem 时 malloc);parse_der 时为 NULL */
uint8_t *owned_der;
} paze_x509_cert_t;
/* ---- 解析 ---- */
/* 从 DER 解析证书。cert 内部指针指向 der(der 须在 cert 使用期间有效)。
* 成功后 cert->owned_der 为 NULL。 */
paze_status_t paze_x509_parse_der(paze_x509_cert_t *cert, const uint8_t *der, size_t len);
/* 从 PEM 文本加载证书(查找 "CERTIFICATE" label 的第一段)。
* 内部 malloc DER 副本,须用 paze_x509_free 释放。 */
paze_status_t paze_x509_load_pem(paze_x509_cert_t *cert, const char *pem_text);
/* 释放 load_pem 分配的内部 DER。parse_der 得到的 cert 调用此函数安全(no-op)。
* 不释放 cert 本身。 */
void paze_x509_free(paze_x509_cert_t *cert);
/* ---- 验证 ---- */
/* 用颁发者证书的公钥验证本证书签名。
* 校验:签名算法、tbs 哈希、签名值。返回 PAZE_OK=通过, PAZE_ERR_VERIFY=失败。 */
paze_status_t paze_x509_verify_signature(const paze_x509_cert_t *cert,
const paze_x509_cert_t *issuer);
/* 自签名判定:issuer_dn == subject_dn (DER 字节比较)。 */
int paze_x509_is_self_signed(const paze_x509_cert_t *cert);
/* 有效期检查:now 为 Unix 秒。返回 PAZE_OK=在有效期内, PAZE_ERR_VERIFY=过期/未生效。 */
paze_status_t paze_x509_check_validity(const paze_x509_cert_t *cert, int64_t now);
/* 主机名匹配:检查 SAN dNSName(优先) 或 subject CN。
* 支持单层左通配符 "*.example.com"。返回 1=匹配, 0=不匹配。 */
int paze_x509_match_host(const paze_x509_cert_t *cert, const char *host);
/* 证书链验证。
* chain[0] = leaf (终端证书)
* chain[1..chain_len-1] = 中间证书(如有,按从 leaf 到 root 方向)
* anchors[] = 信任锚(自签名 CA 证书)
* 校验项:每级 issuer==subject(DN)、签名、有效期(用 now)。
* 不做吊销检查。返回 PAZE_OK=通过, 否则 PAZE_ERR_VERIFY / PAZE_ERR_FORMAT。
* 若 chain 末端能直接与某 anchor 自身相同(DN+公钥),亦视为通过。 */
paze_status_t paze_x509_verify_chain(const paze_x509_cert_t *chain, int chain_len,
const paze_x509_cert_t *anchors, int anchor_count,
int64_t now);
/* ---- 证书生成(最小集,用于自签名/测试/TLS 服务器) ----
* 注意:仅实现最小子集,无 SAN/keyUsage 等扩展。签发者私钥决定签名算法:
* RSA -> sha256WithRSAEncryption
* ECDSA P256 -> ecdsa-with-SHA256
* 有效期用 GeneralizedTime 编码。serial 由调用者提供。 */
/* 证书签发者私钥 */
typedef struct {
paze_x509_pk_kind_t kind;
paze_rsa_priv_t rsa; /* kind==RSA 时有效 */
uint8_t ecdsa_d[32]; /* kind==ECDSA_P256 时,私钥标量大端 */
uint8_t ecdsa_d384[48]; /* kind==ECDSA_P384 时,私钥标量大端 */
uint8_t ecdsa_d521[66]; /* kind==ECDSA_P521 时,私钥标量大端 */
} paze_x509_signer_t;
/* 构造 SubjectPublicKeyInfo DER。
* RSA: rsaEncryption + RSAPublicKey; EC: ecPublicKey + prime256v1 + 0x04||X||Y */
paze_status_t paze_x509_build_spki(uint8_t *out, size_t *outlen,
paze_x509_pk_kind_t kind,
const paze_rsa_pub_t *rsa_pub,
const paze_ecdsa_pub_t *ec_pub);
/* 构造 secp384r1 的 SubjectPublicKeyInfo DER:
* ecPublicKey + secp384r1 + pub_affine(97 字节 0x04||X||Y) */
paze_status_t paze_x509_build_spki384(uint8_t *out, size_t *outlen,
const uint8_t pub_affine[97]);
/* 构造 secp521r1 的 SubjectPublicKeyInfo DER:
* ecPublicKey + secp521r1 + pub_affine(133 字节 0x04||X||Y) */
paze_status_t paze_x509_build_spki521(uint8_t *out, size_t *outlen,
const uint8_t pub_affine[133]);
/* 构造仅含 commonName 的 Name DER (UTF8String)。 */
paze_status_t paze_x509_build_dn_cn(uint8_t *out, size_t *outlen, const char *cn);
/* 用 issuer 私钥对 subject 公钥签发证书,生成完整 Certificate DER。
* out 缓冲,*outlen 给容量/写回实际长度。
* subject_spki/subject_dn/issuer_dn 为完整 TLV DER。 */
paze_status_t paze_x509_sign_cert(uint8_t *out, size_t *outlen,
const uint8_t *subject_spki, size_t spki_len,
const uint8_t *subject_dn, size_t subj_len,
const uint8_t *issuer_dn, size_t iss_len,
const paze_x509_signer_t *issuer_priv,
int64_t not_before, int64_t not_after,
const uint8_t *serial, size_t serial_len);
/* 便利:自签名证书(issuer==subject)。
* 内部从 priv 推导公钥构造 SPKI,DN 用 cn,serial 随机 8 字节。 */
paze_status_t paze_x509_make_self_signed(uint8_t *out, size_t *outlen,
paze_x509_signer_t *priv, const char *cn,
int64_t not_before, int64_t not_after);
#ifdef __cplusplus
}
#endif
#endif /* PAZE_X509_H */