24 函数 · JWT/JWS + JWKS · MIT

xjwt 函数参考

JSON Web Token (RFC 7519) / JWS (RFC 7515) 签发与验证:build.bat xjwt 编入。HS256/384/512(HMAC)、RS256/384/512(RSA PKCS#1,PKCS#8/PKCS#1 双封装私钥)、ES256(ECDSA P-256,签名按 RFC 7518 §3.4 DER 编码,与 OpenSSL 双向互认)、JWKS (RFC 7517) 选钥。claims 以 xrt 的 xvalue* 承载,脚本层零桥接。

签发(9 函数)

xjwtSign config 式

精调签发:注入 iss/aud/sub/jti/exp/iat 与 header kid,自动注入 iat。header 经 JSON 序列化构造(kid 自动转义且不受定长缓冲限制);签名输出容量校验(覆盖 RSA-8192)。注意:就地修改传入 claims 对象。

char* xjwtSign(const xjwtconfig* pConfig, xvalue* claims);
示例 · 签发访问令牌
xjwtconfig cfg;
xjwtConfigInit(&cfg);
cfg.Alg = XJWT_ALG_RS256;
cfg.KeyPem = sPrivateKeyPem;   /* 启动时从文件读入 */
cfg.KeyId = "web-2026-09";
cfg.Issuer = "https://myapp.example.com";
cfg.Audience = "myapp-web";
cfg.ExpireSeconds = 900;

xvalue* claims = xrtValueObject();
xrtValueObjectSetNew(claims, xrtStrView("scope"),
    xrtValueString(xrtStrView("profile orders")));
char* sToken = xjwtSign(&cfg, claims);  /* xrtFree 释放 */
xrtValueRelease(claims);

xjwtHs256 / Hs384 / Hs512 / Rs256 / Rs384 / Rs512 / Es256 一步式

七规格一步签发(claims + 密钥 + 有效期秒)。本族不注入 iss/aud——验证侧 xjwtcheck 带 Issuer/Audience 校验时请改用 xjwtSign。HS 族收密钥 C 字符串(内嵌 '\0' 截断,不支持二进制密钥);RS 族收 PKCS#8/PKCS#1 私钥 PEM;ES256 收 SEC1/PKCS#8 私钥 PEM。

char* xjwtHs256(xvalue* claims, const char* secret, int expireSeconds);

xjwtConfigInit 初始化

配置结构清零并默认 HS256。KeyId ≥256 字符拒绝签发(与验证侧上限一致)。

验证(4 函数)

xjwtVerify PEM 路径

一步验证:header alg 解码 → claims 校验(exp/nbf 非整数拒绝、iss/aud 常时比较、aud 支持字符串与数组)→ 验签。HS 族收密钥字符串,RS/ES 收公钥 PEM;密钥类型与算法族交叉校验(防算法混淆)。通过返回 claims(xrtValueRelease 释放),失败 NULL 并设错误码。

xvalue* xjwtVerify(const char* token, const char* keyPem, const xjwtcheck* check);
示例 · 认证中间件
xjwtcheck check;
xjwtCheckInit(&check);
check.Issuer = "https://myapp.example.com";
check.Audience = "myapp-web";
check.ClockLeeway = 30;

xvalue* claims = xjwtVerify(sBearerToken, sPublicKeyPem, &check);
if ( claims == NULL ) {
    if ( xjwtLastError() == XJWT_ERROR_EXPIRED )
        return 401;   /* 过期 */
    return 401;           /* 篡改/混淆/签名不符 */
}
char aUser[64];
xjwtClaimString(claims, "sub", aUser, sizeof(aUser));
xrtValueRelease(claims);

xjwtKeyParse / xjwtKeyFree / xjwtVerifyKey 公钥缓存

高频验证:公钥解析一次(RSA SPKI/PKCS#1 或 EC P-256 SPKI,拒私钥与证书),缓存对象只读可跨线程共享。VerifyKey 含 HS 族拒绝与算法族↔密钥类型交叉校验。堆指针模式,脚本层正路。

xjwtkey* xjwtKeyParse(const char* publicPem); void xjwtKeyFree(xjwtkey* key); xvalue* xjwtVerifyKey(const char* token, const xjwtkey* key, const xjwtcheck* check);

JWKS(3 函数)

xjwtJwksParse / xjwtJwksFree 公钥集

解析 JWKS (RFC 7517) JSON:RSA n/e 与 EC x/y(仅 P-256,其他曲线跳过;坐标必须恰 32 字节)。最多 16 把密钥,超出整体拒绝;条目畸形(如有 n 无 e)立即释放不残留。

xjwtjwks* xjwtJwksParse(const char* json); void xjwtJwksFree(xjwtjwks* jwks);

xjwtVerifyJwks 选钥验证

按 token header 的 kid 严格匹配选钥(token 无 kid 取第一把;kid ≥256 三路拒绝)。显式拒绝 HS 族;RS* ↔ RSA、ES256 ↔ EC 类型交叉校验。密钥轮换重试:xjwtLastError() == XJWT_ERROR_KEY_NOT_FOUND 时重拉 JWKS 再验一次。

xvalue* xjwtVerifyJwks(const char* token, const xjwtjwks* jwks, const xjwtcheck* check);

claims 与错误(3 函数)

xjwtClaimsValid 手工流程

claims 独立校验(exp/nbf/iss/aud):必须是 JSON 对象;exp/nbf 存在但非整数一律拒绝;aud 支持字符串与数组(任一命中)。

xjwtClaimString 取值

字符串 claim 安全提取:超长截断到 cap-1 并补零,claim 不存在或非字符串返回 false。

bool xjwtClaimString(const xvalue* claims, const char* key, char* pOut, size_t iCap);

xjwtLastError 错误读取

返回当前执行上下文中 xjwt 域错误码(XJWT_ERROR_*),非本域返回 0。仅在失败后立即读取。

解码与工具(5 函数)

xjwtDecode / xjwtDecodeHeader 解码

不验签解码(信任源/调试):前者返回 claims 并可收 header alg;后者返回 header 对象并可收 alg + kid(kid 为堆拷贝,调用方 xrtFree)。claims/header 必须是 JSON 对象;传 pKid 时 kid ≥256 整体拒绝。

xjwtCheckInit / xjwtAlgName / xjwtAlgParse 工具

校验参数清零(NowOverride 可注入测试时钟、ClockLeeway 容差秒);算法枚举与名字互转。

void xjwtCheckInit(xjwtcheck* pCheck); const char* xjwtAlgName(int alg); int xjwtAlgParse(const char* name);
安全基线:exp/nbf 非整数拒绝、alg=none 与算法混淆三路径拒绝(含 JWKS)、iss/aud/HS 常时比较、kid 严格匹配 + 长度三态。测试 117 项(含 OpenSSL 双向互认、RSA-8192、fuzz 2000、稳态零泄漏实测)。完整源码见仓库 lib/xjwt;与 xoauth2 数据耦合组成 OIDC 登录。