golang实现JOSE标准(JWA/JWE/JWK/JWS/JWT)的安全插件库jwx的使用
Golang实现JOSE标准(JWA/JWE/JWK/JWS/JWT)的安全插件库jwx的使用
概述
github.com/lestrrat-go/jwx/v3
是一个实现了各种JWx技术(JWA/JWE/JWK/JWS/JWT,也称为JOSE)的Go模块。
特性
- 完整覆盖JWA/JWE/JWK/JWS/JWT,而不仅仅是JWT+最小工具集
- 支持具有多个签名的JWS消息,包括紧凑和JSON序列化
- 支持具有分离负载的JWS
- 支持具有未编码负载的JWS(RFC7797)
- 支持具有多个接收者的JWE消息,包括紧凑和JSON序列化
- 大多数操作适用于JWK或原始密钥(如*rsa.PrivateKey, *ecdsa.PrivateKey等)
- 统一且对称的API设计
- jws.Parse/Verify/Sign
- jwe.Parse/Encrypt/Decrypt
- 参数组织为显式必需参数和可选的WithXXXX()样式选项
- 额外实用工具
jwk.Cache
保持JWKS始终最新- 支持bazel构建
示例代码
以下是一个完整示例,展示了如何使用jwx库处理JWK、JWT、JWE和JWS:
package main
import (
"bytes"
"fmt"
"net/http"
"time"
"github.com/lestrrat-go/jwx/v3/jwa"
"github.com/lestrrat-go/jwx/v3/jwe"
"github.com/lestrrat-go/jwx/v3/jwk"
"github.com/lestrrat-go/jwx/v3/jws"
"github.com/lestrrat-go/jwx/v3/jwt"
)
func main() {
// 示例JSON RSA私钥
jsonRSAPrivateKey := []byte(`{
"kty": "RSA",
"n": "...",
"e": "AQAB",
"d": "...",
"p": "...",
"q": "...",
"dp": "...",
"dq": "...",
"qi": "..."
}`)
// 示例负载
payloadLoremIpsum := []byte("Lorem ipsum dolor sit amet")
// 1. 解析JWK
privkey, err := jwk.ParseKey(jsonRSAPrivateKey)
if err != nil {
fmt.Printf("failed to parse JWK: %s\n", err)
return
}
// 2. 获取公钥
pubkey, err := jwk.PublicKeyOf(privkey)
if err != nil {
fmt.Printf("failed to get public key: %s\n", err)
return
}
// 3. 处理JWT
{
// 构建JWT
tok, err := jwt.NewBuilder().
Issuer(`github.com/lestrrat-go/jwx`).
IssuedAt(time.Now()).
Build()
if err != nil {
fmt.Printf("failed to build token: %s\n", err)
return
}
// 签名JWT
signed, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256(), privkey))
if err != nil {
fmt.Printf("failed to sign token: %s\n", err)
return
}
// 验证JWT
{
verifiedToken, err := jwt.Parse(signed, jwt.WithKey(jwa.RS256(), pubkey))
if err != nil {
fmt.Printf("failed to verify JWS: %s\n", err)
return
}
_ = verifiedToken
}
// 处理HTTP请求
{
req, err := http.NewRequest(http.MethodGet, `https://github.com/lestrrat-go/jwx`, nil)
req.Header.Set(`Authorization`, fmt.Sprintf(`Bearer %s`, signed))
verifiedToken, err := jwt.ParseRequest(req, jwt.WithKey(jwa.RS256(), pubkey))
if err != nil {
fmt.Printf("failed to verify token from HTTP request: %s\n", err)
return
}
_ = verifiedToken
}
}
// 4. 使用JWE加密和解密任意负载
{
encrypted, err := jwe.Encrypt(payloadLoremIpsum, jwe.WithKey(jwa.RSA_OAEP(), pubkey))
if err != nil {
fmt.Printf("failed to encrypt payload: %s\n", err)
return
}
decrypted, err := jwe.Decrypt(encrypted, jwe.WithKey(jwa.RSA_OAEP(), privkey))
if err != nil {
fmt.Printf("failed to decrypt payload: %s\n", err)
return
}
if !bytes.Equal(decrypted, payloadLoremIpsum) {
fmt.Printf("verified payload did not match\n")
return
}
}
// 5. 使用JWS签名和验证任意负载
{
signed, err := jws.Sign(payloadLoremIpsum, jws.WithKey(jwa.RS256(), privkey))
if err != nil {
fmt.Printf("failed to sign payload: %s\n", err)
return
}
verified, err := jws.Verify(signed, jws.WithKey(jwa.RS256(), pubkey))
if err != nil {
fmt.Printf("failed to verify payload: %s\n", err)
return
}
if !bytes.Equal(verified, payloadLoremIpsum) {
fmt.Printf("verified payload did not match\n")
return
}
}
}
包说明
包名 | 说明 |
---|---|
jwt | RFC 7519 |
jwk | RFC 7517 + RFC 7638 |
jwa | RFC 7518 |
jws | RFC 7515 + RFC 7797 |
jwe | RFC 7516 |
为什么选择这个库
与其他主要处理JWT的项目不同,这个模块处理JWS、JWE、JWK和JWT的完整范围。如果你不仅需要解析JWT,还需要控制JWK,或者处理不是JWT的负载,你应该考虑使用这个模块。
从实现角度来看,这个模块与其他模块的不同之处在于它非常努力地只暴露API,而不暴露内部数据。这种结构允许我们在结构体中放入额外的智能,例如在你想解析/写入自定义字段时做正确的事情。
贡献
对于错误报告和功能请求,请尽量遵循问题模板。对于错误报告或功能请求,失败的测试更好。
对于拉取请求,请确保包含测试以验证你所做的更改。
更多关于golang实现JOSE标准(JWA/JWE/JWK/JWS/JWT)的安全插件库jwx的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang实现JOSE标准(JWA/JWE/JWK/JWS/JWT)的安全插件库jwx的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
使用jwx库实现JOSE标准(JWA/JWE/JWK/JWS/JWT)的安全功能
jwx是一个功能强大的Golang库,用于实现JOSE标准(JWA/JWE/JWK/JWS/JWT)的安全功能。下面我将介绍如何使用这个库来实现常见的加密、签名和令牌功能。
安装
首先安装jwx库:
go get github.com/lestrrat-go/jwx/v2
JWT (JSON Web Token) 示例
创建JWT
package main
import (
"fmt"
"time"
"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v2/jwt"
)
func main() {
// 创建JWT令牌
token, err := jwt.NewBuilder().
Issuer("my-issuer").
Subject("user123").
Audience([]string{"client1", "client2"}).
IssuedAt(time.Now()).
Expiration(time.Now().Add(time.Hour)).
Claim("custom", "value").
Build()
if err != nil {
fmt.Printf("failed to build token: %s\n", err)
return
}
// 使用HMAC算法签名
signed, err := jwt.Sign(token, jwt.WithKey(jwa.HS256, []byte("your-secret-key")))
if err != nil {
fmt.Printf("failed to sign token: %s\n", err)
return
}
fmt.Printf("Signed JWT: %s\n", signed)
}
验证JWT
func verifyJWT(tokenStr string) {
// 解析并验证JWT
token, err := jwt.Parse([]byte(tokenStr),
jwt.WithVerify(true),
jwt.WithKey(jwa.HS256, []byte("your-secret-key")),
)
if err != nil {
fmt.Printf("failed to parse token: %s\n", err)
return
}
// 获取声明
fmt.Printf("Issuer: %s\n", token.Issuer())
fmt.Printf("Subject: %s\n", token.Subject())
if custom, ok := token.Get("custom"); ok {
fmt.Printf("Custom claim: %v\n", custom)
}
}
JWS (JSON Web Signature) 示例
创建JWS
package main
import (
"fmt"
"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v2/jws"
)
func main() {
payload := []byte("important message to sign")
// 使用RSA私钥签名
privateKey := `-----BEGIN PRIVATE KEY-----
... your private key here ...
-----END PRIVATE KEY-----`
signed, err := jws.Sign(payload, jws.WithKey(jwa.RS256, []byte(privateKey)))
if err != nil {
fmt.Printf("failed to sign payload: %s\n", err)
return
}
fmt.Printf("Signed JWS: %s\n", signed)
}
验证JWS
func verifyJWS(jwsStr string) {
publicKey := `-----BEGIN PUBLIC KEY-----
... your public key here ...
-----END PUBLIC KEY-----`
verified, err := jws.Verify([]byte(jwsStr), jws.WithKey(jwa.RS256, []byte(publicKey)))
if err != nil {
fmt.Printf("failed to verify JWS: %s\n", err)
return
}
fmt.Printf("Verified payload: %s\n", verified)
}
JWE (JSON Web Encryption) 示例
加密数据
package main
import (
"fmt"
"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v2/jwe"
)
func main() {
payload := []byte("sensitive data to encrypt")
// 使用RSA公钥加密
publicKey := `-----BEGIN PUBLIC KEY-----
... your public key here ...
-----END PUBLIC KEY-----`
encrypted, err := jwe.Encrypt(
payload,
jwe.WithKey(jwa.RSA_OAEP, []byte(publicKey)),
jwe.WithContentEncryption(jwa.A256GCM),
)
if err != nil {
fmt.Printf("failed to encrypt payload: %s\n", err)
return
}
fmt.Printf("Encrypted JWE: %s\n", encrypted)
}
解密数据
func decryptJWE(jweStr string) {
privateKey := `-----BEGIN PRIVATE KEY-----
... your private key here ...
-----END PRIVATE KEY-----`
decrypted, err := jwe.Decrypt(
[]byte(jweStr),
jwe.WithKey(jwa.RSA_OAEP, []byte(privateKey)),
)
if err != nil {
fmt.Printf("failed to decrypt JWE: %s\n", err)
return
}
fmt.Printf("Decrypted payload: %s\n", decrypted)
}
JWK (JSON Web Key) 示例
生成和解析JWK
package main
import (
"fmt"
"github.com/lestrrat-go/jwx/v2/jwk"
)
func main() {
// 生成RSA密钥对
key, err := jwk.GenerateKey(jwk.WithKeyType(jwa.RSA))
if err != nil {
fmt.Printf("failed to generate key: %s\n", err)
return
}
// 设置密钥ID
_ = key.Set(jwk.KeyIDKey, "my-key-id")
// 将密钥序列化为JSON
jsonBuf, err := jwk.Encode(key)
if err != nil {
fmt.Printf("failed to encode key: %s\n", err)
return
}
fmt.Printf("JWK: %s\n", jsonBuf)
// 从JSON解析JWK
parsedKey, err := jwk.ParseKey(jsonBuf)
if err != nil {
fmt.Printf("failed to parse key: %s\n", err)
return
}
fmt.Printf("Parsed key type: %s\n", parsedKey.KeyType())
}
最佳实践
- 密钥管理:永远不要在代码中硬编码密钥,使用环境变量或密钥管理系统
- 算法选择:
- 签名:优先选择ES256/ES384/ES512或PS256/PS384/PS512
- 加密:优先选择RSA-OAEP-256或ECDH-ES+A256KW
- 令牌有效期:总是设置合理的过期时间
- 错误处理:正确处理所有错误,不要泄露敏感信息
- 密钥轮换:实现密钥轮换机制,定期更新密钥
jwx库提供了丰富的功能来处理JOSE标准,上述示例展示了最常见的用例。根据你的具体需求,你可能需要查阅官方文档来了解更多高级功能。