Skip to content
Daniel edited this page Oct 24, 2018 · 8 revisions

Contents

Primer

A JSON Web Key (JWK) is a JSON data structure that represents a cryptographic key.

Example

The following JSON object represents an RSA public key.

{
    "kty":"RSA",
    "n": "0vx7a...qDKgw",
    "e":"AQAB",
    "alg":"RS256",
    "kid":"2011-04-29"
}

JWK Set

A JWK Set is a JSON object that represents a set of JWKs. It has a ”keys” member. The value of this member is an array of JWKs.

Example

{
    "keys": [
        {
            "kty":"RSA",
            "n": "0vx7a...qDKgw",
            "e":"AQAB",
            "alg":"RS256",
            "kid":"2011-04-29"
        },
        {
            "kty":"RSA",
            "n": "agoeb...pJzKn",
            "e":"AQAB"
        }
    ]
}

Usage

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

JOSESwift implements JWK representation for RSA public keys and for RSA private keys without optional parameters as they are defined in PKCS#1.

JWS is a protocol with the following requirements:

protocol JWK: Codable {
    var keyType: JWKKeyType { get }
    var parameters: [String: JWKParameterType] { get }

    subscript(parameter: String) -> Any? { get }

    init(data: Data) throws

    func jsonString() -> String?
    func jsonData() -> Data?
}

Via the JWK subscript can directly access the parameters dictionary. Additionally, the JWK protocol also provides typed getters for the parameters defined in RFC-7517:

let publicKey: RSAPublicKey = /* ... */

publicKey["use"] // "sig"
publicKey.use // "sig"

type(of: publicKey["use"]) // Optional<Any>.Type
type(of: publicKey.use) // Optional<String>.Type

Key specific parameters defined in RFC-7518 are provided as parameters in the respective key types (e.g. RSAPublicKey).

RSA Keys

RSA keys are represented as structs that conform to the JWS protocol.

struct RSAPublicKey: JWK {
    let keyType: JWKKeyType
    let parameters: [String: JWKParameterType]
    let modulus: String
    let exponent: String
struct RSAPrivateKey: JWK {
    let keyType: JWKKeyType
    let parameters: [String: JWKParameterType]
    let modulus: String
    let exponent: String
    let privateExponent: String

Initializing a JWK from an Existing RSA Key

The following examples use RSA public keys. If not noted otherwise, the functionality is similar for RSA private keys.

RSAPublicKey provides an initializer expecting an existing public key as a parameter. This key has to conform to ExpressibleAsRSAPublicKeyComponents. Optionally additional JWK parameters can be supplied.

init(publicKey: ExpressibleAsRSAPublicKeyComponents, additionalParameters parameters: [String: String] = [:]) throws

By default, Data containing an RSA public key in PKCS#1 format and iOS’s SecKey conform to ExpressibleAsRSAPublicKeyComponents. Of course, you can extend your own custom key types to conform to this protocol. It has the following requirements:

public protocol ExpressibleAsRSAPublicKeyComponents {
    // Constucts an instance of it's type from the given components.
    static func representing(rsaPublicKeyComponents components: RSAPublicKeyComponents) throws -> Self

    // Converts the key to modulus and exponent.
    func rsaPublicKeyComponents() throws -> RSAPublicKeyComponents
}

SecKey already conforms to ExpressibleAsRSAPublicKeyComponents. Instantiating an RSAPublicKey is done using:

let publicKey: SecKey = /* ... */

let jwk = try! RSAPublicKey(publicKey: publicKey)

You can then get the JWK JSON object from this RSAPublicKey like this:

let json = jwk.jsonString()! // "{"kty": "RSA", "n": "MHZ4L...uS2d3", "e": "QVFBQg"}"

Or like this:

let json = jwk.jsonData()!

JWK requires conformance to Codable. This is possible as well:

 let json = try! JSONEncoder().encode(jwk)

💡 Note that SecKey and Data do not already conform to ExpressibleAsRSAPrivateKeyComponents.


Parsing a JWK Into Another Key Type

The following examples use RSA public keys. If not noted otherwise, the functionality is similar for RSA private keys.

RSAPublicKey provides an initializer expecting JSON data representing a JWK as a parameter.

init(data: Data) throws

You can get a RSAPublicKey instance from JWK data like this:

let json: Data = /* ... */

let jwk = try! RSAPublicKey(data: json)

JWK requires conformance to Codable. This is possible as well:

let jwk = try JSONDecoder().decode(RSAPublicKey.self, from: json)

You can then convert this RSAPublicKey to any type that conforms to ExpressibleAsRSAPublicKeyComponents. For more details regarding this protocol, see Initializing a JWK from an Existing RSA Key.

let publicKey: SecKey = try! jwk.converted(to: SecKey.self)

Or:

// Data will be in PKCS#1 format.
let publicKey: Data = try! jwk.converted(to: Data.self)

💡 Note that SecKey and Data do not already conform to ExpressibleAsRSAPrivateKeyComponents.