885 行
38 KiB
C
885 行
38 KiB
C
/* x509.c —— X.509 v3 证书最小解析与签名验证
|
|
*
|
|
* 依赖: asn1 (DER 读写)、rsa、ecdsa、p256、hash、encoding (PEM)。
|
|
*
|
|
* 解析路径:
|
|
* Certificate ::= SEQUENCE { tbsCertificate, signatureAlgorithm, signatureValue }
|
|
* TBSCertificate ::= SEQUENCE {
|
|
* version [0] EXPLICIT INTEGER DEFAULT v1,
|
|
* serialNumber INTEGER,
|
|
* signature AlgorithmIdentifier,
|
|
* issuer Name,
|
|
* validity SEQUENCE { notBefore Time, notAfter Time },
|
|
* subject Name,
|
|
* subjectPublicKeyInfo SubjectPublicKeyInfo,
|
|
* ... [3] extensions OPTIONAL }
|
|
*
|
|
* 验签: 对 tbsCertificate 原始 DER 做哈希,用颁发者公钥验签。
|
|
*
|
|
* 时间: UTCTime (YYMMDDHHMMSSZ) / GeneralizedTime (YYYYMMDDHHMMSSZ),
|
|
* RFC 5280 仅允许 Z 时区。转 Unix 秒用 Howard Hinnant days_from_civil。
|
|
*/
|
|
#include "paze/x509.h"
|
|
#include "paze/p384.h"
|
|
#include "paze/asn1.h"
|
|
#include "paze/encoding.h"
|
|
#include "paze/hash.h"
|
|
#include "paze/mem.h"
|
|
#include "paze/random.h"
|
|
#include "paze/error.h"
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
/* ============================================================
|
|
* OID 常量(DER 中 OID 元素的 body,即去掉 06 LL 后的内容)
|
|
* ============================================================ */
|
|
static const uint8_t OID_RSA[] = {0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01};
|
|
static const uint8_t OID_SHA1_RSA[] = {0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05};
|
|
static const uint8_t OID_SHA256_RSA[] = {0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B};
|
|
static const uint8_t OID_SHA384_RSA[] = {0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0C};
|
|
static const uint8_t OID_SHA512_RSA[] = {0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0D};
|
|
static const uint8_t OID_EC_PUBKEY[] = {0x2A,0x86,0x48,0xCE,0x3D,0x02,0x01};
|
|
static const uint8_t OID_ECDSA_SHA256[]= {0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x02};
|
|
static const uint8_t OID_ECDSA_SHA384[]= {0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x03};
|
|
static const uint8_t OID_P256[] = {0x2A,0x86,0x48,0xCE,0x3D,0x03,0x01,0x07};
|
|
static const uint8_t OID_P384[] = {0x2B,0x81,0x04,0x00,0x22};
|
|
static const uint8_t OID_SAN[] = {0x55,0x1D,0x11};
|
|
|
|
static int oid_eq(const uint8_t *a, size_t alen, const uint8_t *b, size_t blen) {
|
|
return alen == blen && (alen == 0 || memcmp(a, b, alen) == 0);
|
|
}
|
|
|
|
static paze_x509_sig_alg_t sig_alg_from_oid(const uint8_t *o, size_t n) {
|
|
if (oid_eq(o,n,OID_SHA1_RSA,9)) return PAZE_X509_SIG_SHA1_RSA;
|
|
if (oid_eq(o,n,OID_SHA256_RSA,9)) return PAZE_X509_SIG_SHA256_RSA;
|
|
if (oid_eq(o,n,OID_SHA384_RSA,9)) return PAZE_X509_SIG_SHA384_RSA;
|
|
if (oid_eq(o,n,OID_SHA512_RSA,9)) return PAZE_X509_SIG_SHA512_RSA;
|
|
if (oid_eq(o,n,OID_ECDSA_SHA256,8))return PAZE_X509_SIG_ECDSA_SHA256;
|
|
if (oid_eq(o,n,OID_ECDSA_SHA384,8))return PAZE_X509_SIG_ECDSA_SHA384;
|
|
return PAZE_X509_SIG_UNKNOWN;
|
|
}
|
|
|
|
/* ============================================================
|
|
* 时间: civil_to_unix (Howard Hinnant) + UTCTime/GeneralizedTime 解析
|
|
* ============================================================ */
|
|
static int64_t civil_to_unix(int y, unsigned m, unsigned d,
|
|
unsigned h, unsigned mi, unsigned s) {
|
|
y -= (m <= 2);
|
|
int64_t era = (y >= 0 ? y : y - 399) / 400;
|
|
unsigned yoe = (unsigned)(y - era * 400);
|
|
unsigned doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1;
|
|
unsigned doe = yoe * 365 + yoe/4 - yoe/100 + doy;
|
|
int64_t days = era * 146097 + (int64_t)doe - 719468;
|
|
return days * 86400 + (int64_t)h * 3600 + (int64_t)mi * 60 + s;
|
|
}
|
|
|
|
static int digit(char c, int *v) {
|
|
if (c < '0' || c > '9') return -1;
|
|
*v = c - '0';
|
|
return 0;
|
|
}
|
|
static int rd2(const char **pp) {
|
|
int a, b;
|
|
if (digit((*pp)[0], &a) || digit((*pp)[1], &b)) return -1;
|
|
*pp += 2;
|
|
return a * 10 + b;
|
|
}
|
|
|
|
/* tag: 0x17=UTCTime, 0x18=GeneralizedTime。成功返回 PAZE_OK */
|
|
static paze_status_t parse_time(const uint8_t *body, size_t blen, uint8_t tag, int64_t *out) {
|
|
if (blen == 0) return PAZE_ERR_FORMAT;
|
|
const char *p = (const char *)body;
|
|
size_t n = blen;
|
|
if (p[n-1] != 'Z') return PAZE_ERR_FORMAT; /* RFC 5280 仅允许 Z */
|
|
n--; /* 去掉 Z */
|
|
int y, mo, d, h, mi, s = 0;
|
|
if (tag == 0x17) { /* UTCTime: YYMMDDHHMM(SS)Z */
|
|
if (n != 10 && n != 12) return PAZE_ERR_FORMAT;
|
|
y = rd2(&p);
|
|
if (y < 0) return PAZE_ERR_FORMAT;
|
|
y = (y < 50) ? 2000 + y : 1900 + y;
|
|
} else if (tag == 0x18) { /* GeneralizedTime: YYYYMMDDHHMM(SS)Z */
|
|
if (n != 12 && n != 14) return PAZE_ERR_FORMAT;
|
|
{
|
|
int a,b,c,dd;
|
|
if (digit(p[0],&a)||digit(p[1],&b)||digit(p[2],&c)||digit(p[3],&dd))
|
|
return PAZE_ERR_FORMAT;
|
|
y = a*1000 + b*100 + c*10 + dd;
|
|
p += 4;
|
|
}
|
|
} else return PAZE_ERR_FORMAT;
|
|
mo = rd2(&p); d = rd2(&p); h = rd2(&p); mi = rd2(&p);
|
|
if (mo < 0 || d < 0 || h < 0 || mi < 0) return PAZE_ERR_FORMAT;
|
|
if (n == 12 || n == 14) { s = rd2(&p); if (s < 0) return PAZE_ERR_FORMAT; }
|
|
if (mo < 1 || mo > 12 || d < 1 || d > 31 || h > 23 || mi > 59 || s > 60)
|
|
return PAZE_ERR_FORMAT;
|
|
*out = civil_to_unix(y, (unsigned)mo, (unsigned)d, (unsigned)h, (unsigned)mi, (unsigned)s);
|
|
return PAZE_OK;
|
|
}
|
|
|
|
/* ============================================================
|
|
* DER 读取辅助
|
|
* ============================================================ */
|
|
|
|
/* 读取下一 TLV。body/blen 给内容;start/tlv_len(可 NULL)给完整 TLV 范围。 */
|
|
static paze_status_t read_tlv(paze_asn1_reader_t *r, uint8_t *tag,
|
|
const uint8_t **body, size_t *blen,
|
|
const uint8_t **start, size_t *tlv_len) {
|
|
const uint8_t *s = r->p;
|
|
paze_status_t st = paze_asn1_read_tl(r, tag, body, blen);
|
|
if (st != PAZE_OK) return st;
|
|
if (start) *start = s;
|
|
if (tlv_len) *tlv_len = (size_t)(r->p - s);
|
|
return PAZE_OK;
|
|
}
|
|
|
|
/* 解析 AlgorithmIdentifier SEQUENCE { OID, params? }。
|
|
* oid_body/oid_len 给 OID 内容。param_start/param_len(可 NULL)给参数 TLV 范围。 */
|
|
static paze_status_t parse_alg_id(paze_asn1_reader_t *r,
|
|
const uint8_t **oid_body, size_t *oid_len,
|
|
const uint8_t **param_start, size_t *param_len) {
|
|
paze_asn1_reader_t alg;
|
|
if (paze_asn1_enter_seq(r, &alg) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
uint8_t tag; const uint8_t *body; size_t blen;
|
|
if (paze_asn1_read_tl(&alg, &tag, &body, &blen) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
if (tag != ASN1_OID) return PAZE_ERR_FORMAT;
|
|
*oid_body = body; *oid_len = blen;
|
|
if (param_start) {
|
|
if (paze_asn1_at_end(&alg)) { *param_start = NULL; *param_len = 0; }
|
|
else {
|
|
const uint8_t *s = alg.p;
|
|
if (paze_asn1_read_tl(&alg, &tag, &body, &blen) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
*param_start = s; *param_len = (size_t)(alg.p - s);
|
|
}
|
|
}
|
|
return PAZE_OK;
|
|
}
|
|
|
|
/* ============================================================
|
|
* SubjectPublicKeyInfo 解析
|
|
* ============================================================ */
|
|
static paze_status_t parse_spki(paze_asn1_reader_t *r, paze_x509_cert_t *cert) {
|
|
paze_asn1_reader_t spki;
|
|
if (paze_asn1_enter_seq(r, &spki) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
|
|
const uint8_t *oid; size_t olen;
|
|
const uint8_t *param; size_t plen;
|
|
if (parse_alg_id(&spki, &oid, &olen, ¶m, &plen) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
|
|
uint8_t tag; const uint8_t *body; size_t blen;
|
|
if (paze_asn1_read_tl(&spki, &tag, &body, &blen) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
if (tag != ASN1_BITSTRING || blen < 1 || body[0] != 0) return PAZE_ERR_FORMAT;
|
|
const uint8_t *pk = body + 1;
|
|
size_t pklen = blen - 1;
|
|
|
|
if (oid_eq(oid, olen, OID_RSA, sizeof(OID_RSA))) {
|
|
cert->pk_kind = PAZE_X509_PK_RSA;
|
|
return paze_rsa_pub_from_der(&cert->rsa_pub, pk, pklen); /* RSAPublicKey */
|
|
}
|
|
if (oid_eq(oid, olen, OID_EC_PUBKEY, sizeof(OID_EC_PUBKEY))) {
|
|
/* 参数须为 prime256v1 / secp384r1 OID */
|
|
if (plen == 0) return PAZE_ERR_FORMAT;
|
|
paze_asn1_reader_t pr; paze_asn1_r_init(&pr, param, plen);
|
|
uint8_t t2; const uint8_t *b2; size_t l2;
|
|
if (paze_asn1_read_tl(&pr, &t2, &b2, &l2) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
if (t2 != ASN1_OID) return PAZE_ERR_FORMAT;
|
|
if (oid_eq(b2, l2, OID_P256, sizeof(OID_P256))) {
|
|
if (pklen != 65 || pk[0] != 0x04) return PAZE_ERR_FORMAT;
|
|
cert->pk_kind = PAZE_X509_PK_ECDSA_P256;
|
|
return paze_p256_from_affine(&cert->ecdsa_pub.Q, pk, 65);
|
|
}
|
|
if (oid_eq(b2, l2, OID_P384, sizeof(OID_P384))) {
|
|
if (pklen != 97 || pk[0] != 0x04) return PAZE_ERR_FORMAT;
|
|
paze_p384_point_t Q;
|
|
paze_status_t st = paze_p384_from_affine(&Q, pk, 97);
|
|
if (st != PAZE_OK) return st; /* 校验在曲线上 */
|
|
cert->pk_kind = PAZE_X509_PK_ECDSA_P384;
|
|
memcpy(cert->ecdsa_p384_pub, pk, 97);
|
|
return PAZE_OK;
|
|
}
|
|
return PAZE_ERR_NOTSUP; /* 未知 EC 曲线 */
|
|
}
|
|
return PAZE_ERR_NOTSUP;
|
|
}
|
|
|
|
/* ============================================================
|
|
* SAN dNSName 提取
|
|
* ============================================================ */
|
|
static paze_status_t parse_san(const uint8_t *data, size_t len, paze_x509_cert_t *cert) {
|
|
paze_asn1_reader_t san, seq;
|
|
paze_asn1_r_init(&san, data, len);
|
|
if (paze_asn1_enter_seq(&san, &seq) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
while (!paze_asn1_at_end(&seq) && cert->san_dns_count < PAZE_X509_SAN_MAX) {
|
|
uint8_t tag; const uint8_t *body; size_t blen;
|
|
if (paze_asn1_read_tl(&seq, &tag, &body, &blen) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
if (tag == 0x82) { /* [2] IMPLICIT IA5String dNSName */
|
|
if (blen > PAZE_X509_DNS_MAX) continue;
|
|
memcpy(cert->san_dns[cert->san_dns_count], body, blen);
|
|
cert->san_dns[cert->san_dns_count][blen] = '\0';
|
|
cert->san_dns_count++;
|
|
}
|
|
}
|
|
return PAZE_OK;
|
|
}
|
|
|
|
/* Extension ::= SEQUENCE { extnID OID, critical BOOLEAN DEFAULT FALSE, extnValue OCTET STRING } */
|
|
static paze_status_t parse_extension(paze_asn1_reader_t *r, paze_x509_cert_t *cert) {
|
|
paze_asn1_reader_t ext;
|
|
if (paze_asn1_enter_seq(r, &ext) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
uint8_t tag; const uint8_t *body; size_t blen;
|
|
if (paze_asn1_read_tl(&ext, &tag, &body, &blen) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
if (tag != ASN1_OID) return PAZE_ERR_FORMAT;
|
|
const uint8_t *oid = body; size_t olen = blen;
|
|
/* 可选 critical */
|
|
if (paze_asn1_read_tl(&ext, &tag, &body, &blen) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
if (tag == ASN1_BOOLEAN) {
|
|
if (paze_asn1_read_tl(&ext, &tag, &body, &blen) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
}
|
|
if (tag != ASN1_OCTETSTRING) return PAZE_ERR_FORMAT;
|
|
if (oid_eq(oid, olen, OID_SAN, sizeof(OID_SAN))) {
|
|
parse_san(body, blen, cert); /* 失败则忽略 SAN,不致命 */
|
|
}
|
|
return PAZE_OK;
|
|
}
|
|
|
|
/* ============================================================
|
|
* TBSCertificate 解析
|
|
* ============================================================ */
|
|
static paze_status_t parse_tbs_inner(paze_asn1_reader_t *tbs, paze_x509_cert_t *cert) {
|
|
uint8_t tag; const uint8_t *body; size_t blen;
|
|
|
|
/* version [0] EXPLICIT,可选 */
|
|
if (read_tlv(tbs, &tag, &body, &blen, NULL, NULL) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
if (tag == ASN1_CTX0) {
|
|
paze_asn1_reader_t vr; paze_asn1_r_init(&vr, body, blen);
|
|
uint8_t t2; const uint8_t *b2; size_t l2;
|
|
if (paze_asn1_read_tl(&vr, &t2, &b2, &l2) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
if (t2 != ASN1_INTEGER || l2 != 1) return PAZE_ERR_FORMAT;
|
|
cert->version = b2[0];
|
|
if (read_tlv(tbs, &tag, &body, &blen, NULL, NULL) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
} else {
|
|
cert->version = 0; /* v1 默认 */
|
|
}
|
|
|
|
/* serialNumber */
|
|
if (tag != ASN1_INTEGER) return PAZE_ERR_FORMAT;
|
|
{
|
|
size_t skip = 0;
|
|
while (skip + 1 < blen && body[skip] == 0x00 && (body[skip+1] & 0x80) == 0) skip++;
|
|
size_t n = blen - skip;
|
|
if (n > sizeof(cert->serial)) return PAZE_ERR_OVERFLOW;
|
|
memcpy(cert->serial, body + skip, n);
|
|
cert->serial_len = n;
|
|
}
|
|
|
|
/* signature AlgorithmIdentifier */
|
|
{
|
|
const uint8_t *oid; size_t olen;
|
|
if (parse_alg_id(tbs, &oid, &olen, NULL, NULL) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
cert->sig_alg = sig_alg_from_oid(oid, olen);
|
|
}
|
|
|
|
/* issuer Name */
|
|
{
|
|
const uint8_t *s; size_t L;
|
|
if (read_tlv(tbs, &tag, &body, &blen, &s, &L) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
if (tag != ASN1_SEQUENCE) return PAZE_ERR_FORMAT;
|
|
cert->issuer_dn = s; cert->issuer_dn_len = L;
|
|
}
|
|
|
|
/* validity */
|
|
{
|
|
paze_asn1_reader_t val;
|
|
if (paze_asn1_enter_seq(tbs, &val) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
if (paze_asn1_read_tl(&val, &tag, &body, &blen) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
if (parse_time(body, blen, tag, &cert->not_before) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
if (paze_asn1_read_tl(&val, &tag, &body, &blen) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
if (parse_time(body, blen, tag, &cert->not_after) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
}
|
|
|
|
/* subject Name */
|
|
{
|
|
const uint8_t *s; size_t L;
|
|
if (read_tlv(tbs, &tag, &body, &blen, &s, &L) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
if (tag != ASN1_SEQUENCE) return PAZE_ERR_FORMAT;
|
|
cert->subject_dn = s; cert->subject_dn_len = L;
|
|
}
|
|
|
|
/* subjectPublicKeyInfo */
|
|
if (parse_spki(tbs, cert) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
|
|
/* 可选: issuerUniqueID[1], subjectUniqueID[2], extensions[3] */
|
|
while (!paze_asn1_at_end(tbs)) {
|
|
if (read_tlv(tbs, &tag, &body, &blen, NULL, NULL) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
if (tag == ASN1_CTX3) { /* [3] EXPLICIT extensions */
|
|
/* body = SEQUENCE OF Extension 的完整 TLV */
|
|
paze_asn1_reader_t exts, extseq;
|
|
paze_asn1_r_init(&exts, body, blen);
|
|
if (paze_asn1_enter_seq(&exts, &extseq) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
while (!paze_asn1_at_end(&extseq)) {
|
|
parse_extension(&extseq, cert); /* 单个扩展失败则忽略 */
|
|
}
|
|
}
|
|
/* [1]/[2] uniqueID 等其它可选字段忽略 */
|
|
}
|
|
return PAZE_OK;
|
|
}
|
|
|
|
/* ============================================================
|
|
* 顶层 Certificate 解析
|
|
* ============================================================ */
|
|
paze_status_t paze_x509_parse_der(paze_x509_cert_t *cert, const uint8_t *der, size_t len) {
|
|
if (!cert || !der) return PAZE_ERR_INVALID;
|
|
memset(cert, 0, sizeof(*cert));
|
|
|
|
paze_asn1_reader_t r, top;
|
|
paze_asn1_r_init(&r, der, len);
|
|
if (paze_asn1_enter_seq(&r, &top) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
|
|
/* tbsCertificate:记录完整 TLV 范围用于验签 */
|
|
const uint8_t *tbs_start = top.p;
|
|
uint8_t tag; const uint8_t *body; size_t blen;
|
|
if (paze_asn1_read_tl(&top, &tag, &body, &blen) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
if (tag != ASN1_SEQUENCE) return PAZE_ERR_FORMAT;
|
|
cert->tbs_der = tbs_start;
|
|
cert->tbs_der_len = (size_t)(top.p - tbs_start);
|
|
|
|
/* 解析 tbs 内容 */
|
|
paze_asn1_reader_t tbs_r;
|
|
paze_asn1_r_init(&tbs_r, body, blen);
|
|
paze_status_t st = parse_tbs_inner(&tbs_r, cert);
|
|
if (st != PAZE_OK) return st;
|
|
|
|
/* signatureAlgorithm(应与 tbs 内一致,以这里为准重识别) */
|
|
{
|
|
const uint8_t *oid; size_t olen;
|
|
if (parse_alg_id(&top, &oid, &olen, NULL, NULL) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
paze_x509_sig_alg_t outer = sig_alg_from_oid(oid, olen);
|
|
if (outer != PAZE_X509_SIG_UNKNOWN) cert->sig_alg = outer;
|
|
}
|
|
|
|
/* signatureValue BIT STRING */
|
|
if (paze_asn1_read_tl(&top, &tag, &body, &blen) != PAZE_OK) return PAZE_ERR_FORMAT;
|
|
if (tag != ASN1_BITSTRING || blen < 1 || body[0] != 0) return PAZE_ERR_FORMAT;
|
|
cert->signature = body + 1;
|
|
cert->signature_len = blen - 1;
|
|
|
|
return PAZE_OK;
|
|
}
|
|
|
|
paze_status_t paze_x509_load_pem(paze_x509_cert_t *cert, const char *pem_text) {
|
|
if (!cert || !pem_text) return PAZE_ERR_INVALID;
|
|
memset(cert, 0, sizeof(*cert));
|
|
size_t dlen = 0;
|
|
uint8_t *der = paze_pem_decode(pem_text, "CERTIFICATE", &dlen);
|
|
if (!der) return PAZE_ERR_FORMAT;
|
|
paze_status_t st = paze_x509_parse_der(cert, der, dlen);
|
|
if (st != PAZE_OK) { free(der); return st; }
|
|
cert->owned_der = der;
|
|
return PAZE_OK;
|
|
}
|
|
|
|
void paze_x509_free(paze_x509_cert_t *cert) {
|
|
if (!cert) return;
|
|
if (cert->owned_der) { free(cert->owned_der); cert->owned_der = NULL; }
|
|
}
|
|
|
|
/* ============================================================
|
|
* 验签
|
|
* ============================================================ */
|
|
paze_status_t paze_x509_verify_signature(const paze_x509_cert_t *cert,
|
|
const paze_x509_cert_t *issuer) {
|
|
if (!cert || !issuer) return PAZE_ERR_INVALID;
|
|
uint8_t hash[64];
|
|
size_t hlen = 0;
|
|
paze_rsa_hash_t rsa_alg = PAZE_RSA_HASH_SHA256;
|
|
|
|
switch (cert->sig_alg) {
|
|
case PAZE_X509_SIG_SHA1_RSA:
|
|
rsa_alg = PAZE_RSA_HASH_SHA1; hlen = 20;
|
|
if (paze_sha1(cert->tbs_der, cert->tbs_der_len, hash) != PAZE_OK) return PAZE_ERR;
|
|
break;
|
|
case PAZE_X509_SIG_SHA256_RSA:
|
|
rsa_alg = PAZE_RSA_HASH_SHA256; hlen = 32;
|
|
if (paze_sha256(cert->tbs_der, cert->tbs_der_len, hash) != PAZE_OK) return PAZE_ERR;
|
|
break;
|
|
case PAZE_X509_SIG_SHA384_RSA:
|
|
rsa_alg = PAZE_RSA_HASH_SHA384; hlen = 48;
|
|
if (paze_sha384(cert->tbs_der, cert->tbs_der_len, hash) != PAZE_OK) return PAZE_ERR;
|
|
break;
|
|
case PAZE_X509_SIG_SHA512_RSA:
|
|
rsa_alg = PAZE_RSA_HASH_SHA512; hlen = 64;
|
|
if (paze_sha512(cert->tbs_der, cert->tbs_der_len, hash) != PAZE_OK) return PAZE_ERR;
|
|
break;
|
|
case PAZE_X509_SIG_ECDSA_SHA256:
|
|
if (paze_sha256(cert->tbs_der, cert->tbs_der_len, hash) != PAZE_OK) return PAZE_ERR;
|
|
if (issuer->pk_kind == PAZE_X509_PK_ECDSA_P256)
|
|
return paze_ecdsa_verify(&issuer->ecdsa_pub, PAZE_RSA_HASH_SHA256,
|
|
hash, 32, cert->signature, cert->signature_len);
|
|
if (issuer->pk_kind == PAZE_X509_PK_ECDSA_P384)
|
|
return paze_ecdsa_p384_verify(issuer->ecdsa_p384_pub, PAZE_RSA_HASH_SHA256,
|
|
hash, 32, cert->signature, cert->signature_len);
|
|
return PAZE_ERR_VERIFY;
|
|
case PAZE_X509_SIG_ECDSA_SHA384:
|
|
if (paze_sha384(cert->tbs_der, cert->tbs_der_len, hash) != PAZE_OK) return PAZE_ERR;
|
|
if (issuer->pk_kind == PAZE_X509_PK_ECDSA_P384)
|
|
return paze_ecdsa_p384_verify(issuer->ecdsa_p384_pub, PAZE_RSA_HASH_SHA384,
|
|
hash, 48, cert->signature, cert->signature_len);
|
|
if (issuer->pk_kind == PAZE_X509_PK_ECDSA_P256)
|
|
return paze_ecdsa_verify(&issuer->ecdsa_pub, PAZE_RSA_HASH_SHA384,
|
|
hash, 48, cert->signature, cert->signature_len);
|
|
return PAZE_ERR_VERIFY;
|
|
default:
|
|
return PAZE_ERR_NOTSUP;
|
|
}
|
|
|
|
if (issuer->pk_kind != PAZE_X509_PK_RSA) return PAZE_ERR_VERIFY;
|
|
return paze_rsa_pkcs1_verify(&issuer->rsa_pub, rsa_alg, hash, hlen,
|
|
cert->signature, cert->signature_len);
|
|
}
|
|
|
|
int paze_x509_is_self_signed(const paze_x509_cert_t *cert) {
|
|
if (!cert) return 0;
|
|
return cert->issuer_dn_len > 0 &&
|
|
cert->issuer_dn_len == cert->subject_dn_len &&
|
|
memcmp(cert->issuer_dn, cert->subject_dn, cert->issuer_dn_len) == 0;
|
|
}
|
|
|
|
paze_status_t paze_x509_check_validity(const paze_x509_cert_t *cert, int64_t now) {
|
|
if (!cert) return PAZE_ERR_INVALID;
|
|
if (now < cert->not_before || now > cert->not_after) return PAZE_ERR_VERIFY;
|
|
return PAZE_OK;
|
|
}
|
|
|
|
/* ============================================================
|
|
* 主机名匹配
|
|
* ============================================================ */
|
|
static int ci_eq(const char *a, const char *b) {
|
|
while (*a && *b) {
|
|
unsigned char ca = (unsigned char)*a, cb = (unsigned char)*b;
|
|
if (ca >= 'A' && ca <= 'Z') ca = (unsigned char)(ca + 32);
|
|
if (cb >= 'A' && cb <= 'Z') cb = (unsigned char)(cb + 32);
|
|
if (ca != cb) return 0;
|
|
a++; b++;
|
|
}
|
|
return *a == 0 && *b == 0;
|
|
}
|
|
|
|
static int dns_match(const char *pat, const char *host) {
|
|
if (pat[0] == '*' && pat[1] == '.') { /* 单层左通配符 */
|
|
const char *dot = host;
|
|
while (*dot && *dot != '.') dot++;
|
|
if (*dot != '.') return 0;
|
|
return ci_eq(dot + 1, pat + 2);
|
|
}
|
|
return ci_eq(pat, host);
|
|
}
|
|
|
|
int paze_x509_match_host(const paze_x509_cert_t *cert, const char *host) {
|
|
if (!cert || !host) return 0;
|
|
for (int i = 0; i < cert->san_dns_count; i++) {
|
|
if (dns_match(cert->san_dns[i], host)) return 1;
|
|
}
|
|
return 0; /* 仅支持 SAN dNSName;无 SAN 则不匹配 */
|
|
}
|
|
|
|
/* ============================================================
|
|
* 证书链验证
|
|
* ============================================================ */
|
|
static int dn_eq(const uint8_t *a, size_t alen, const uint8_t *b, size_t blen) {
|
|
return alen == blen && (alen == 0 || memcmp(a, b, alen) == 0);
|
|
}
|
|
|
|
static int pubkey_eq(const paze_x509_cert_t *a, const paze_x509_cert_t *b) {
|
|
if (a->pk_kind != b->pk_kind) return 0;
|
|
if (a->pk_kind == PAZE_X509_PK_RSA)
|
|
return paze_bn_cmp(&a->rsa_pub.n, &b->rsa_pub.n) == 0 &&
|
|
paze_bn_cmp(&a->rsa_pub.e, &b->rsa_pub.e) == 0;
|
|
if (a->pk_kind == PAZE_X509_PK_ECDSA_P256) {
|
|
uint8_t ab[65], bb[65];
|
|
if (paze_p256_to_affine(&a->ecdsa_pub.Q, ab, 65) != PAZE_OK) return 0;
|
|
if (paze_p256_to_affine(&b->ecdsa_pub.Q, bb, 65) != PAZE_OK) return 0;
|
|
return paze_ct_equal(ab, bb, 65) == 0; /* ct_equal:0=相等 */
|
|
}
|
|
if (a->pk_kind == PAZE_X509_PK_ECDSA_P384) {
|
|
return paze_ct_equal(a->ecdsa_p384_pub, b->ecdsa_p384_pub, 97) == 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int cert_in_anchors(const paze_x509_cert_t *c,
|
|
const paze_x509_cert_t *anchors, int n) {
|
|
for (int i = 0; i < n; i++) {
|
|
if (dn_eq(c->subject_dn, c->subject_dn_len,
|
|
anchors[i].subject_dn, anchors[i].subject_dn_len) &&
|
|
pubkey_eq(c, &anchors[i]))
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
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) {
|
|
if (!chain || chain_len < 1) return PAZE_ERR_INVALID;
|
|
if (!anchors && anchor_count > 0) return PAZE_ERR_INVALID;
|
|
|
|
/* 有效期检查:链中所有证书 */
|
|
for (int i = 0; i < chain_len; i++) {
|
|
if (paze_x509_check_validity(&chain[i], now) != PAZE_OK) return PAZE_ERR_VERIFY;
|
|
}
|
|
|
|
for (int i = 0; i < chain_len; i++) {
|
|
const paze_x509_cert_t *c = &chain[i];
|
|
const paze_x509_cert_t *issuer = NULL;
|
|
int issuer_is_anchor = 0;
|
|
|
|
if (paze_x509_is_self_signed(c)) {
|
|
/* 自签名:验证自身签名,且须在信任锚中 */
|
|
if (paze_x509_verify_signature(c, c) != PAZE_OK) return PAZE_ERR_VERIFY;
|
|
return cert_in_anchors(c, anchors, anchor_count) ? PAZE_OK : PAZE_ERR_VERIFY;
|
|
}
|
|
|
|
/* 在链后续证书中找 issuer */
|
|
for (int j = i + 1; j < chain_len; j++) {
|
|
if (dn_eq(chain[j].subject_dn, chain[j].subject_dn_len,
|
|
c->issuer_dn, c->issuer_dn_len)) {
|
|
issuer = &chain[j];
|
|
break;
|
|
}
|
|
}
|
|
/* 在信任锚中找 issuer */
|
|
if (!issuer && anchors) {
|
|
for (int a = 0; a < anchor_count; a++) {
|
|
if (dn_eq(anchors[a].subject_dn, anchors[a].subject_dn_len,
|
|
c->issuer_dn, c->issuer_dn_len)) {
|
|
issuer = &anchors[a];
|
|
issuer_is_anchor = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!issuer) return PAZE_ERR_VERIFY;
|
|
if (paze_x509_verify_signature(c, issuer) != PAZE_OK) return PAZE_ERR_VERIFY;
|
|
if (issuer_is_anchor) return PAZE_OK; /* 已链到信任锚 */
|
|
}
|
|
/* 走到链末尾仍未达信任锚 */
|
|
return PAZE_ERR_VERIFY;
|
|
}
|
|
|
|
/* ============================================================
|
|
* 证书生成(最小集)
|
|
* ============================================================ */
|
|
|
|
static paze_status_t w_seq(paze_asn1_writer_t *w, const uint8_t *body, size_t n) {
|
|
paze_status_t st = paze_asn1_w_tag_len(w, ASN1_SEQUENCE, n);
|
|
if (st != PAZE_OK) return st;
|
|
return paze_asn1_w_data(w, body, n);
|
|
}
|
|
|
|
static paze_status_t w_alg_id(paze_asn1_writer_t *w, const uint8_t *oid, size_t olen,
|
|
int with_null) {
|
|
paze_asn1_writer_t inner; paze_asn1_w_init(&inner, NULL, 0);
|
|
paze_status_t st = paze_asn1_w_oid(&inner, oid, olen);
|
|
if (st == PAZE_OK && with_null) st = paze_asn1_w_null(&inner);
|
|
if (st == PAZE_OK) st = w_seq(w, inner.buf, inner.len);
|
|
paze_asn1_w_free(&inner);
|
|
return st;
|
|
}
|
|
|
|
/* Unix 秒 -> civil(Howard Hinnant civil_from_days) */
|
|
static void unix_to_civil(int64_t t, int *yy, unsigned *mm, unsigned *dd,
|
|
unsigned *hh, unsigned *mi, unsigned *ss) {
|
|
int64_t days = t / 86400;
|
|
int64_t rem = t % 86400;
|
|
if (rem < 0) { days -= 1; rem += 86400; }
|
|
*hh = (unsigned)(rem / 3600);
|
|
*mi = (unsigned)((rem % 3600) / 60);
|
|
*ss = (unsigned)(rem % 60);
|
|
days += 719468;
|
|
int64_t era = (days >= 0 ? days : days - 146096) / 146097;
|
|
unsigned doe = (unsigned)(days - era * 146097);
|
|
unsigned yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365;
|
|
int y = (int)(yoe + (unsigned)era * 400);
|
|
unsigned doy = doe - (365*yoe + yoe/4 - yoe/100);
|
|
unsigned mp = (5*doy + 2) / 153;
|
|
*dd = doy - (153*mp + 2)/5 + 1;
|
|
*mm = mp + (mp < 10 ? 3 : -9);
|
|
if (*mm <= 2) y += 1;
|
|
*yy = y;
|
|
}
|
|
|
|
/* 编码证书时间(RFC 5280 §4.1.2.5):2050 年前用 UTCTime(YYMMDDHHMMSSZ),
|
|
* 超出范围回退 GeneralizedTime(YYYYMMDDHHMMSSZ)。out 需 32 字节。 */
|
|
static void time_to_cert_time(int64_t t, char out[32]) {
|
|
int y; unsigned mo, d, h, mi, s;
|
|
unix_to_civil(t, &y, &mo, &d, &h, &mi, &s);
|
|
if (y < 1950 || y >= 2050)
|
|
snprintf(out, 32, "G%04d%02u%02u%02u%02u%02uZ", y, mo, d, h, mi, s);
|
|
else
|
|
snprintf(out, 32, "%02d%02u%02u%02u%02u%02uZ", y % 100, mo, d, h, mi, s);
|
|
}
|
|
|
|
paze_status_t paze_x509_build_dn_cn(uint8_t *out, size_t *outlen, const char *cn) {
|
|
if (!out || !outlen || !cn) return PAZE_ERR_INVALID;
|
|
static const uint8_t cn_oid[] = {0x55, 0x04, 0x03};
|
|
size_t cnlen = strlen(cn);
|
|
paze_asn1_writer_t atv, ent, rdn, name;
|
|
paze_asn1_w_init(&atv, NULL, 0);
|
|
paze_asn1_w_init(&ent, NULL, 0);
|
|
paze_asn1_w_init(&rdn, NULL, 0);
|
|
paze_asn1_w_init(&name, NULL, 0);
|
|
paze_status_t st = paze_asn1_w_oid(&atv, cn_oid, sizeof(cn_oid));
|
|
if (st == PAZE_OK) { st = paze_asn1_w_tag_len(&atv, ASN1_UTF8, cnlen); }
|
|
if (st == PAZE_OK) st = paze_asn1_w_data(&atv, (const uint8_t *)cn, cnlen);
|
|
/* AttributeTypeAndValue = SEQUENCE { atv } (RFC 5280 §4.1.2.4) */
|
|
if (st == PAZE_OK) st = w_seq(&ent, atv.buf, atv.len);
|
|
/* RDN = SET { ent } */
|
|
if (st == PAZE_OK) { st = paze_asn1_w_tag_len(&rdn, ASN1_SET, ent.len); }
|
|
if (st == PAZE_OK) st = paze_asn1_w_data(&rdn, ent.buf, ent.len);
|
|
/* Name = SEQUENCE { rdn } */
|
|
if (st == PAZE_OK) st = w_seq(&name, rdn.buf, rdn.len);
|
|
if (st == PAZE_OK) {
|
|
if (*outlen < name.len) { *outlen = name.len; st = PAZE_ERR_SHORT; }
|
|
else { memcpy(out, name.buf, name.len); *outlen = name.len; }
|
|
}
|
|
paze_asn1_w_free(&atv);
|
|
paze_asn1_w_free(&ent);
|
|
paze_asn1_w_free(&rdn);
|
|
paze_asn1_w_free(&name);
|
|
return st;
|
|
}
|
|
|
|
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) {
|
|
if (!out || !outlen) return PAZE_ERR_INVALID;
|
|
paze_asn1_writer_t alg, bs, spki;
|
|
paze_asn1_w_init(&alg, NULL, 0);
|
|
paze_asn1_w_init(&bs, NULL, 0);
|
|
paze_asn1_w_init(&spki, NULL, 0);
|
|
paze_status_t st;
|
|
|
|
if (kind == PAZE_X509_PK_RSA) {
|
|
if (!rsa_pub) { st = PAZE_ERR_INVALID; goto done; }
|
|
st = paze_asn1_w_oid(&alg, OID_RSA, sizeof(OID_RSA));
|
|
if (st == PAZE_OK) st = paze_asn1_w_null(&alg);
|
|
if (st == PAZE_OK) st = paze_asn1_w_byte(&bs, 0x00);
|
|
if (st == PAZE_OK) {
|
|
uint8_t rsader[1200];
|
|
size_t rl = sizeof(rsader);
|
|
st = paze_rsa_pub_to_der(rsa_pub, rsader, &rl);
|
|
if (st == PAZE_OK) st = paze_asn1_w_data(&bs, rsader, rl);
|
|
}
|
|
} else if (kind == PAZE_X509_PK_ECDSA_P256) {
|
|
if (!ec_pub) { st = PAZE_ERR_INVALID; goto done; }
|
|
st = paze_asn1_w_oid(&alg, OID_EC_PUBKEY, sizeof(OID_EC_PUBKEY));
|
|
if (st == PAZE_OK) st = paze_asn1_w_oid(&alg, OID_P256, sizeof(OID_P256));
|
|
if (st == PAZE_OK) st = paze_asn1_w_byte(&bs, 0x00);
|
|
if (st == PAZE_OK) {
|
|
uint8_t pt[65];
|
|
st = paze_p256_to_affine(&ec_pub->Q, pt, 65);
|
|
if (st == PAZE_OK) st = paze_asn1_w_data(&bs, pt, 65);
|
|
}
|
|
} else { st = PAZE_ERR_INVALID; goto done; }
|
|
|
|
/* 将 AlgorithmIdentifier + BIT STRING 包裹在 SPKI 外层 SEQUENCE 中 */
|
|
{
|
|
paze_asn1_writer_t inner;
|
|
paze_asn1_w_init(&inner, NULL, 0);
|
|
if (st == PAZE_OK) st = w_seq(&inner, alg.buf, alg.len);
|
|
if (st == PAZE_OK) st = paze_asn1_w_tag_len(&inner, ASN1_BITSTRING, bs.len);
|
|
if (st == PAZE_OK) st = paze_asn1_w_data(&inner, bs.buf, bs.len);
|
|
if (st == PAZE_OK) st = w_seq(&spki, inner.buf, inner.len);
|
|
paze_asn1_w_free(&inner);
|
|
}
|
|
if (st == PAZE_OK) {
|
|
if (*outlen < spki.len) { *outlen = spki.len; st = PAZE_ERR_SHORT; }
|
|
else { memcpy(out, spki.buf, spki.len); *outlen = spki.len; }
|
|
}
|
|
done:
|
|
paze_asn1_w_free(&alg);
|
|
paze_asn1_w_free(&bs);
|
|
paze_asn1_w_free(&spki);
|
|
return st;
|
|
}
|
|
|
|
/* 构造 secp384r1 的 SubjectPublicKeyInfo DER */
|
|
paze_status_t paze_x509_build_spki384(uint8_t *out, size_t *outlen,
|
|
const uint8_t pub_affine[97]) {
|
|
if (!out || !outlen || !pub_affine) return PAZE_ERR_INVALID;
|
|
paze_asn1_writer_t alg, bs, spki;
|
|
paze_asn1_w_init(&alg, NULL, 0);
|
|
paze_asn1_w_init(&bs, NULL, 0);
|
|
paze_asn1_w_init(&spki, NULL, 0);
|
|
paze_status_t st;
|
|
st = paze_asn1_w_oid(&alg, OID_EC_PUBKEY, sizeof(OID_EC_PUBKEY));
|
|
if (st == PAZE_OK) st = paze_asn1_w_oid(&alg, OID_P384, sizeof(OID_P384));
|
|
if (st == PAZE_OK) st = paze_asn1_w_byte(&bs, 0x00);
|
|
if (st == PAZE_OK) st = paze_asn1_w_data(&bs, pub_affine, 97);
|
|
{
|
|
paze_asn1_writer_t inner;
|
|
paze_asn1_w_init(&inner, NULL, 0);
|
|
if (st == PAZE_OK) st = w_seq(&inner, alg.buf, alg.len);
|
|
if (st == PAZE_OK) st = paze_asn1_w_tag_len(&inner, ASN1_BITSTRING, bs.len);
|
|
if (st == PAZE_OK) st = paze_asn1_w_data(&inner, bs.buf, bs.len);
|
|
if (st == PAZE_OK) st = w_seq(&spki, inner.buf, inner.len);
|
|
paze_asn1_w_free(&inner);
|
|
}
|
|
if (st == PAZE_OK) {
|
|
if (*outlen < spki.len) { *outlen = spki.len; st = PAZE_ERR_SHORT; }
|
|
else { memcpy(out, spki.buf, spki.len); *outlen = spki.len; }
|
|
}
|
|
paze_asn1_w_free(&alg);
|
|
paze_asn1_w_free(&bs);
|
|
paze_asn1_w_free(&spki);
|
|
return st;
|
|
}
|
|
|
|
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) {
|
|
if (!out || !outlen || !subject_spki || !subject_dn || !issuer_dn ||
|
|
!issuer_priv || (!serial && serial_len))
|
|
return PAZE_ERR_INVALID;
|
|
|
|
const uint8_t *sig_oid; size_t sig_oid_len; int with_null;
|
|
if (issuer_priv->kind == PAZE_X509_PK_RSA) {
|
|
sig_oid = OID_SHA256_RSA; sig_oid_len = sizeof(OID_SHA256_RSA); with_null = 1;
|
|
} else if (issuer_priv->kind == PAZE_X509_PK_ECDSA_P256) {
|
|
sig_oid = OID_ECDSA_SHA256; sig_oid_len = sizeof(OID_ECDSA_SHA256); with_null = 0;
|
|
} else if (issuer_priv->kind == PAZE_X509_PK_ECDSA_P384) {
|
|
sig_oid = OID_ECDSA_SHA384; sig_oid_len = sizeof(OID_ECDSA_SHA384); with_null = 0;
|
|
} else return PAZE_ERR_INVALID;
|
|
|
|
char nb[32], na[32];
|
|
time_to_cert_time(not_before, nb);
|
|
time_to_cert_time(not_after, na);
|
|
|
|
paze_asn1_writer_t tbs; paze_asn1_w_init(&tbs, NULL, 0);
|
|
paze_status_t st = PAZE_OK;
|
|
|
|
/* version [0] EXPLICIT INTEGER(2) */
|
|
{
|
|
paze_asn1_writer_t v; paze_asn1_w_init(&v, NULL, 0);
|
|
if (st == PAZE_OK) st = paze_asn1_w_integer(&v, (const uint8_t *)"\x02", 1);
|
|
if (st == PAZE_OK) st = paze_asn1_w_tag_len(&tbs, ASN1_CTX0, v.len);
|
|
if (st == PAZE_OK) st = paze_asn1_w_data(&tbs, v.buf, v.len);
|
|
paze_asn1_w_free(&v);
|
|
}
|
|
if (st == PAZE_OK) st = paze_asn1_w_integer(&tbs, serial, serial_len);
|
|
if (st == PAZE_OK) st = w_alg_id(&tbs, sig_oid, sig_oid_len, with_null);
|
|
if (st == PAZE_OK) st = paze_asn1_w_data(&tbs, issuer_dn, iss_len);
|
|
/* validity SEQUENCE { Time(nb), Time(na) } */
|
|
{
|
|
paze_asn1_writer_t val; paze_asn1_w_init(&val, NULL, 0);
|
|
/* UTCTime(0x17, 13 字节)或 GeneralizedTime(0x18, 'G'+15 字节) */
|
|
uint8_t nb_tag = (nb[0] == 'G') ? 0x18 : 0x17;
|
|
uint8_t na_tag = (na[0] == 'G') ? 0x18 : 0x17;
|
|
size_t nb_len = (nb_tag == 0x18) ? 16 : 13;
|
|
size_t na_len = (na_tag == 0x18) ? 16 : 13;
|
|
if (st == PAZE_OK) st = paze_asn1_w_tag_len(&val, nb_tag, nb_len);
|
|
if (st == PAZE_OK) st = paze_asn1_w_data(&val, (const uint8_t *)(nb_tag == 0x18 ? nb + 1 : nb), nb_len);
|
|
if (st == PAZE_OK) st = paze_asn1_w_tag_len(&val, na_tag, na_len);
|
|
if (st == PAZE_OK) st = paze_asn1_w_data(&val, (const uint8_t *)(na_tag == 0x18 ? na + 1 : na), na_len);
|
|
if (st == PAZE_OK) st = w_seq(&tbs, val.buf, val.len);
|
|
paze_asn1_w_free(&val);
|
|
}
|
|
if (st == PAZE_OK) st = paze_asn1_w_data(&tbs, subject_dn, subj_len);
|
|
if (st == PAZE_OK) st = paze_asn1_w_data(&tbs, subject_spki, spki_len);
|
|
if (st != PAZE_OK) { paze_asn1_w_free(&tbs); return st; }
|
|
|
|
/* tbs_der = SEQUENCE { tbs body } */
|
|
paze_asn1_writer_t tbsfull; paze_asn1_w_init(&tbsfull, NULL, 0);
|
|
st = w_seq(&tbsfull, tbs.buf, tbs.len);
|
|
paze_asn1_w_free(&tbs);
|
|
if (st != PAZE_OK) { paze_asn1_w_free(&tbsfull); return st; }
|
|
|
|
/* 签名(曲线决定哈希:P-256→SHA-256, P-384→SHA-384) */
|
|
uint8_t sig[4096];
|
|
size_t sig_len = sizeof(sig);
|
|
if (issuer_priv->kind == PAZE_X509_PK_RSA) {
|
|
uint8_t hsh[32];
|
|
paze_sha256(tbsfull.buf, tbsfull.len, hsh);
|
|
sig_len = paze_rsa_modulus_len(&issuer_priv->rsa.n);
|
|
if (sig_len > sizeof(sig)) { paze_asn1_w_free(&tbsfull); return PAZE_ERR_OVERFLOW; }
|
|
st = paze_rsa_pkcs1_sign(&issuer_priv->rsa, PAZE_RSA_HASH_SHA256, hsh, 32, sig, sig_len);
|
|
} else if (issuer_priv->kind == PAZE_X509_PK_ECDSA_P256) {
|
|
uint8_t hsh[32];
|
|
paze_sha256(tbsfull.buf, tbsfull.len, hsh);
|
|
paze_ecdsa_priv_t ek;
|
|
paze_ecdsa_priv_set_d(&ek, issuer_priv->ecdsa_d);
|
|
st = paze_ecdsa_sign(&ek, PAZE_RSA_HASH_SHA256, hsh, 32, sig, &sig_len);
|
|
} else { /* P-384 */
|
|
uint8_t hsh[48];
|
|
paze_sha384(tbsfull.buf, tbsfull.len, hsh);
|
|
st = paze_ecdsa_p384_sign(issuer_priv->ecdsa_d384, PAZE_RSA_HASH_SHA384,
|
|
hsh, 48, sig, &sig_len);
|
|
}
|
|
if (st != PAZE_OK) { paze_asn1_w_free(&tbsfull); return st; }
|
|
|
|
/* Certificate = SEQUENCE { tbsfull, AlgId, BIT STRING(0x00||sig) } */
|
|
paze_asn1_writer_t cert, outw;
|
|
paze_asn1_w_init(&cert, NULL, 0);
|
|
paze_asn1_w_init(&outw, NULL, 0);
|
|
st = paze_asn1_w_data(&cert, tbsfull.buf, tbsfull.len);
|
|
paze_asn1_w_free(&tbsfull);
|
|
if (st == PAZE_OK) st = w_alg_id(&cert, sig_oid, sig_oid_len, with_null);
|
|
if (st == PAZE_OK) st = paze_asn1_w_tag_len(&cert, ASN1_BITSTRING, sig_len + 1);
|
|
if (st == PAZE_OK) st = paze_asn1_w_byte(&cert, 0x00);
|
|
if (st == PAZE_OK) st = paze_asn1_w_data(&cert, sig, sig_len);
|
|
if (st == PAZE_OK) st = w_seq(&outw, cert.buf, cert.len);
|
|
if (st == PAZE_OK) {
|
|
if (*outlen < outw.len) { *outlen = outw.len; st = PAZE_ERR_SHORT; }
|
|
else { memcpy(out, outw.buf, outw.len); *outlen = outw.len; }
|
|
}
|
|
paze_asn1_w_free(&cert);
|
|
paze_asn1_w_free(&outw);
|
|
return st;
|
|
}
|
|
|
|
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) {
|
|
if (!out || !outlen || !priv || !cn) return PAZE_ERR_INVALID;
|
|
uint8_t spki[1024]; size_t sl = sizeof(spki);
|
|
uint8_t dn[256]; size_t dl = sizeof(dn);
|
|
paze_status_t st;
|
|
|
|
if (priv->kind == PAZE_X509_PK_RSA) {
|
|
paze_rsa_pub_t rpub;
|
|
paze_rsa_pub_from_priv(&rpub, &priv->rsa);
|
|
st = paze_x509_build_spki(spki, &sl, PAZE_X509_PK_RSA, &rpub, NULL);
|
|
} else if (priv->kind == PAZE_X509_PK_ECDSA_P256) {
|
|
paze_ecdsa_pub_t epub;
|
|
paze_ecdsa_pub_from_priv(&epub, priv->ecdsa_d);
|
|
st = paze_x509_build_spki(spki, &sl, PAZE_X509_PK_ECDSA_P256, NULL, &epub);
|
|
} else if (priv->kind == PAZE_X509_PK_ECDSA_P384) {
|
|
/* 公钥 = d384 * G */
|
|
paze_p384_point_t Q;
|
|
st = paze_p384_scalar_mult(&Q, priv->ecdsa_d384, NULL);
|
|
if (st == PAZE_OK) {
|
|
uint8_t qa[97];
|
|
st = paze_p384_to_affine(&Q, qa, sizeof(qa));
|
|
if (st == PAZE_OK) st = paze_x509_build_spki384(spki, &sl, qa);
|
|
}
|
|
} else return PAZE_ERR_INVALID;
|
|
if (st != PAZE_OK) return st;
|
|
|
|
st = paze_x509_build_dn_cn(dn, &dl, cn);
|
|
if (st != PAZE_OK) return st;
|
|
|
|
uint8_t serial[8];
|
|
if (paze_random(serial, 8) != PAZE_OK) return PAZE_ERR_PLATFORM;
|
|
serial[0] &= 0x7F; /* 保证正数 */
|
|
|
|
return paze_x509_sign_cert(out, outlen, spki, sl, dn, dl, dn, dl, priv,
|
|
not_before, not_after, serial, sizeof(serial));
|
|
}
|