jwc

package module
v0.0.0-...-fd1cdc9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 18, 2019 License: MIT Imports: 21 Imported by: 0

README

jwc

[DEPRECATED] I'd recommend https://github.com/square/go-jose instead

GoDoc Build Status codecov Go Report Card

JSON Web Cryptography

  • jws - JSON Web Signature

    • RSASSA-PSS + SHA256, recommended +
    • RSASSA-PKCS1-v1_5 + SHA256, recommended -
  • jwe - JSON Web Encryption

    • key encryption
      • RSA-OAEP, recommended +
      • RSAES-PKCS1-v1_5, recommended -
    • content encryption
      • A128CBC-HS256
      • A192CBC-HS384
      • A256CBC-HS512
      • A128GCM
      • A192GCM
      • A256GCM

Example

Signature
package main

import (
	"crypto/rand"
	"crypto/rsa"
	"encoding/json"
	"fmt"
	"time"

	"github.com/google/uuid"
	"github.com/khezen/jwc"
)

func main() {
	var (
		privateKey, _ = rsa.GenerateKey(rand.Reader, 1024)
		jwkid         = jwc.JWKID("52d510e3-8d0a-4ef8-a81a-c8cd7ce06472")
		jwk, _        = jwc.RSAToPublicJWK(&privateKey.PublicKey, jwkid, jwc.PS256, nil)
		jwkBytes, _   = json.Marshal(jwk)
	)
	jwtStr := issueJWT(jwkid, privateKey)
	token := verify(jwtStr, jwkBytes)
	fmt.Println(jwtStr)
	fmt.Println()
	fmt.Println(token)
}

func issueJWT(keyID jwc.JWKID, privateKey *rsa.PrivateKey) string {
	now := time.Now().UTC()
	nowUnix := now.Unix()
	exp := now.Add(time.Minute)
	expUnix := exp.Unix()
	jwt, err := jwc.NewJWT(
		jwc.JWTPayload{
			RegisteredClaims: jwc.RegisteredClaims{
				IssuedAtTimestamp:   nowUnix,
				ExpirationTimestamp: expUnix,
				Issuer:              "github.com/khezen/jwc/jwt_test.go",
				Subject:             "customer_id",
				Audiance:            "android.myapp.com",
			},
			PrivateClaims: jwc.PrivateClaims{
				"id":  "token_id",
				"did": "device_id",
				"sco": "offline",
				"cc":  "dummy_code_challenge",
				"ccm": "S256",
			},
		},
		jwc.PS256,
	)
	if err != nil {
		panic(err)
	}
	jwtStr, err := jwt.Encode(keyID, privateKey)
	if err != nil {
		panic(err)
	}
	return jwtStr
}

func verify(jwtStr string, jwkBytes []byte) *jwc.JWT {
	var pubJWK jwc.RSAPublicJWK
	err := json.Unmarshal(jwkBytes, &pubJWK)
	if err != nil {
		panic(err)
	}
	pubKey, err := pubJWK.PublicRSA()
	if err != nil {
		panic(err)
	}
	token, err := jwc.DecodeVerifyJWT(jwtStr, pubKey)
	if err != nil {
		panic(err)
	}
	return token
}
output
eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjRkYjY2NzgwLWYzNjMtNDdmZC05MDlkLTQ0MWM1ZmUyM2Q2MSJ9.eyJleHAiOjE1NDYzNjk0NTIsImlhdCI6MTU0NjM2OTM5MiwiaXNzIjoiZ2l0aHViLmNvbS9raGV6ZW4vandjL2p3dF90ZXN0LmdvIiwic3ViIjoidGVzdCIsInByaXYiOnsiYXVkIjoiYW5kcm9pZC5teWFwcC5jb20iLCJjYyI6ImR1bW15Q29kZUNoYWxsZW5nZSIsImNjbSI6IlMyNTYiLCJjaWQiOiIwMTA4YmZiZC0yZjc2LTQyMmEtYTNiZC0yNjMxZmVhYWNiZWUiLCJkaWQiOiJkZXZpY2VJRCIsInNjbyI6Im9mZmxpbmUiLCJ0aWQiOiJiMGIwZWM5Mi1jZTNjLTQ3ZjUtODQ5Ny03Y2FiMjkxNDcyZDAifX0.WktA5tt_Tt6R-qZuTqpSB7xnYDrMlJXjz7aTzQys1UjMAEjLFHCWqmLp33DRlUboZiZQWa_6D4c6fzS-UHFQ9pQ_73s_Rg83i6XEMJIlr2k420g_cO-N_y425gnoJ2GDOpVSGxMS5uofh8JoE6OZpPNauJo_Z5MNpEKp5XZDEAE

