This document describes a mechanism to add enveloped signatures to JSON objects. This mechanism combines the signing and key management of JWS with a subset of the features of XML Signatures. Unlike JWS, it supports multiple signatures, selective signatures over only part of the information in the document, and canonicalization.

This specification is designed to be usable to sign WoT Thing Descriptions [[WOT-THING-DESCRIPTION]], although it is not limited to this use case. An ontology is also defined so it can be used with JSON-LD. However, it is designed to only require JSON processing, not RDF.

Introduction

To do. Examples should go here.

Vocabulary Definitions

This section defines a Signature object and signing process that can be used to preserve the integrity of all or part of a JSON object.

SignedInfo

A collection of information to be protected by a Signature, including a reference (which may be one of several query types) to a subset of the enclosing JSON object, a digest (hash) of the reference result, and an identification of the digest algorithms used.

Vocabulary term Description Assignment Type
reference Pointer or query string identifying the subset of the enclosing JSON object to sign. mandatory string
referenceType The type of reference used to define the subset of the enclosing JSON object to sign. Can be one of jsonpointer, jsonpath, or xpath. mandatory string
digest A digest value computed from the result of the specified reference. mandatory string
digestAlg The algorithm used to compute the digest value. One of sha256, sha384, or sha512. mandatory string

Signature

A Signature object includes an array of SignedInfo objects and an encrypted value which can be used to confirm that the content of this array and the information it references is identical to that which was present when the Signature object was created.

Vocabulary term Description Assignment Type
signedInfo Information to be signed. mandatory SignedInfo or Array of SignedInfo
sig JWS [[RFC7515]] signature value computed using the JSON canonicalized serialization of the signedInfo field as payload and header set using corresponding fields from the Signature object. mandatory string
alg The algorithm used to compute the signature value. Can be any digital signature or MAC algorithm specified in either [[RFC7518]] or [[RFC8037]] corresponding to values of alg allowable in JWS [[RFC7515]]. These include HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES384, ES512, PS256, PS384, or PS512 from [[RFC7518]] and Ed25519 or Ed448 from [[RFC8037]]. Considered part of the JWS header used for computing the signature value. mandatory string
jku A URI referring to a JWK Set [[RFC7517]]. Considered part of the JWS header used for computing the signature value. mandatory anyURI
kid An identifier specifying an element of a JWK Set [[RFC7517]]. Considered part of the JWS header used for computing the signature value. mandatory string

Processing

Signature Generation

Signature objects are used to verify the integrity of a specified subset of the information contained in a JSON object. Generally speaking, Signature objects follow the structure of XML Signatures [[xmldsig-core1]] to enable flexible extraction of the subset of information to be signed, that is, the payload. However, signature algorithms are drawn from JSON Web Algorithms (JWA) [[RFC7518]] and extensions [[RFC8037]] consistent with their use in JSON Web Signatures (JWS) [[RFC7515]]. The signature value itself is computed by constructing a JWS header from the information contained in the Signature object and using it to sign the specified payload.

Each Signature object and the array containing all signatures MUST be computed (and the array extended) as described below:

  1. As input, the signer MUST construct a template for a Signature object, with all mandatory fields filled in except for digest fields and sig values. If a key needs to be specfied, then appropriate values for both jku and kid fields should be given. If values for these fields are not given, then the key will have to be known to the Consumer by context.
  2. The template Signature object MUST then be inserted into the JSON object to be signed, at the end of the signatures array, creating a new array if necessary. This allows SignedInfo sections of the template to optionally refer to (and therefore protect, if necessary) elements of the Signature object itself, such as the definition of the signature algorithm.
  3. (Fragments are canonicalized below; this assertion covers the signature block itself) The JSON object serialization must be expressed as in JCS (canonical) form.
  4. (Move to TD Canonical form) References to named objects MUST be expanded. Specifically, whenever a security or schema refers to an object defined in a securityDefinition or schemaDefinition, the string (and its enclosing quotes) must be replaced with the object it refers to.
  5. (Move to TD Canonical form) Prefixes MUST all be expanded into URLs wherever they are used. Note: the @context object should be retained as is.
  6. All reference queries MUST be applied to the output of the previous step to extract a result for each SignedInfo object. Each reference result may be a JSON fragment or a JSON element, and MUST be transformed into JSON Canonical Serialization [[RFC8785]] form.
  7. A digest value MUST be computed using the specified digestAlg from the output of the previous step for each SignedInfo object. These values are then inserted into the corresponding SignedInfo objects.
  8. The array of SignedInfo objects MUST be serialized in JSON Canonical Serialization [[RFC8785]] form. This string will be used as the JWS payload.
  9. A JSON object MUST be created using the alg value as well as the jku and kid values, if specified, from the Signature object, using corresponding member names, and then serialized in JSON Canonical Serialization [[RFC8785]] form. This string will be used as the JWS header.
  10. Using the header and the payload computed in the previous steps, the signature value MUST then computed as specified for JSON Web Signatures [[RFC7515]]. the resulting string to compute a signature value. This value is then inserted into the sig field of the template Signature object, which is now complete, with all mandatory elements specified.
  11. The completed Signature object MUST be inserted into the JSON object, replacing the already inserted template object.

Signature Verification

To verify a signature, the following process MUST be followed:

  1. The identified Signature object and all Signature objects following it in the array associated with the signatures member MUST be removed from the JSON object. If the resulting array is empty it should be removed.
  2. A template Signature object MUST be reconstructed by deleting the digest and sig fields and reinserting it at the end of the signatures array, (re)creating the signatures array if necessary.
  3. (Move to TD specification) The TD serialization MUST be expressed as a Canonical TD, with the same validation exception for the most recent Signature object being incomplete noted above.
  4. (Move to TD specification) References to named objects MUST be replaced with their referents as described for the signing process.
  5. (Move to TD specification) References to prefixes MUST be replaced with their referents as described for the signing process.
  6. All reference queries MUST be performed as described for the signing process.
  7. The digest computation MUST be performed as described for the signing process.
  8. The sig computation MUST be performed as described for the signing process, and the validation process appropriate for the signature type performed as specified in JWS [[RFC7515]], JWA [[RFC7518]] and extensions [[RFC8037]]. Note that in general validation is not as simple as comparing signature values. The specific validation process for each signature type needs to be followed as specified, and may require additional inputs, such as keys.

The reference values in SignedInfo objects can include previous Signature objects, allowing signatures to be chained. Inserting the new Signature object at the end of any existing signatures array makes it easier to stably select existing signatures using references such as JSON Pointers.

A parallel effort is being made to define signatures for RDF datasets, called Linked Data Signatures (LDS). This can be applied to JSON-LD files. The current LDS proposal, however, would require the conversion of a JSON-LD file into a sorted set of RDF triples, and so would require RDF processing. The current definition of EJS Signatures requires only JSON processing, so we expect both to be useful in different circumstances.

Recent Specification Changes

Initial draft.

Acknowledgements

Thanks to Ege Korkan and Oliver Pfaff for providing detailed feedback on the initial draft.

Temporary ReSpec fix regarding non-listed references: [[RFC6068]], [[RFC3966]], [[html]], [[RFC6750]], [[RFC7519]], [[RFC7797]], [[RFC8392]], [[RFC7516]], [[LDML]], [[SEMVER]], [[RFC7617]], [[RFC7616]]