golang实现JWT/JWS/JWE规范的完整安全插件库go-jose的使用
Go JOSE - Golang实现JWT/JWS/JWE规范的完整安全插件库
概述
go-jose是一个实现了Javascript对象签名和加密(JOSE)标准的Golang库,包括对JSON Web Encryption(JWE)、JSON Web Signature(JWS)和JSON Web Token(JWT)标准的支持。
该库支持紧凑和JWS/JWE JSON序列化格式,并可选支持多个接收者。
安装
import "github.com/go-jose/go-jose/v4"
支持的算法
密钥加密算法
算法 | 标识符 |
---|---|
RSA-PKCS#1v1.5 | RSA1_5 |
RSA-OAEP | RSA-OAEP, RSA-OAEP-256 |
AES密钥包装 | A128KW, A192KW, A256KW |
AES-GCM密钥包装 | A128GCMKW, A192GCMKW, A256GCMKW |
ECDH-ES + AES密钥包装 | ECDH-ES+A128KW, ECDH-ES+A192KW, ECDH-ES+A256KW |
ECDH-ES (直接) | ECDH-ES |
直接加密 | dir |
签名/MAC算法
算法 | 标识符 |
---|---|
RSASSA-PKCS#1v1.5 | RS256, RS384, RS512 |
RSASSA-PSS | PS256, PS384, PS512 |
HMAC | HS256, HS384, HS512 |
ECDSA | ES256, ES384, ES512 |
Ed25519 | EdDSA |
内容加密算法
算法 | 标识符 |
---|---|
AES-CBC+HMAC | A128CBC-HS256, A192CBC-HS384, A256CBC-HS512 |
AES-GCM | A128GCM, A192GCM, A256GCM |
压缩算法
算法 | 标识符 |
---|---|
DEFLATE (RFC 1951) | DEF |
示例代码
创建JWT令牌
package main
import (
"fmt"
"time"
"github.com/go-jose/go-jose/v4"
"github.com/go-jose/go-jose/v4/jwt"
)
func main() {
// 创建签名密钥
key := []byte("secret")
// 创建签名器
signer, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: key}, nil)
if err != nil {
panic(err)
}
// 创建JWT声明
cl := jwt.Claims{
Subject: "subject",
Issuer: "issuer",
Audience: jwt.Audience{"audience"},
Expiry: jwt.NewNumericDate(time.Now().Add(time.Hour)),
IssuedAt: jwt.NewNumericDate(time.Now()),
}
// 添加自定义声明
privateCl := struct {
User string `json:"user"`
}{
User: "testuser",
}
// 签名并生成JWT
token, err := jwt.Signed(signer).Claims(cl).Claims(privateCl).CompactSerialize()
if err != nil {
panic(err)
}
fmt.Println("JWT Token:", token)
}
验证JWT令牌
func verifyJWT(tokenStr string, key []byte) (*jwt.Claims, error) {
// 解析令牌
token, err := jwt.ParseSigned(tokenStr)
if err != nil {
return nil, fmt.Errorf("failed to parse token: %v", err)
}
// 验证声明
cl := jwt.Claims{}
privateCl := struct {
User string `json:"user"`
}{}
if err := token.Claims(key, &cl, &privateCl); err != nil {
return nil, fmt.Errorf("failed to verify claims: %v", err)
}
// 验证过期时间
if err := cl.Validate(jwt.Expected{
Issuer: "issuer",
Time: time.Now(),
}); err != nil {
return nil, fmt.Errorf("invalid claims: %v", err)
}
fmt.Println("User:", privateCl.User)
return &cl, nil
}
创建和加密JWE
func createJWE(publicKey *rsa.PublicKey) (string, error) {
// 创建加密器
encrypter, err := jose.NewEncrypter(
jose.A256GCM,
jose.Recipient{Algorithm: jose.RSA_OAEP, Key: publicKey},
(&jose.EncrypterOptions{}).WithType("JWT"),
)
if err != nil {
return "", err
}
// 准备payload
payload := []byte("secret message")
// 加密
jwe, err := encrypter.Encrypt(payload)
if err != nil {
return "", err
}
// 序列化为紧凑格式
return jwe.CompactSerialize()
}
解密JWE
func decryptJWE(jweStr string, privateKey *rsa.PrivateKey) ([]byte, error) {
// 解析JWE
jwe, err := jose.ParseEncrypted(jweStr)
if err != nil {
return nil, err
}
// 解密
return jwe.Decrypt(privateKey)
}
注意事项
-
该库使用了Go标准库
encoding/json
的一个分支版本,该版本对成员名称使用区分大小写的匹配(而不是不区分大小写的匹配) -
当前稳定版本是v4,旧版本(v1/v2)仍然可用但不再积极开发
-
v3版本仍然接收安全修复但不接收功能更新
更多关于golang实现JWT/JWS/JWE规范的完整安全插件库go-jose的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang实现JWT/JWS/JWE规范的完整安全插件库go-jose的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
go-jose库:Golang中的JWT/JWS/JWE完整实现
go-jose是一个功能强大的Golang库,实现了完整的JWT(JSON Web Token)、JWS(JSON Web Signature)和JWE(JSON Web Encryption)规范。它提供了安全的加密、签名和验证功能,是构建现代认证系统的理想选择。
安装
go get github.com/go-jose/go-jose/v3
基本使用
1. 创建和验证JWT
package main
import (
"fmt"
"time"
"github.com/go-jose/go-jose/v3"
"github.com/go-jose/go-jose/v3/jwt"
)
func main() {
// 创建签名密钥
key := []byte("secret-key-123456") // 生产环境请使用更强的密钥
// 创建签名器
signer, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: key}, nil)
if err != nil {
panic(err)
}
// 创建JWT声明
claims := jwt.Claims{
Subject: "1234567890",
Issuer: "my-auth-service",
Expiry: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
NotBefore: jwt.NewNumericDate(time.Now()),
IssuedAt: jwt.NewNumericDate(time.Now()),
Audience: jwt.Audience{"api-service"},
}
// 添加自定义声明
privateClaims := struct {
UserID string `json:"user_id"`
Email string `json:"email"`
}{
UserID: "u123",
Email: "user@example.com",
}
// 签名生成JWT
token, err := jwt.Signed(signer).Claims(claims).Claims(privateClaims).CompactSerialize()
if err != nil {
panic(err)
}
fmt.Println("Generated JWT:", token)
// 验证JWT
parsed, err := jwt.ParseSigned(token)
if err != nil {
panic(err)
}
// 验证声明
out := jwt.Claims{}
custom := struct {
UserID string `json:"user_id"`
}{}
if err := parsed.Claims(key, &out, &custom); err != nil {
panic(err)
}
// 验证过期时间等
if err := out.Validate(jwt.Expected{
Issuer: "my-auth-service",
Time: time.Now(),
}); err != nil {
panic(err)
}
fmt.Printf("Verified claims: %+v\n", out)
fmt.Printf("Custom claims: %+v\n", custom)
}
2. 使用JWE加密数据
package main
import (
"fmt"
"github.com/go-jose/go-jose/v3"
)
func main() {
// 生成RSA密钥对
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
panic(err)
}
// 创建加密器
encrypter, err := jose.NewEncrypter(
jose.A256GCM,
jose.Recipient{Algorithm: jose.RSA_OAEP, Key: &privateKey.PublicKey},
(&jose.EncrypterOptions{}).WithType("JWT").WithContentType("JWT"),
)
if err != nil {
panic(err)
}
// 加密数据
payload := []byte("secret message")
jwe, err := encrypter.Encrypt(payload)
if err != nil {
panic(err)
}
// 序列化为紧凑格式
serialized, err := jwe.CompactSerialize()
if err != nil {
panic(err)
}
fmt.Println("JWE:", serialized)
// 解密
parsed, err := jose.ParseEncrypted(serialized)
if err != nil {
panic(err)
}
decrypted, err := parsed.Decrypt(privateKey)
if err != nil {
panic(err)
}
fmt.Println("Decrypted:", string(decrypted))
}
3. 高级JWS使用(多签名)
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"fmt"
"github.com/go-jose/go-jose/v3"
)
func main() {
// 创建多个签名密钥
hmacKey := []byte("secret-key-123456")
ecdsaKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
panic(err)
}
// 创建多个签名器
signer1, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: hmacKey}, nil)
if err != nil {
panic(err)
}
signer2, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.ES256, Key: ecdsaKey}, nil)
if err != nil {
panic(err)
}
// 创建多重签名
multiSig := jose.MultiSigner{signer1, signer2}
payload := []byte("multi-signed message")
// 签名
signed, err := multiSig.Sign(payload)
if err != nil {
panic(err)
}
// 序列化
serialized, err := signed.CompactSerialize()
if err != nil {
panic(err)
}
fmt.Println("Multi-signed JWS:", serialized)
// 验证
parsed, err := jose.ParseSigned(serialized)
if err != nil {
panic(err)
}
// 验证其中一个签名即可
_, err = parsed.Verify(hmacKey)
if err != nil {
_, err = parsed.Verify(&ecdsaKey.PublicKey)
if err != nil {
panic(err)
}
}
fmt.Println("Signature verified successfully")
}
安全最佳实践
-
密钥管理:
- 永远不要将密钥硬编码在代码中
- 使用环境变量或密钥管理系统
- 定期轮换密钥
-
算法选择:
- 签名:推荐使用ES256 (ECDSA P-256) 或 RS256 (RSA 2048+)
- 加密:推荐使用RSA-OAEP或ECDH-ES+A256KW
-
令牌验证:
- 总是验证令牌的签名
- 检查过期时间(exp)
- 验证发行者(iss)和受众(aud)
- 检查令牌是否已撤销
-
令牌生命周期:
- 设置合理的过期时间
- 考虑使用刷新令牌机制
- 实现令牌撤销机制
go-jose库提供了企业级的安全功能实现,可以满足大多数现代认证和加密需求。在实际应用中,建议结合具体的业务场景和安全要求进行适当的配置和扩展。