&{{PS256 JWT 4db66780-f363-47fd-909d-441c5fe23d61} {{1546369452 1546369392 github.com/khezen/jwc/jwt_test.go test } map[tid:b0b0ec92-ce3c-47f5-8497-7cab291472d0 aud:android.myapp.com cc:dummyCodeChallenge ccm:S256 cid:0108bfbd-2f76-422a-a3bd-2631feaacbee did:deviceID sco:offline]}}
Encryption
package main

import (
	"bytes"
	"crypto/rand"
	"crypto/rsa"
	"encoding/json"
	"fmt"

	"github.com/google/uuid"
	"github.com/khezen/jwc"
)

func main() {
	var (
		privateKey, _     = rsa.GenerateKey(rand.Reader, 2042)
		jwkid         = jwc.JWKID("52d510e3-8d0a-4ef8-a81a-c8cd7ce06472")
		publicJWK, _      = jwc.RSAToPublicJWK(&privateKey.PublicKey, jwkid, jwc.ROAEP, nil)
		publicJWKBytes, _ = json.Marshal(publicJWK)
		message           = []byte("lorem ipsum ipsa occaecati aut velit facilis enim dolorum id eius magni ducimus sed illum similique cupiditate sit id perferendis alias sint")
	)
	compactJWE := encrypt(message, publicJWKBytes)
	plaintext := decrypt(compactJWE, privateKey)
	fmt.Println(bytes.EqualFold(message, plaintext))
}

func encrypt(plaintext, jwkBytes []byte) []byte {
	var jwk jwc.RSAPublicJWK
	err := json.Unmarshal(jwkBytes, &jwk)
	if err != nil {
		panic(err)
	}
	pubKey, err := jwk.PublicRSA()
	if err != nil {
		panic(err)
	}
	jwe, err := jwc.NewJWE(
		&jwc.JOSEHeaders{Algorithm: jwc.ROAEP, Encryption: jwc.A256GCM},
		pubKey,
		plaintext,
	)
	if err != nil {
		panic(err)
	}
	jweString, err := jwe.Compact()
	if err != nil {
		panic(err)
	}
	return jweString
}

func decrypt(compactJWE []byte, privateKey *rsa.PrivateKey) []byte {
	jwe, err := jwc.ParseCompactJWE(compactJWE)
	if err != nil {
		panic(err)
	}
	plaintext, err := jwe.Plaintext(privateKey)
	if err != nil {
		panic(err)
	}
	return plaintext
}
output
true

Documentation

Index

Examples

Constants

View Source
const (
	// Signing - JWK is intended to be used for signature
	Signing Usage = "sig"
	// RS256 - RSASSA-PKCS1-v1_5 + SHA256
	RS256 Algorithm = "RS256"
	// PS256 - RSASSA-PSS + SHA256
	PS256 Algorithm = "PS256"
)
View Source
const (
	// Encryption - JWK is intended to be used for encryption
	Encryption Usage = "enc"
	// ROAEP - RSAES OAEP
	ROAEP Algorithm = "RSA-OAEP"
	// RSA15 - RSAES-PKCS1-v1_5
	RSA15 Algorithm = "RSA1_5"
)

