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)
}

注意事项

  1. 该库使用了Go标准库encoding/json的一个分支版本,该版本对成员名称使用区分大小写的匹配(而不是不区分大小写的匹配)

  2. 当前稳定版本是v4,旧版本(v1/v2)仍然可用但不再积极开发

  3. v3版本仍然接收安全修复但不接收功能更新


更多关于golang实现JWT/JWS/JWE规范的完整安全插件库go-jose的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于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")
}

安全最佳实践

  1. 密钥管理

    • 永远不要将密钥硬编码在代码中
    • 使用环境变量或密钥管理系统
    • 定期轮换密钥
  2. 算法选择

    • 签名:推荐使用ES256 (ECDSA P-256) 或 RS256 (RSA 2048+)
    • 加密:推荐使用RSA-OAEP或ECDH-ES+A256KW
  3. 令牌验证

    • 总是验证令牌的签名
    • 检查过期时间(exp)
    • 验证发行者(iss)和受众(aud)
    • 检查令牌是否已撤销
  4. 令牌生命周期

    • 设置合理的过期时间
    • 考虑使用刷新令牌机制
    • 实现令牌撤销机制

go-jose库提供了企业级的安全功能实现,可以满足大多数现代认证和加密需求。在实际应用中,建议结合具体的业务场景和安全要求进行适当的配置和扩展。

回到顶部