JWT(JSON Web Token)通常由标头 + 载荷 + 签名组成。
头部
包含 JWT 的元数据信息。
alg:用于签署令牌的算法。
typ: 令牌的类型,始终为 JWT。
{
"alg": "HS256",
"typ": "JWT"
}
您可以找到更多标头,但上述标头始终存在。
载荷
载荷包含 JWT 的声明。标准请求头如下:
iss:签发者,即生成并签发 JWT 的实体。
sub: 主体,即此令牌所标识的实体。
aud: 受众,即此 JWT 的目标受众。
exp:到期时间,UNIX 格式的时间戳,在此时间之后令牌不应被接受。
iat: 签发时间,指定令牌的签发日期。
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
签名
签名使用编码后的标头、编码后的载荷、密钥和加密算法创建。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
以下算法同时受客户端和服务器 JWT 组件支持。
需要 OpenSSL 库 才能签名和验证 JWT,但有一个例外。
这三种 ML-DSA 算法是 RFC 9964 所定义的后量子 JOSE 算法, 也就是上面所说的那个例外:它们由 sgcCrypto 包以纯 Object Pascal 实现, 因此在没有任何 OpenSSL 库的情况下也能签名和验证。签名采用 FIPS 204 的 ML-DSA, 在纯模式下以空上下文对 JWS 签名输入进行签名,并以 base64url 形式放入令牌中。 签名器是确定性的,因此相同的密钥和相同的输入总是产生相同的签名。
枚举值为 jwtMLDSA44、 jwtMLDSA65 和 jwtMLDSA87,它们写入的 alg 头分别是 ML-DSA-44、 ML-DSA-65 和 ML-DSA-87。客户端使用 JWTOptions.Algorithms.MLDSA.PrivateKey (一个 PKCS#8 PEM 私钥)进行签名,服务器使用 JWTOptions.Algorithms.MLDSA.PublicKey (一个 SubjectPublicKeyInfo PEM 公钥)进行验证。密钥的参数集必须与该算法所指明的一致: 否则签名会引发异常,验证则返回 False。
签名体积很大。ML-DSA-44 的签名为 2420 字节,而 ES256 只有 64 字节, 因此令牌大约会增加 3.2 KB 的 base64url 内容,ML-DSA-87 则大约增加 6.2 KB。 在采用它之前,请先检查令牌所经过的每一个环节的头部大小限制。
密钥还可以以 AKP 形式的 JSON Web Key 携带,见 RFC 9964 第 3 节和第 4 节。 sgcMLDSA_ExportPublicJWK 和 sgcMLDSA_ExportPrivateJWK 负责写出,sgcMLDSA_ImportJWK 可以读取其中任意一种形式,而 sgcMLDSA_ImportJWKAsPEM 则直接把一个 JWK 转换成这两个组件所读取的 PEM 字符串。私有 JWK 在 priv 中保存 32 字节的种子, 这是第 4 节定义的唯一私钥形式,因此展开后的私钥无法写成 JWK。 这四个函数都声明在 sgcHTTP_JWT_MLDSA 中。
* ML-DSA 算法需要 sgcCrypto 包。没有它时这些枚举值依然存在, 因此用它们保存的窗体仍然可以加载,但签名会引发异常, 并且每一个 ML-DSA 令牌都会被拒绝。
TsgcHTTP_JWT_Client: JWT 客户端,允许您对 JWT 进行编码和签名,并在 HTTP 和 WebSocket 协议中作为授权头发送。
TsgcHTTP_JWT_Server:JWT 服务器,允许解码和验证作为 HTTP 和 WebSocket 协议中的 Authorization 标头接收的 JWT。
* JWT 组件至少需要 Indy 版本 10.6.0.5169 或 sgcWebSockets Enterprise 版。