Variables

View Source
var (
	// ErrUnsupportedAlgorithm -
	ErrUnsupportedAlgorithm = errors.New("ErrUnsupportedAlgorithm")
	// ErrUnsupportedEncryption -
	ErrUnsupportedEncryption = errors.New("ErrUnsupportedEncryption")
)
View Source
var (
	// JWTBase64Encoding - encoding used to encode and decode tokens
	JWTBase64Encoding = base64.URLEncoding.WithPadding(base64.NoPadding)
	// ErrJWTUnparsable -
	ErrJWTUnparsable = errors.New("ErrJWTUnparsable")
)
View Source
var (
	// ErrCompactJWEUnparsable -
	ErrCompactJWEUnparsable = errors.New("ErrCompactJWEUnparsable")
)
View Source
var (
	// ErrJWKValueOutOfRangeParsingBigInt -
	ErrJWKValueOutOfRangeParsingBigInt = errors.New("ErrJWKValueOutOfRangeParsingBigInt")
)

Functions

func ExtractJWTParts

func ExtractJWTParts(jwtStr string) (headerBase64, payloadBase64, signatureBase64 string, err error)

ExtractJWTParts return base64 encoded string of the header, the payload and the signature

func PadPKCS7

func PadPKCS7(src []byte, blockSize int) []byte

PadPKCS7 -

func UnpadPKCS7

func UnpadPKCS7(src []byte) ([]byte, error)

UnpadPKCS7 -

func VerifyJWT

func VerifyJWT(jwtStr string, signKey crypto.PublicKey) error

VerifyJWT - validate the jwt integerity

Types

type Algorithm

type Algorithm string

Algorithm -

type ContentEncryptAlgorithm

type ContentEncryptAlgorithm string

ContentEncryptAlgorithm -

const (
	// A128CBCHS256 - A128CBC-HS256
	A128CBCHS256 ContentEncryptAlgorithm = "A128CBC-HS256"
	// A192CBCHS384 - A192CBC-HS384
	A192CBCHS384 ContentEncryptAlgorithm = "A192CBC-HS384"
	// A256CBCHS512 - A256CBC-HS512
	A256CBCHS512 ContentEncryptAlgorithm = "A256CBC-HS512"
	// A128GCM - A128GCM
	A128GCM ContentEncryptAlgorithm = "A128GCM"
	// A192GCM - A192GCM
	A192GCM ContentEncryptAlgorithm = "A192GCM"
	// A256GCM - A256GCM
	A256GCM ContentEncryptAlgorithm = "A256GCM"
)

type JOSEHeaders

type JOSEHeaders struct {
	Algorithm                      Algorithm               `json:"alg"`
	Encryption                     ContentEncryptAlgorithm `json:"enc"`
	AdditionalAuthenticatedDataB64 string                  `json:"aad,omitempty"`
	Type                           string                  `json:"typ,omitempty"`
	ContentType                    string                  `json:"cty,omitempty"`
	KeyID                          JWKID                   `json:"kid,omitempty"`
	JWKURI                         string                  `json:"jku,omitempty"`
	JWK                            *JWK                    `json:"jwk,omitempty"`
	Zip                            string                  `json:"zip,omitempty"`
	Critical                       []string                `json:"crit,omitempty"`
}

JOSEHeaders JWE header

type JWE

type JWE struct {
	ProtectedB64                string `json:"protected"`
	UnprotectedB64              string `json:"unprotected,omitmepty"`
	AdditionalAuthenticatedData string `json:"add,omitempty"`
	CipherCEKB64                string `json:"encrypted_key"`
	InitVectorB64               string `json:"iv"`
	CiphertextB64               string `json:"ciphertext"`
	TagB64                      string `json:"tag,omitempty"`
}

JWE -

Example
package main

