187 行
8.2 KiB
C
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 */
|