Skip to content
Daniel edited this page Feb 26, 2018 · 24 revisions

Contents

Primer

A JSON Web Signature (JWS) represents content secured with digital signatures. It provides integrity protection for this content. A JWS consists of:

  1. A JSON-based header
  2. Some arbitrary payload
  3. A signature over this header and payload

Header

The JWS Header is a JSON object specifying the algorithm used to compute the signature. Optionally it can contain additional properties of the JWS.

Example

The following header specifies that the JWS' signature is computed using the RSASSA-PKCS1-v1_5 using SHA-512 algorithm.

{ "alg": "RS512" }

A detailed list describing possible header parameters can be found here.

Payload

The JWS Payload is the data being secured by the JWS.

Example

The following string, encoded as UTF-8 data, is an example of a JWS Payload.

"Trumpets of Mexico 🏜"

Signature

The JWS Signature is a digital signature over the JWS Header and JWS Payload. It’s computed using the algorithm specified in the JWS Header.

The input to the signing function (also called signing input) is the following concatenation:

ascii(base64URL(utf8(JWS Header)) + "." + base64url(payload))

Example

The following is the signing input for the header and payload described above:

eyAiYWxnIjogIlJTNTEyIiB9.VHJ1bXBldHMgb2YgTWV4aWNvIPCfj5w

Serialization

JOSESwift implements compact serialization for JWS. Compact serialization represents a JWS as the following concatenation:

base64url(header) + "." + base64url(payload) + "." base64url(signature)

Example

Given the following header and payload:

// Header
{ "alg": "RS512" }

// Payload
"Trumpets of Mexico 🏜"

We get the following base64url encodings:

// base64url(header)
eyAiYWxnIjogIlJTNTEyIiB9

// base64url(payload)
VHJ1bXBldHMgb2YgTWV4aWNvIPCfj5w

// base64url(signature)
TwJS6...YvlTQ

Which yields the following compact serialization:

// Comapct serialized JWS
eyAiYWxnIjogIlJTNTEyIiB9.VHJ1bXBldHMgb2YgTWV4aWNvIPCfj5w.TwJS6...YvlTQ

Usage

This section describes how the above concepts are implemented and used in JOSESwift.

A JWS in JOSESwift is an immutable struct:

struct JWS {
    let header: JWSHeader
    let payload: Payload
    let signature: Data
}

You need three parts to construct a JWS instance in JOSESwift.

  1. A JWSHeader instance
  2. A Payload instance
  3. A Signer instance which computes the signature

These three parts are described in more detail in the following sections.

Header

A JWSHeader is a struct, holding a dictionary of parameters:

struct JWSHeader {
    let parameters: [String: Any]
}

For convenient use, a JWSHeader has an initializer that lets you provide the value of the algorithm parameter:

init(algorithm: SignatureAlgorithm)

To instantiate a JWSHeader you specify the signing algorithm that it should carry:

let header = JWSHeader(algorithm: .RS512)

💡 A JWS Header should also support optional additional parameters. This is currently not supported. See issue #60.

Payload

Payload has an initializer that lets you specify the data that your JWS should represent:

init(_ payload: Data)

To instantiate Payload you provide it with the data that it should carry:

let message = "Trumpets of Mexico 🏜"

let data = message.data(using: .utf8)!

let payload = Payload(data)

Signer

A Signer handles all cryptographic functionality needed to compute a JWS Signature. It computes the signing input from the header and payload. It then passes this signing input to the respective libraries providing the cryptographic primitives.

Signer provides an initializer which lets you specify the desired signing algorithm and your private key:

init?(signingAlgorithm: SignatureAlgorithm, privateKey: KeyType)

Note that the KeyType depends on the underlying crypto implementation. On iOS it’s SecKey per default. The initializer will return nil if you specify the wrong key type.

Make sure to check that you get a valid Signer instance after calling the initializer:

let privateKey: SecKey = /* ... */

guard let signer = Signer(signingAlgorithm: .RS512, privateKey: privateKey) else {
    // Wrong key type.
}

// You have a valid signer.

As a framework user, you don’t need to worry about calling the signer’s sign function. It happens automatically during the initialization of a JWS.

Instantiating a JWS

Once you have created the header, payload, and signer, you can construct a JWS using the following initializer:

init<KeyType>(header: JWSHeader, payload: Payload, signer: Signer<KeyType>) throws

Make sure to check if any errors occur during the initialization:

do {
    let jws = try JWS(header: header, payload: payload, signer: signer)
} catch {
    // Signing went wrong.
}
Clone this wiki locally