import (
	"bytes"
	"crypto/rand"
	"crypto/rsa"
	"encoding/json"
	"fmt"

	"github.com/khezen/jwc"
)

func main() {
	var (
		privateKey, _     = rsa.GenerateKey(rand.Reader, 2042)
		jwkid             = jwc.JWKID("52d510e3-8d0a-4ef8-a81a-c8cd7ce06472")
		publicJWK, _      = jwc.RSAToPublicJWK(&privateKey.PublicKey, jwkid, jwc.ROAEP, nil)
		publicJWKBytes, _ = json.Marshal(publicJWK)
		message           = []byte("lorem ipsum ipsa occaecati aut velit facilis enim dolorum id eius magni ducimus sed illum similique cupiditate sit id perferendis alias sint")
	)
	compactJWE := encrypt(message, publicJWKBytes)
	plaintext := decrypt(compactJWE, privateKey)
	fmt.Println(bytes.EqualFold(message, plaintext))
}

func encrypt(plaintext, jwkBytes []byte) []byte {
	var jwk jwc.RSAPublicJWK
	err := json.Unmarshal(jwkBytes, &jwk)
	if err != nil {
		panic(err)
	}
	pubKey, err := jwk.PublicRSA()
	if err != nil {
		panic(err)
	}
	jwe, err := jwc.NewJWE(
		&jwc.JOSEHeaders{Algorithm: jwc.ROAEP, Encryption: jwc.A256GCM},
		pubKey,
		plaintext,
	)
	if err != nil {
		panic(err)
	}
	jweString, err := jwe.Compact()
	if err != nil {
		panic(err)
	}
	return jweString
}

func decrypt(compactJWE []byte, privateKey *rsa.PrivateKey) []byte {
	jwe, err := jwc.ParseCompactJWE(compactJWE)
	if err != nil {
		panic(err)
	}
	plaintext, err := jwe.Plaintext(privateKey)
	if err != nil {
		panic(err)
	}
	return plaintext
}
Output:

func NewJWE

func NewJWE(protectedHeaders *JOSEHeaders, pubKey crypto.PublicKey, plaintext []byte) (*JWE, error)

NewJWE -

func ParseCompactJWE

func ParseCompactJWE(compact []byte) (jwe *JWE, err error)

ParseCompactJWE -

func (*JWE) Compact

func (jwe *JWE) Compact() ([]byte, error)

Compact formats to the JWE compact serialisation

func (*JWE) Plaintext

func (jwe *JWE) Plaintext(privKey crypto.PrivateKey) (plaintext []byte, err error)

Plaintext returns deciphered content

type JWK

type JWK struct {
	Type                   string     `json:"kty"`
	ID                     JWKID      `json:"kid"`
	Usage                  Usage      `json:"use"`
	Algorithm              Algorithm  `json:"alg"`
	CertificateChainBase64 []string   `json:"x5c,omitempty"`
	ThumbprintBase64       string     `json:"x5t,omitempty"`
	ExpirationTime         *time.Time `json:"exp,omitempty"`
}

JWK - JSON Web Key

type JWKID

type JWKID string

JWKID - identify a specific jwk in a set

type JWT

type JWT struct {
	Header  JWTHeader
	Payload JWTPayload
}

JWT - JSON Web Token

Example
package main

import (
	"crypto/rand"
	"crypto/rsa"
	"encoding/json"
	"fmt"
	"time"

	"github.com/khezen/jwc"
)

