In this post, we explore following alternatives available to sign a JSON message and then build a comparison between each of them:
- JSON Web Signature (JWS)
- JSON Cleartext Signature (JCS)
- Concise Binary Object Representation (CBOR) Object Signing
We discussed JSON Web Signature (JWS) in detail, in the medium post, JWT, JWS and JWE for Not So Dummies!. JWS is an IETF standard (RFC 7516), which defines how to sign any payload (not necessarily JSON, it can be even XML) and represent the signed payload along with the signature and the associated metadata in one of the two serialized formats: JSON serialization or compact serialization. JWS does not worry about canonicalization — in fact, it does not have to. In its serialized form, the original payload is included as it is (to be precise base64url-encoded).
Let’s quickly have a look at both the JWS serialized formats:
There are multiple libraries, which support JWS:
- Nimbus (Java): http://connect2id.com/products/nimbus-jose-jwt
- Nuget (.NET): http://www.nuget.org/packages/jose-jwt/
- pyjwt (Python): https://github.com/jpadilla/pyjwt/
- php-jwt (PHP): https://github.com/firebase/php-jwt
JCS is a scheme for signing data expressed as JSON (RFC 7159) objects. It is loosely modeled after XML Signature “Enveloped” signatures. Compared to its XML counterpart JCS is quite primitive but on the other hand it has proved to be simple to implement and use.
The XML Signature specification, which was developed under the W3C introduced 3 types signatures: Enveloped, Enveloping and Detached.
Under the Enveloped signature, the signature of the signed payload (xml) is embedded into the payload itself. In other words, the <Signature> element, which holds the actual signature and the related metadata becomes a child element of the xml payload to be signed.
Under the Enveloping signature, the signature of the signed payload (xml) embeds the payload into itself. In other words, the <Signature> element, which holds the actual signature and the related metadata becomes the parent element of the xml payload to be signed.
Under the Detached signature, the signature of the signed payload (xml) is detached from the payload itself. In other words, the <Signature> element, which holds the actual signature and the related metadata is neither a parent element nor a child element of the xml payload to be signed.
In the end of 2013, JCS was developed by Anders Rundgren as a part of the OpenKeyStore project, where the signature is encoded much like an enveloped XML signature, just all in JSON. Unlike IETF’s JWS (JOSE), JCS was designed to be an integral part of a JSON object rather than embedding the signed data.
Under JCS, the JSON message should be canonicalized first, prior to signing. The canonicalization rules are defined here: https://cyberphone.github.io/openkeystore/resources/docs/jcs.html.
Whitespace must be removed which in practical terms means removal of all characters outside of quoted strings having a value of x09, x0a, x0d or x20.
JSON ‘\/’ escape sequences must be honored on input within quoted strings but be treated as a “degenerate” equivalents to ‘/’ by rewriting them.
Unicode escape sequences (‘\uhhhh’) within quoted strings must be adjusted as follows: If the Unicode value falls within the traditional ASCII control character range (0x00–0x1f), it must be rewritten in lower-case hexadecimal notation unless it is one of the pre-defined JSON escapes (‘\n’ etc.) because the latter have precedence. If the Unicode value is outside of the ASCII control character range, it must be replaced by the corresponding Unicode character with the exception of ‘”’ and ‘\’ which always must be escaped as well.
The JSON object associated with the signature must now be recreated using the actual text left after applying the previous measures. Rationale: JSON numbers are ambiguously defined (“unnormalized”) which means that a decoding/encoding sequence may produce a different representation compared to the original. As an example, floating point data is often expressed like 4.50 in spite of the trailing zero being redundant. To cope with this potential problem, compliant parsers must preserve the original textual representation of properties internally in order to support JCS normalization requirements.
JCS does not have many supporting implementations. The JCS reference implementation is available at https://github.com/cyberphone/openkeystore.
The Concise Binary Object Representation (CBOR) is a data format whose design goals include the possibility of extremely small code size, fairly small message size, and extensibility without the need for version negotiation. The underlying data model of CBOR is an extended version of the JSON data model, as defined in RFC 7049. A JSON text is a sequence of characters, not an encoded sequence of bytes, while a CBOR data item consists of bytes, not characters. The RFC 7049 also defines the conversion from JSON to CBOR and vise versa.
Some applications that would like to use JSON need to transport binary data, such as signatures, encryption keys, graphic data, or sensor values. In JSON, these data need to be encoded (usually in base64 format), adding complexity and bulk. If you could recall, in JWS, it uses base64url encoding.
The draft specification — CBOR Encoded Message Syntax, which is produced under the IETF COSE (CBOR Object Signing and Encryption) working group, specifies processing for signatures, message authentication codes, and encryption using CBOR. It also specifies a representation for cryptographic keys using CBOR.
The IETF JOSE working group produced a set of documents for JWT, JWS, JWE, JWA using JSON that specify how to process encryption, signatures, and message authentication (MAC) operations, and how to encode keys using JSON. The IETF COSE working group does the same work for use with the CBOR (RFC 7049) document format.
COSE supports two different signature structures. COSE_Sign allows for one or more signers to be applied to a single content (like JWS JSON serialization). COSE_Sign1 is restricted to a single signer (like JWS compact serialization).
The COSE_Sign structure is a CBOR array. One of the elements in the array is the payload. The payload contains the serialized content to be signed. If the payload is not present in the message, the application is required to supply the payload separately. The payload is wrapped in a bstr (a byte string) to ensure that it is transported without changes. If the payload is transported separately (i.e. detached content), then a nil CBOR object is placed in this location and it is the responsibility of the application to ensure that it will be transported without changes.
The predefined names for types, for example bstr , are defined in the CBOR data definition language (CDDL) : https://tools.ietf.org/html/draft-greevenbosch-appsawg-cbor-cddl-07
Even though the libraries to support CBOR Object Signing are yet to arrive, there are multiple libraries, which support CBOR itself:
- JACOB (Java): https://github.com/jawi/jacob
- cbor-java (Java): https://github.com/c-rack/cbor-java
- CBOR Encoder (PHP): https://github.com/2tvenom/CBOREncode
The JWS produced by the IETF JOSE working group is the only standard available at the moment as an RFC, which defines how to represent a signed payload (JSON or anything) in either of its supported serialized formats: compact serialization or JSON serialization. JWS is widely accepted and has a wide range of library support under multiple platforms.
The work in progress under the IETF COSE working group is to produce a set of RFCs to make the work done by the IETF JOSE working group more applicable in a constrained environment. CBOR is being adopted by several of the IETF working groups dealing with the IoT world as their encoding of data structures. CBOR was designed specifically to be both small in terms of messages transport and implementation size as well having a schema-free decoder. When a need exists to provide message security services for IoT and using CBOR as the message encoding format, then CBOR Object Signing makes more sense than just using plain JWS. CBOR Object Signing is relatively new and yet to find any libraries to support that.
JCS is not a widely accepted standard or there is no widely accepted standard body behind it. Its focus is to make the signature an integral part of the signed payload. There is only one library we found, which supports JCS implementation. Everything you can do with JCS can be done with JWS — hence, in most of the cases, JWS is the preferred option over JCS. Also, both the JWS and CBOR Object Signing are not just about signing a JSON payload — but anything, while JCS is only about signing a JSON payload.