Documentation ¶
Overview ¶
Package jws implements the digital signature on JSON based data structures as described in https://tools.ietf.org/html/rfc7515
If you do not care about the details, the only things that you would need to use are the following functions:
jws.Sign(payload, jws.WithKey(algorithm, key)) jws.Verify(serialized, jws.WithKey(algorithm, key))
To sign, simply use `jws.Sign`. `payload` is a []byte buffer that contains whatever data you want to sign. `alg` is one of the jwa.SignatureAlgorithm constants from package jwa. For RSA and ECDSA family of algorithms, you will need to prepare a private key. For HMAC family, you just need a []byte value. The `jws.Sign` function will return the encoded JWS message on success.
To verify, use `jws.Verify`. It will parse the `encodedjws` buffer and verify the result using `algorithm` and `key`. Upon successful verification, the original payload is returned, so you can work on it.
Index ¶
- Constants
- func AlgorithmsForKey(key interface{}) ([]jwa.SignatureAlgorithm, error)
- func Compact(msg *Message, options ...CompactOption) ([]byte, error)
- func RegisterCustomField(name string, object interface{})
- func RegisterSigner(alg jwa.SignatureAlgorithm, f SignerFactory)
- func RegisterVerifier(alg jwa.SignatureAlgorithm, f VerifierFactory)
- func Sign(payload []byte, options ...SignOption) ([]byte, error)
- func SplitCompact(src []byte) ([]byte, []byte, []byte, error)
- func SplitCompactReader(rdr io.Reader) ([]byte, []byte, []byte, error)
- func SplitCompactString(src string) ([]byte, []byte, []byte, error)
- func Verify(buf []byte, options ...VerifyOption) ([]byte, error)
- type CompactOption
- type DecodeCtx
- type HMACSigner
- type HMACVerifier
- type HeaderPair
- type Headers
- type Iterator
- type KeyProvider
- type KeyProviderFunc
- type KeySink
- type Message
- func (m *Message) AppendSignature(v *Signature) *Message
- func (m *Message) ClearSignatures() *Message
- func (m *Message) DecodeCtx() DecodeCtx
- func (m Message) LookupSignature(kid string) []*Signature
- func (m Message) MarshalJSON() ([]byte, error)
- func (m Message) Payload() []byte
- func (m *Message) SetDecodeCtx(dc DecodeCtx)
- func (m *Message) SetPayload(v []byte) *Message
- func (m Message) Signatures() []*Signature
- func (m *Message) UnmarshalJSON(buf []byte) error
- type Option
- type ParseOption
- type ReadFileOption
- type SignOption
- type SignVerifyOption
- type Signature
- func (s *Signature) DecodeCtx() DecodeCtx
- func (s Signature) ProtectedHeaders() Headers
- func (s Signature) PublicHeaders() Headers
- func (s *Signature) SetDecodeCtx(dc DecodeCtx)
- func (s *Signature) SetProtectedHeaders(v Headers) *Signature
- func (s *Signature) SetPublicHeaders(v Headers) *Signature
- func (s *Signature) SetSignature(v []byte) *Signature
- func (s *Signature) Sign(payload []byte, signer Signer, key interface{}) ([]byte, []byte, error)
- func (s Signature) Signature() []byte
- func (s *Signature) UnmarshalJSON(data []byte) error
- type Signer
- type SignerFactory
- type SignerFactoryFn
- type Verifier
- type VerifierFactory
- type VerifierFactoryFn
- type VerifyOption
- func WithContext(v context.Context) VerifyOption
- func WithKeyProvider(v KeyProvider) VerifyOption
- func WithKeySet(set jwk.Set, options ...WithKeySetSuboption) VerifyOption
- func WithKeyUsed(v interface{}) VerifyOption
- func WithMessage(v *Message) VerifyOption
- func WithVerifyAuto(f jwk.Fetcher, options ...jwk.FetchOption) VerifyOption
- type Visitor
- type VisitorFunc
- type WithJSONSuboption
- type WithKeySetSuboption
- type WithKeySuboption
Constants ¶
const ( AlgorithmKey = "alg" ContentTypeKey = "cty" CriticalKey = "crit" JWKKey = "jwk" JWKSetURLKey = "jku" KeyIDKey = "kid" TypeKey = "typ" X509CertChainKey = "x5c" X509CertThumbprintKey = "x5t" X509CertThumbprintS256Key = "x5t#S256" X509URLKey = "x5u" )
Variables ¶
This section is empty.
Functions ¶
func AlgorithmsForKey ¶
func AlgorithmsForKey(key interface{}) ([]jwa.SignatureAlgorithm, error)
AlgorithmsForKey returns the possible signature algorithms that can be used for a given key. It only takes in consideration keys/algorithms for verification purposes, as this is the only usage where one may need dynamically figure out which method to use.
func Compact ¶
func Compact(msg *Message, options ...CompactOption) ([]byte, error)
Compact generates a JWS message in compact serialization format from `*jws.Message` object. The object contain exactly one signature, or an error is returned.
If using a detached payload, the payload must already be stored in the `*jws.Message` object, and the `jws.WithDetached()` option must be passed to the function.
func RegisterCustomField ¶
func RegisterCustomField(name string, object interface{})
RegisterCustomField allows users to specify that a private field be decoded as an instance of the specified type. This option has a global effect.
For example, suppose you have a custom field `x-birthday`, which you want to represent as a string formatted in RFC3339 in JSON, but want it back as `time.Time`.
In that case you would register a custom field as follows
jwe.RegisterCustomField(`x-birthday`, timeT)
Then `hdr.Get("x-birthday")` will still return an `interface{}`, but you can convert its type to `time.Time`
bdayif, _ := hdr.Get(`x-birthday`) bday := bdayif.(time.Time)
func RegisterSigner ¶
func RegisterSigner(alg jwa.SignatureAlgorithm, f SignerFactory)
RegisterSigner is used to register a factory object that creates Signer objects based on the given algorithm.
For example, if you would like to provide a custom signer for jwa.EdDSA, use this function to register a `SignerFactory` (probably in your `init()`)
func RegisterVerifier ¶
func RegisterVerifier(alg jwa.SignatureAlgorithm, f VerifierFactory)
RegisterVerifier is used to register a factory object that creates Verifier objects based on the given algorithm.
For example, if you would like to provide a custom verifier for jwa.EdDSA, use this function to register a `VerifierFactory` (probably in your `init()`)
func Sign ¶
func Sign(payload []byte, options ...SignOption) ([]byte, error)
Sign generates a JWS message for the given payload and returns it in serialized form, which can be in either compact or JSON format. Default is compact.
You must pass at least one key to `jws.Sign()` by using `jws.WithKey()` option.
jws.Sign(payload, jws.WithKey(alg, key)) jws.Sign(payload, jws.WithJSON(), jws.WithKey(alg1, key1), jws.WithKey(alg2, key2))
Note that in the second example the `jws.WithJSON()` option is specified as well. This is because the compact serialization format does not support multiple signatures, and users must specifically ask for the JSON serialization format.
Read the documentation for `jws.WithKey()` to learn more about the possible values that can be used for `alg` and `key`.
If you want to use a detached payload, use `jws.WithDetachedPayload()` as one of the options. When you use this option, you must always set the first parameter (`payload`) to `nil`, or the function will return an error
You may also wantt to look at how to pass protected headers to the signing process, as you will likely be required to set the `b64` field when using detached payload.
Look for options that return `jws.SignOption` or `jws.SignVerifyOption` for a complete list of options that can be passed to this function.
func SplitCompact ¶
SplitCompact splits a JWT and returns its three parts separately: protected headers, payload and signature.
func SplitCompactReader ¶
SplitCompactReader splits a JWT and returns its three parts separately: protected headers, payload and signature.
func SplitCompactString ¶
SplitCompactString splits a JWT and returns its three parts separately: protected headers, payload and signature.
func Verify ¶
func Verify(buf []byte, options ...VerifyOption) ([]byte, error)
Verify checks if the given JWS message is verifiable using `alg` and `key`. `key` may be a "raw" key (e.g. rsa.PublicKey) or a jwk.Key
If the verification is successful, `err` is nil, and the content of the payload that was signed is returned. If you need more fine-grained control of the verification process, manually generate a `Verifier` in `verify` subpackage, and call `Verify` method on it. If you need to access signatures and JOSE headers in a JWS message, use `Parse` function to get `Message` object.
Types ¶
type CompactOption ¶
type CompactOption interface { Option // contains filtered or unexported methods }
CompactOption describes options that can be passed to `jws.Compact`
func WithDetached ¶
func WithDetached(v bool) CompactOption
WithDetached specifies that the `jws.Message` should be serialized in JWS compact serialization with detached payload. The resulting octet sequence will not contain the payload section.
type HMACSigner ¶
type HMACSigner struct {
// contains filtered or unexported fields
}
HMACSigner uses crypto/hmac to sign the payloads.
func (HMACSigner) Algorithm ¶
func (s HMACSigner) Algorithm() jwa.SignatureAlgorithm
type HMACVerifier ¶
type HMACVerifier struct {
// contains filtered or unexported fields
}
func (HMACVerifier) Verify ¶
func (v HMACVerifier) Verify(payload, signature []byte, key interface{}) (err error)
type HeaderPair ¶
type Headers ¶
type Headers interface { json.Marshaler json.Unmarshaler Algorithm() jwa.SignatureAlgorithm ContentType() string Critical() []string JWK() jwk.Key JWKSetURL() string KeyID() string Type() string X509CertChain() *cert.Chain X509CertThumbprint() string X509CertThumbprintS256() string X509URL() string Iterate(ctx context.Context) Iterator Walk(context.Context, Visitor) error AsMap(context.Context) (map[string]interface{}, error) Copy(context.Context, Headers) error Merge(context.Context, Headers) (Headers, error) Get(string) (interface{}, bool) Set(string, interface{}) error Remove(string) error // PrivateParams returns the non-standard elements in the source structure // WARNING: DO NOT USE PrivateParams() IF YOU HAVE CONCURRENT CODE ACCESSING THEM. // Use AsMap() to get a copy of the entire header instead PrivateParams() map[string]interface{} }
Headers describe a standard Header set.
func NewHeaders ¶
func NewHeaders() Headers
type KeyProvider ¶
KeyProvider is responsible for providing key(s) to sign or verify a payload. Multiple `jws.KeyProvider`s can be passed to `jws.Verify()` or `jws.Sign()`
`jws.Sign()` can only accept static key providers via `jws.WithKey()`, while `jws.Verify()` can accept `jws.WithKey()`, `jws.WithKeySet()`, `jws.WithVerifyAuto()`, and `jws.WithKeyProvider()`.
Understanding how this works is crucial to learn how this package works.
`jws.Sign()` is straightforward: signatures are created for each provided key.
`jws.Verify()` is a bit more involved, because there are cases you will want to compute/deduce/guess the keys that you would like to use for verification.
The first thing that `jws.Verify()` does is to collect the KeyProviders from the option list that the user provided (presented in pseudocode):
keyProviders := filterKeyProviders(options)
Then, remember that a JWS message may contain multiple signatures in the message. For each signature, we call on the KeyProviders to give us the key(s) to use on this signature:
for sig in msg.Signatures { for kp in keyProviders { kp.FetcKeys(ctx, sink, sig, msg) ... } }
The `sink` argument passed to the KeyProvider is a temporary storage for the keys (either a jwk.Key or a "raw" key). The `KeyProvider` is responsible for sending keys into the `sink`.
When called, the `KeyProvider` created by `jws.WithKey()` sends the same key, `jws.WithKeySet()` sends keys that matches a particular `kid` and `alg`, `jws.WithVerifyAuto()` fetchs a JWK from the `jku` URL, and finally `jws.WithKeyProvider()` allows you to execute arbitrary logic to provide keys. If you are providing a custom `KeyProvider`, you should execute the necessary checks or retrieval of keys, and then send the key(s) to the sink:
sink.Key(alg, key)
These keys are then retrieved and tried for each signature, until a match is found:
keys := sink.Keys() for key in keys { if givenSignature == makeSignatre(key, payload, ...)) { return OK } }
type KeyProviderFunc ¶
KeyProviderFunc is a type of KeyProvider that is implemented by a single function. You can use this to create ad-hoc `KeyProvider` instances.
type KeySink ¶
type KeySink interface {
Key(jwa.SignatureAlgorithm, interface{})
}
KeySink is a data storage where `jws.KeyProvider` objects should send their keys to.
type Message ¶
type Message struct {
// contains filtered or unexported fields
}
Message represents a full JWS encoded message. Flattened serialization is not supported as a struct, but rather it's represented as a Message struct with only one `signature` element.
Do not expect to use the Message object to verify or construct a signed payload with. You should only use this when you want to actually programmatically view the contents of the full JWS payload.
As of this version, there is one big incompatibility when using Message objects to convert between compact and JSON representations. The protected header is sometimes encoded differently from the original message and the JSON serialization that we use in Go.
For example, the protected header `eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9` decodes to
{"typ":"JWT", "alg":"HS256"}
However, when we parse this into a message, we create a jws.Header object, which, when we marshal into a JSON object again, becomes
{"typ":"JWT","alg":"HS256"}
Notice that serialization lacks a line break and a space between `"JWT",` and `"alg"`. This causes a problem when verifying the signatures AFTER a compact JWS message has been unmarshaled into a jws.Message.
jws.Verify() doesn't go through this step, and therefore this does not manifest itself. However, you may see this discrepancy when you manually go through these conversions, and/or use the `jwx` tool like so:
jwx jws parse message.jws | jwx jws verify --key somekey.jwk --stdin
In this scenario, the first `jwx jws parse` outputs a parsed jws.Message which is marshaled into JSON. At this point the message's protected headers and the signatures don't match.
To sign and verify, use the appropriate `Sign()` and `Verify()` functions.
func NewMessage ¶
func NewMessage() *Message
func Parse ¶
func Parse(src []byte, _ ...ParseOption) (*Message, error)
Parse parses contents from the given source and creates a jws.Message struct. The input can be in either compact or full JSON serialization.
Parse() currently does not take any options, but the API accepts it in anticipation of future addition.
func ParseReader ¶
Parse parses contents from the given source and creates a jws.Message struct. The input can be in either compact or full JSON serialization.
func ParseString ¶
Parse parses contents from the given source and creates a jws.Message struct. The input can be in either compact or full JSON serialization.
func (*Message) AppendSignature ¶
func (*Message) ClearSignatures ¶
func (Message) LookupSignature ¶
LookupSignature looks up a particular signature entry using the `kid` value
func (Message) MarshalJSON ¶
func (*Message) SetDecodeCtx ¶
func (*Message) SetPayload ¶
func (Message) Signatures ¶
func (*Message) UnmarshalJSON ¶
type ParseOption ¶
type ParseOption interface { Option // contains filtered or unexported methods }
ReadFileOption is a type of `Option` that can be passed to `jwe.Parse`
type ReadFileOption ¶
type ReadFileOption interface { Option // contains filtered or unexported methods }
ReadFileOption is a type of `Option` that can be passed to `jws.ReadFile`
func WithFS ¶
func WithFS(v fs.FS) ReadFileOption
WithFS specifies the source `fs.FS` object to read the file from.
type SignOption ¶
type SignOption interface { Option // contains filtered or unexported methods }
SignOption describes options that can be passed to `jws.Sign`
func WithCompact ¶
func WithCompact() SignOption
WithCompact specifies that the result of `jws.Sign()` is serialized in compact format.
By default `jws.Sign()` will opt to use compact format, so you usually do not need to specify this option other than to be explicit about it
func WithHeaders ¶
func WithHeaders(h Headers) SignOption
WithHeaders allows you to specify extra header values to include in the final JWS message
func WithJSON ¶
func WithJSON(options ...WithJSONSuboption) SignOption
WithJSON specifies that the result of `jws.Sign()` is serialized in JSON format.
If you pass multiple keys to `jws.Sign()`, it will fail unless you also pass this option.
type SignVerifyOption ¶
type SignVerifyOption interface { Option // contains filtered or unexported methods }
SignVerifyOption describes options that can be passed to either `jws.Verify` or `jws.Sign`
func WithDetachedPayload ¶
func WithDetachedPayload(v []byte) SignVerifyOption
WithDetachedPayload can be used to both sign or verify a JWS message with a detached payload.
When this option is used for `jws.Sign()`, the first parameter (normally the payload) must be set to `nil`.
If you have to verify using this option, you should know exactly how and why this works.
func WithKey ¶
func WithKey(alg jwa.KeyAlgorithm, key interface{}, options ...WithKeySuboption) SignVerifyOption
WithKey is used to pass a static algorithm/key pair to either `jws.Sign()` or `jws.Verify()`.
The `alg` parameter is the identifier for the signature algorithm that should be used. It is of type `jwa.KeyAlgorithm` but in reality you can only pass `jwa.SignatureAlgorithm` types. It is this way so that the value in `(jwk.Key).Algorithm()` can be directly passed to the option. If you specify other algorithm types such as `jwa.ContentEncryptionAlgorithm`, then you will get an error when `jws.Sign()` or `jws.Verify()` is executed.
The algorithm specified in the `alg` parameter must be able to support the type of key you provided, otherwise an error is returned.
Any of the followin is accepted for the `key` parameter: * A "raw" key (e.g. rsa.PrivateKey, ecdsa.PrivateKey, etc) * A crypto.Signer * A jwk.Key
A `crypto.Signer` is used when the private part of a key is kept in an inaccessible location, such as hardware. `crypto.Signer` is currently supported for RSA, ECDSA, and EdDSA family of algorithms. You may consider using `github.com/jwx-go/crypto-signer` if you would like to use keys stored in GCP/AWS KMS services.
If the key is a jwk.Key and the key contains a key ID (`kid` field), then it is added to the protected header generated by the signature.
`jws.WithKey()` can furher accept suboptions to change signing behavior when used with `jws.Sign()`. `jws.WithProtected()` and `jws.WithPublic()` can be passed to specify JWS headers that should be used whe signing.
If the protected headers contain "b64" field, then the boolean value for the field is respected when serializing. That is, if you specify a header with `{"b64": false}`, then the payload is not base64 encoded.
These suboptions are ignored whe the `jws.WithKey()` option is used with `jws.Verify()`.
type Signature ¶
type Signature struct {
// contains filtered or unexported fields
}
func NewSignature ¶
func NewSignature() *Signature
func (Signature) ProtectedHeaders ¶
func (Signature) PublicHeaders ¶
func (*Signature) SetDecodeCtx ¶
func (*Signature) SetProtectedHeaders ¶
func (*Signature) SetPublicHeaders ¶
func (*Signature) SetSignature ¶
func (*Signature) Sign ¶
Sign populates the signature field, with a signature generated by given the signer object and payload.
The first return value is the raw signature in binary format. The second return value s the full three-segment signature (e.g. "eyXXXX.XXXXX.XXXX")
func (*Signature) UnmarshalJSON ¶
type Signer ¶
type Signer interface { // Sign creates a signature for the given payload. // The second argument is the key used for signing the payload, and is usually // the private key type associated with the signature method. For example, // for `jwa.RSXXX` and `jwa.PSXXX` types, you need to pass the // `*"crypto/rsa".PrivateKey` type. // Check the documentation for each signer for details Sign([]byte, interface{}) ([]byte, error) Algorithm() jwa.SignatureAlgorithm }
Signer generates the signature for a given payload.
type SignerFactory ¶
type SignerFactoryFn ¶
func (SignerFactoryFn) Create ¶
func (fn SignerFactoryFn) Create() (Signer, error)
type Verifier ¶
type Verifier interface { // Verify checks whether the payload and signature are valid for // the given key. // `key` is the key used for verifying the payload, and is usually // the public key associated with the signature method. For example, // for `jwa.RSXXX` and `jwa.PSXXX` types, you need to pass the // `*"crypto/rsa".PublicKey` type. // Check the documentation for each verifier for details Verify(payload []byte, signature []byte, key interface{}) error }
func NewVerifier ¶
func NewVerifier(alg jwa.SignatureAlgorithm) (Verifier, error)
NewVerifier creates a verifier that signs payloads using the given signature algorithm.
type VerifierFactory ¶
type VerifierFactoryFn ¶
func (VerifierFactoryFn) Create ¶
func (fn VerifierFactoryFn) Create() (Verifier, error)
type VerifyOption ¶
type VerifyOption interface { Option // contains filtered or unexported methods }
VerifyOption describes options that can be passed to `jws.Verify`
func WithContext ¶
func WithContext(v context.Context) VerifyOption
func WithKeyProvider ¶
func WithKeyProvider(v KeyProvider) VerifyOption
func WithKeySet ¶
func WithKeySet(set jwk.Set, options ...WithKeySetSuboption) VerifyOption
WithKeySet specifies a JWKS (jwk.Set) to use for verification.
By default both `alg` and `kid` fields in the JWS _and_ the key must match for a key in the JWKS to be considered to be used.
The behavior can be tweaked by using the `jws.WithKeySetSuboption` suboption types.
func WithKeyUsed ¶
func WithKeyUsed(v interface{}) VerifyOption
WithKeyUsed allows you to specify the `jws.Verify()` function to return the key used for verification. This may be useful when you specify multiple key sources or if you pass a `jwk.Set` and you want to know which key was successful at verifying the signature.
`v` must be a pointer to an empty `interface{}`. Do not use `jwk.Key` here unless you are 100% sure that all keys that you have provided are instances of `jwk.Key` (remember that the jwx API allows users to specify a raw key such as *rsa.PublicKey)
func WithMessage ¶
func WithMessage(v *Message) VerifyOption
WithMessage can be passed to Verify() to obtain the jws.Message upon a successful verification.
func WithVerifyAuto ¶
func WithVerifyAuto(f jwk.Fetcher, options ...jwk.FetchOption) VerifyOption
type Visitor ¶
type Visitor = iter.MapVisitor
type VisitorFunc ¶
type VisitorFunc = iter.MapVisitorFunc
type WithJSONSuboption ¶
type WithJSONSuboption interface { Option // contains filtered or unexported methods }
JSONSuboption describes suboptions that can be passed to `jws.WithJSON()` option
func WithPretty ¶
func WithPretty(v bool) WithJSONSuboption
WithPretty specifies whether the JSON output should be formatted and indented
type WithKeySetSuboption ¶
type WithKeySetSuboption interface { Option // contains filtered or unexported methods }
WithKeySetSuboption is a suboption passed to the `jws.WithKeySet()` option
func WithInferAlgorithmFromKey ¶
func WithInferAlgorithmFromKey(v bool) WithKeySetSuboption
WithInferAlgorithmFromKey specifies whether the JWS signing algorithm name should be inferred by looking at the provided key, in case the JWS message or the key does not have a proper `alg` header.
Compared to providing explicit `alg` from the key this is slower, and verification may fail to verify if some how our heuristics are wrong or outdated.
Also, automatic detection of signature verification methods are always more vulnerable for potential attack vectors.
It is highly recommended that you fix your key to contain a proper `alg` header field instead of resorting to using this option, but sometimes it just needs to happen.
func WithMultipleKeysPerKeyID ¶ added in v2.0.4
func WithMultipleKeysPerKeyID(v bool) WithKeySetSuboption
WithMultipleKeysPerKeyID specifies if we should expect multiple keys to match against a key ID. By default it is assumed that key IDs are unique, i.e. for a given key ID, the key set only contains a single key that has the matching ID. When this option is set to true, multiple keys that match the same key ID in the set can be tried.
func WithRequireKid ¶
func WithRequireKid(v bool) WithKeySetSuboption
WithRequiredKid specifies whether the keys in the jwk.Set should only be matched if the target JWS message's Key ID and the Key ID in the given key matches.
func WithUseDefault ¶
func WithUseDefault(v bool) WithKeySetSuboption
WithUseDefault specifies that if and only if a jwk.Key contains exactly one jwk.Key, that tkey should be used. (I think this should be removed)
type WithKeySuboption ¶
type WithKeySuboption interface { Option // contains filtered or unexported methods }
WithKeySuboption describes option types that can be passed to the `jws.WithKey()` option.
func WithProtectedHeaders ¶
func WithProtectedHeaders(v Headers) WithKeySuboption
WithProtected is used with `jws.WithKey()` option when used with `jws.Sign()` to specify a protected header to be attached to the JWS signature.
It has no effect if used when `jws.WithKey()` is passed to `jws.Verify()`
func WithPublicHeaders ¶
func WithPublicHeaders(v Headers) WithKeySuboption
WithPublic is used with `jws.WithKey()` option when used with `jws.Sign()` to specify a public header to be attached to the JWS signature.
It has no effect if used when `jws.WithKey()` is passed to `jws.Verify()`
`jws.Sign()` will result in an error if `jws.WithPublic()` is used and the serialization format is compact serialization.