func main() {
	var (
		privateKey, _ = rsa.GenerateKey(rand.Reader, 1024)
		jwkid         = jwc.JWKID("52d510e3-8d0a-4ef8-a81a-c8cd7ce06472")
		jwk, _        = jwc.RSAToPublicJWK(&privateKey.PublicKey, jwkid, jwc.PS256, nil)
		jwkBytes, _   = json.Marshal(jwk)
	)
	jwtStr := issueJWT(jwkid, privateKey)
	token := verify(jwtStr, jwkBytes)
	fmt.Println(jwtStr)
	fmt.Println()
	fmt.Println(token)
	// eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjRkYjY2NzgwLWYzNjMtNDdmZC05MDlkLTQ0MWM1ZmUyM2Q2MSJ9.eyJleHAiOjE1NDYzNjk0NTIsImlhdCI6MTU0NjM2OTM5MiwiaXNzIjoiZ2l0aHViLmNvbS9raGV6ZW4vandjL2p3dF90ZXN0LmdvIiwic3ViIjoidGVzdCIsInByaXYiOnsiYXVkIjoiYW5kcm9pZC5teWFwcC5jb20iLCJjYyI6ImR1bW15Q29kZUNoYWxsZW5nZSIsImNjbSI6IlMyNTYiLCJjaWQiOiIwMTA4YmZiZC0yZjc2LTQyMmEtYTNiZC0yNjMxZmVhYWNiZWUiLCJkaWQiOiJkZXZpY2VJRCIsInNjbyI6Im9mZmxpbmUiLCJ0aWQiOiJiMGIwZWM5Mi1jZTNjLTQ3ZjUtODQ5Ny03Y2FiMjkxNDcyZDAifX0.WktA5tt_Tt6R-qZuTqpSB7xnYDrMlJXjz7aTzQys1UjMAEjLFHCWqmLp33DRlUboZiZQWa_6D4c6fzS-UHFQ9pQ_73s_Rg83i6XEMJIlr2k420g_cO-N_y425gnoJ2GDOpVSGxMS5uofh8JoE6OZpPNauJo_Z5MNpEKp5XZDEAE
	//
	// &{{PS256 JWT 4db66780-f363-47fd-909d-441c5fe23d61} {{1546369452 1546369392 github.com/khezen/jwc/jwt_test.go test } map[tid:b0b0ec92-ce3c-47f5-8497-7cab291472d0 aud:android.myapp.com cc:dummyCodeChallenge ccm:S256 cid:0108bfbd-2f76-422a-a3bd-2631feaacbee did:deviceID sco:offline]}}
}

func issueJWT(keyID jwc.JWKID, privateKey *rsa.PrivateKey) string {
	now := time.Now().UTC()
	nowUnix := now.Unix()
	exp := now.Add(time.Minute)
	expUnix := exp.Unix()
	jwt, err := jwc.NewJWT(
		jwc.JWTPayload{
			RegisteredClaims: jwc.RegisteredClaims{
				IssuedAtTimestamp:   nowUnix,
				ExpirationTimestamp: expUnix,
				Issuer:              "github.com/khezen/jwc/jwt_test.go",
				Subject:             "customer_id",
				Audiance:            "android.myapp.com",
			},
			PrivateClaims: jwc.PrivateClaims{
				"id":  "token_id",
				"did": "device_id",
				"sco": "offline",
				"cc":  "dummy_code_challenge",
				"ccm": "S256",
			},
		},
		jwc.PS256,
	)
	if err != nil {
		panic(err)
	}
	jwtStr, err := jwt.Encode(keyID, privateKey)
	if err != nil {
		panic(err)
	}
	return jwtStr
}

func verify(jwtStr string, jwkBytes []byte) *jwc.JWT {
	var pubJWK jwc.RSAPublicJWK
	err := json.Unmarshal(jwkBytes, &pubJWK)
	if err != nil {
		panic(err)
	}
	pubKey, err := pubJWK.PublicRSA()
	if err != nil {
		panic(err)
	}
	token, err := jwc.DecodeVerifyJWT(jwtStr, pubKey)
	if err != nil {
		panic(err)
	}
	return token
}
Output:

func DecodeJWT

func DecodeJWT(jwtStr string) (*JWT, error)

DecodeJWT - parse a base64 string into a jwt

func DecodeVerifyJWT

func DecodeVerifyJWT(jwtStr string, signKey crypto.PublicKey) (*JWT, error)

DecodeVerifyJWT - parse a base64 string into a jwt

func NewJWT

func NewJWT(payload JWTPayload, signAlgo Algorithm) (*JWT, error)

NewJWT - creates a new JWT from the payload

func (*JWT) Encode

func (jwt *JWT) Encode(signKeyID JWKID, signKey crypto.PrivateKey) (string, error)

Encode - encode jwt into signed base64 string

type JWTHeader

type JWTHeader struct {
	Algorithm   Algorithm `json:"alg"`
	Type        string    `json:"typ,omitempty"`
	ContentType string    `json:"cty,omitempty"`
	SignKeyID   JWKID     `json:"kid,omitempty"`
	JWKURI      string    `json:"jku,omitempty"`
	Critical    []string  `jon:"crit,omitempty"`
	Zip         string    `json:"zip,omitempty"`
	JWK         *JWK      `json:"jwk,omitmepty"`
}

JWTHeader JWT header

type JWTPayload

type JWTPayload struct {
	RegisteredClaims
	PrivateClaims `json:"priv"`
}

JWTPayload - JWT payload

type PrivateClaims

type PrivateClaims map[string]interface{}

PrivateClaims - app specific data embedded in JWT

type RSAPrivateJWK

type RSAPrivateJWK struct {
	JWK
	ModulusBase64           string `json:"n"`
	PublicExponentBase64    string `json:"e"`
	PrivateExponentBase64   string `json:"d"`
	FirstPrimeFactorBase64  string `json:"p"`
	SecondPrimeFactorBase64 string `json:"q"`
	// precomputed fields
	PrivateExpModFirstPrimeMinusOneBase64  string `json:"dp"` // d mod(p-1)
	PrivateExpModSecondPrimeMinusOneBase64 string `json:"dq"` // d mod(q-1)
	SecondPrimeInverseModFirstPrimeBase64  string `json:"qi"` // q^-1 mod p
}

RSAPrivateJWK - rsa private JSON web key

func RSAToPrivateJWK

func RSAToPrivateJWK(privateKey *rsa.PrivateKey, jwkID JWKID, algo Algorithm, expirationTime *time.Time) (*RSAPrivateJWK, error)

RSAToPrivateJWK - takes rsa private key and returns it as JWK

func (*RSAPrivateJWK) PrivateRSA

func (jwk *RSAPrivateJWK) PrivateRSA() (*rsa.PrivateKey, error)

PrivateRSA - takes JWK and return it as rsa private key

func (*RSAPrivateJWK) PublicKey

func (jwk *RSAPrivateJWK) PublicKey() *RSAPublicJWK

PublicKey -

type RSAPublicJWK

type RSAPublicJWK struct {
	JWK
	ModulusBase64        string `json:"n"`
	PublicExponentBase64 string `json:"e"`
}

RSAPublicJWK - rsa public JSON web key

func RSAToPublicJWK

func RSAToPublicJWK(publicKey *rsa.PublicKey, jwkID JWKID, algo Algorithm, expirationTime *time.Time) (*RSAPublicJWK, error)

RSAToPublicJWK - takes rsa public key and returns it as JWK

func (*RSAPublicJWK) PublicRSA

func (jwk *RSAPublicJWK) PublicRSA() (*rsa.PublicKey, error)

PublicRSA - takes JWK and return it as rsa public key

type RegisteredClaims

type RegisteredClaims struct {
	ExpirationTimestamp int64  `json:"exp,omitempty"`
	IssuedAtTimestamp   int64  `json:"iat,omitempty"`
	Issuer              string `json:"iss,omitempty"`
	Subject             string `json:"sub,omitempty"`
	Audiance            string `json:"aud,omitempty"`
}

RegisteredClaims - common data embedded in JWT

type TokenID

type TokenID string

TokenID -

type Usage

type Usage string

Usage -

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL