JSON Cleartext Signature
Table of Contents
1. Introduction
2. Sample Signature
3. Signature Scope
4. Normalization and Signature Validation
5. Notation
6. Data Types
7. JCS Objects
Top-level Property
Additional EC properties
Additional RSA properties
Appendix A: ECMAScript Compatibility Mode
Appendix B: Multiple Signatures
Appendix C: Usage in Applications
Appendix D: Interoperability
Appendix E: Acknowledgements
Appendix F: References
Appendix G: Document History
Appendix H: Author
1. Introduction
JCS is a scheme for signing data expressed as JSON [RFC7159] objects. It is loosely modeled after XML DSig's [XMLDSIG] "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. That is, JCS follows the "spirit" of JSON.
Unlike IETF's JWS [RFC7515], JCS was designed to be an integral part of a JSON object rather than embedding arbitrary signed data. There are (of course) pros and cons to both approaches, but for information-rich messaging, clear-text data at least have an advantage for documentation and debugging. To cope with the primary drawback (the potential dependency on canonicalization), this part has been extremely simplified. In fact, JCS only relies on predictable serialization of JSON data.
In order to make library support of JCS straightforward in spite of having a different structure compared to JWS [RFC7515], JCS supports the same algorithms, curve names, signature blob representation, and public key objects. The only crypto object that differs is JWS's "x5c" since it (presumably for historical reasons), uses Base64 [RFC4648] rather than Base64URL encoding.
Thanks to predictable serialization introduced in ECMAScript [ES6], JCS may also be used for "in-object" JavaScript signatures, making JCS ideal for HTML5 applications. See ECMAScript Compatibility Mode.
2. Sample Signature
The following cryptographically verifiable sample signature is used to visualize the JCS specification:
  "now": "2015-01-12T09:22:36Z",
  "escapeMe": "\u20ac$\u000F\u000aA'\u0042\u0022\u005c\\\"\/",
  "numbers": [1e0, 4.50, 6],
  "signature": {
    "algorithm": "ES256",
    "publicKey": {
      "type": "EC",
      "curve": "P-256",
      "x": "lNxNvAUEE8t7DSQBft93LVSXxKCiVjhbWWfyg023FCk",
      "y": "LmTlQxXB3LgZrNLmhOfMaCnDizczC_RfQ6Kx8iNwfFA"
    "value": "C1qP8PghKOl6NT8gpoy6cDI1Uzls_o8mYECYnWT-CkRaIaiZd9u_4nDKRhQvjbbeWZ1FiHPMs3TKmXIBSCuUHg"
The sample signature's payload consists of the properties above signature. Note: JCS does not mandate any specific ordering of properties like in the sample.
3. Signature Scope
The scope of a signature (what is actually signed) comprises all properties including possible child objects of the JSON object holding the signature property except for the value property (shaded area in the sample).
4. Normalization and Signature Validation
Prerequisite: A JSON object in accordance with [RFC7159] containing a properly formatted signature sub-object.
Parsing restrictions:The normalization steps are as follows:Applied on the sample signature, a conforming JCS normalization process should return the following JSON string:
The text in red highlights the core of the normalization process. Note that the output string was folded for improving readability.
The signature value can now be calculated by running the algorithm specified in the algorithm property using the signature key over the UTF-8 representation of the normalized data.
Path validation (when applicable), is out of scope for JCS, but is preferably carried out as described in X.509 [RFC5280].
The next sections cover the JCS format.
5. Notation
JCS consists of a top-level signature property holding a composite JSON object.
JSON objects are described as tables with associated properties. When a property holds a JSON object this is denoted by a link to the actual definition.
Properties may either be mandatory (M) or optional (O) as defined in the "Req" column.
Array properties are identified by [ ] x-y where the range expression represents the valid number of array elements.
In some JSON objects there is a choice from a set of mutually exclusive alternatives.
This is manifested in object tables like the following:
Property selection 1Type selection 1ReqComment selection 1
Property selection 2Type selection 2Comment selection 2
6. Data Types
The table below shows how the data types used by this specification are mapped into native JSON types:
any"any"Arbitrary JSON type or object
bigintstringBase10-encoded integer with arbitrary precision
stringstringArbitrary string
uristringURI [RFC3986]
byte[]stringBase64URL-encoded [RFC4648] binary data
cryptostringBase64URL-encoded positive integer with arbitrary precision. Note that leading zero-valued bytes must be discarded
object{}JSON object
Note that "Type" refers to the element type for arrays.
7. JCS Objects
The following tables describe the JCS JSON structures in detail.
Top-level Property
"signature": signatureobjectMThe mandatory top-level property
"version": "http://xmlns.webpki.org/jcs/v1"uriOOption: Signature object version identifier. For future revisions of JCS, this property would be mandatory.
"algorithm": "algorithm"stringMSignature algorithm ID. The currently recognized symmetric key algorithms include:
  • http://www.w3.org/2000/09/xmldsig#hmac-sha1
  • http://www.w3.org/2001/04/xmldsig-more#hmac-sha256
  • http://www.w3.org/2001/04/xmldsig-more#hmac-sha384
  • http://www.w3.org/2001/04/xmldsig-more#hmac-sha512
The currently recognized asymmetric key algorithms include:
  • http://www.w3.org/2000/09/xmldsig#rsa-sha1
  • http://www.w3.org/2001/04/xmldsig-more#rsa-sha256
  • http://www.w3.org/2001/04/xmldsig-more#rsa-sha384
  • http://www.w3.org/2001/04/xmldsig-more#rsa-sha512
  • http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256
  • http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384
  • http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512
For detailed descriptions of these algorithms, see XML DSig [XMLDSIG].
A subset of the signature algorithms may also be expressed in the JWS [RFC7515] notation:
  • HS256  =  http://www.w3.org/2001/04/xmldsig-more#hmac-sha256
  • HS384  =  http://www.w3.org/2001/04/xmldsig-more#hmac-sha384
  • HS512  =  http://www.w3.org/2001/04/xmldsig-more#hmac-sha512
  • RS256  =  http://www.w3.org/2001/04/xmldsig-more#rsa-sha256
  • RS384  =  http://www.w3.org/2001/04/xmldsig-more#rsa-sha384
  • RS512  =  http://www.w3.org/2001/04/xmldsig-more#rsa-sha512
  • ES256  =  http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256
  • ES384  =  http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384
  • ES512  =  http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512
"keyId": "keyId"stringOOption: Application specific string identifying the signature key.
"publicKey": publicKeyobjectOOption: Public key object.
"pemUrl": "pemUrl"uriOption: A single public key or X.509 [RFC5280] certificate path stored in a PEM [RFC7468] file accessible via an HTTP URL.
"certificatePath": ["Sorted Certificate Path"] 1-nbyte[]Option: Sorted array of X.509 [RFC5280] certificates, where the first element must contain the signature certificate. The certificate path must be contiguous but is not required to be complete.
"signerCertificate": signerCertificateobjectOOption: Signature certificate attribute data for usage with the certificatePath option.
A compliant JCS implementation must verify that the signerCertificate object matches the first certificate in the certificatePath.
Note: due to the fact that X.500 name comparisons have turned out (in practice) to be a source of non-interoperability, the signerCertificate option should only be used in specific environments.
"extensions": [extensions] 1-nobjectOOption: Array holding custom extension objects like time-stamps, CRLs, and OCSP responses.
A conforming implementation must reject extensions that are not recognized.
"value": "value"byte[]MThe signature data. Note that the binary representation must follow the JWS [RFC7515] specifications.
Note that asymmetric key signatures are not required providing an associated publicKey, pemUrl or certificatePath property since the key may be given by the context or through the keyId property.
"type": "type"stringMKey type indicator. Currently the following types are recognized:
Additional EC properties
"curve": "curve"stringMEC curve ID. The currently recognized EC curves include:
  • http://xmlns.webpki.org/sks/algorithm#ec.nist.b163
  • http://xmlns.webpki.org/sks/algorithm#ec.nist.b233
  • http://xmlns.webpki.org/sks/algorithm#ec.nist.b283
  • http://xmlns.webpki.org/sks/algorithm#ec.nist.p192
  • http://xmlns.webpki.org/sks/algorithm#ec.nist.p256
  • http://xmlns.webpki.org/sks/algorithm#ec.nist.p384
  • http://xmlns.webpki.org/sks/algorithm#ec.nist.p521
  • http://xmlns.webpki.org/sks/algorithm#ec.secg.p256k1
  • http://xmlns.webpki.org/sks/algorithm#ec.brainpool.p256r1
The NIST algorithms are described in FIPS 186-4 [FIPS-186-4], while Brainpool algorithms are covered by RFC 5639 [RFC5639].
The algorithm names were derived from the SKS [SKS] specification.
A subset of the EC curves may also be expressed in the JWS [RFC7515] notation:
  • P-256  =  http://xmlns.webpki.org/sks/algorithm#ec.nist.p256
  • P-384  =  http://xmlns.webpki.org/sks/algorithm#ec.nist.p384
  • P-521  =  http://xmlns.webpki.org/sks/algorithm#ec.nist.p521
"x": "x"byte[]MEC curve point X. The length of this field must be the full size of a coordinate for the curve specified in the curve parameter. For example, if the value of curve is P-521, the decoded argument must be 66 bytes.
"y": "y"byte[]MEC curve point Y. The length of this field must be the full size of a coordinate for the curve specified in the curve parameter. For example, if the value of curve is P-521, the decoded argument must be 66 bytes.
Additional RSA properties
"n": "n"cryptoMRSA modulus. Also see the crypto data type.
"e": "e"cryptoMRSA exponent. Also see the crypto data type.
"issuer": "issuer"stringMIssuer distinguished name in LDAP [RFC4514] notation.
"serialNumber": "serialNumber"bigintMCertificate serial number.
"subject": "subject"stringMSubject distinguished name in LDAP [RFC4514] notation.
"type": "type"uriMMandatory unique extension type.
"...": ...anyOExtension-specfic properties.
Appendix A: ECMAScript Compatibility Mode
ECMAScript mode in this context refers to the ability to sign JavaScript objects as well as using the standard JSON support for parsing and creating signed data.
The code snippet below shows a signed JavaScript object:
var signedObject = {
  // The data
  statement: "Hello signed world!",
  otherProperties: [2000, true],
  // The signature
  signature: {
    algorithm: "ES256",
    publicKey: {
      type: "EC",
      curve: "P-256",
      x: "vlYxD4dtFJOp1_8_QUcieWCW-4KrLMmFL2rpkY1bQDs",
      y: "fxEF70yJenP3SPHM9hv-EnvhG6nXr3_S-fDqoj-F6yM"
    value: "2H__TkcV28QpGWPkyVbR1CW0I8L4xARrVGL0LjOeHJLOPozdzRqCTyYfmAippJXqdzgNAonnFPVCSI5A6novMQ"
This signature could be verified by the following code:
function convertToUTF8(string) {
  var buffer = [];
  for (var i = 0; i < string.length; i++) {
    var c = string.charCodeAt(i);
    if (c < 128) {
    } else if ((c > 127) && (c < 2048)) {
      buffer.push((c >> 6) | 0xC0);
      buffer.push((c & 0x3F) | 0x80);
    } else {
      buffer.push((c >> 12) | 0xE0);
      buffer.push(((c >> 6) & 0x3F) | 0x80);
      buffer.push((c & 0x3F) | 0x80);
  return new Uint8Array(buffer);

function decodeBase64URL(encoded) {
  var string = atob(encoded.replace(/-/g,'+').replace(/_/g,'/'));
  var buffer = [];
  for (var i = 0; i < string.length; i++) {
  return new Uint8Array(buffer);

function verifySignature(jcs) {
  // Perform JCS normalization
  var clone = Object.assign({}, jcs.signature);     // Clone "signature" child object
  var signature = decodeBase64URL(clone.value);     // Get signature value
  delete jcs.signature.value;                       // Remove signature "value" property from signed object
  var data = convertToUTF8(JSON.stringify(jcs));    // Get normalized JSON string (signed data)
  jcs.signature = clone;                            // Restore signed object
  // Perform the actual crypto, here using W3C WebCrypto
  crypto.subtle.importKey('jwk', { kty: clone.publicKey.type, 
                                   crv: clone.publicKey.curve,
                                   x: clone.publicKey.x,
                                   y: clone.publicKey.y },
                          { name: 'ECDSA', namedCurve: clone.publicKey.curve },
                          true, ['verify']).then(function(publicKey) {
    crypto.subtle.verify({ name: 'ECDSA', hash: { name: 'SHA-256' } }, 
                         publicKey, signature, data).then(function(result) {
      console.debug('Success=' + result);

In order to use JCS with ECMAScript, the following additional constraints must be taken in consideration:This level of compliance with the JCS specification is referred to as "JCS/ES6".
Appendix B: Multiple Signatures
Since JSON properties are single-valued, JCS does not intrinsically support multiple signings of the same object. Although it would be technically feasible using an array of signature objects, this would greatly complicate message normalization. However, there is a "workaround" which fits most real-world scenarios needing multiple signatures and that is using wrapping signatures.
Original signed JSON object:
  "timeStamp": "2014-12-08T13:56:08Z",
  "id": "lADU_sO067Wlgoo52-9L",
  "data": ["One","Two","Three"],
  "signature": {
Original signature...
Dual-signed JSON object:
  "container": {
    "timeStamp": "2014-12-08T13:56:08Z",
    "id": "lADU_sO067Wlgoo52-9L",
    "data": ["One","Two","Three"],
    "signature": {
Original signature...
  "signature": {
Wrapping signature...
That is, using JCS there is no distinction between multiple signatures and counter-signatures.
Appendix C: Usage in Applications
JCS as well as the freestanding sub-objects publicKey and certificatePath, have been utilized in a proof-of-concept application [PKIDROID] running on Android.
The sample code below is based on the Java reference implementation [OPENKEY] which features an integrated JSON encoder, decoder and signature solution:
public void signAndVerifyJCS(final PublicKey publicKey, final PrivateKey privateKey) throws IOException {

  // Create an empty JSON document
  JSONObjectWriter writer = new JSONObjectWriter();

  // Fill it with some data
  writer.setString("myProperty", "Some data");

  // Sign document
  writer.setSignature(new JSONAsymKeySigner(new AsymKeySignerInterface() {
    public byte[] signData (byte[] data, AsymSignatureAlgorithms algorithm) throws IOException {
      try {
        return new SignatureWrapper(algorithm, privateKey).update(data).sign();
      } catch (GeneralSecurityException e) {
        throw new IOException(e);
    public PublicKey getPublicKey() throws IOException {
      return publicKey;

  // Serialize document
  String json = writer.toString();

  // Print document on the console
  System.out.println("Signed doc: " + json);

  // Parse document
  JSONObjectReader reader = JSONParser.parse(json);

  // Get and verify signature
  JSONSignatureDecoder signature = reader.getSignature();
  signature.verify(new JSONAsymKeyVerifier(publicKey));

  // Print document payload on the console
  System.out.println("Returned data: " + reader.getString("myProperty"));
Appendix D: Interoperability
Since serialization of floating point numbers as specified by JCS is (at the time of writing) not a standard feature, it is recommended putting such data in quotes. Albeit a limitation, financial data is not natively supported by JSON either due to the fact that JavaScript lacks support for big decimals. Note the handling of certificate serial numbers in JCS.
JSON tool designers could also consider implementing the ECMAScript Compatibility Mode since it presumably requires very moderate adjustments of existing code.
Fully JCS compatible reference implementations [OPENKEY] are available both for Java and JavaScript. These implementations use ECMAScript number serialization when creating JSON data, making them compliant with the ECMAScript Compatibility Mode as well.
Pyhton users can get the required parser behavior (modulo floating point data...) by using the following constructs:
jsonObject = json.loads(jcsSignedData,object_pairs_hook=collections.OrderedDict)       # Parse JSON while keeping original property order
signatureObject = jsonObject['signature']                                              # As described in this document
clonedSignatureObject = collections.OrderedDict(signatureObject)                       # For non-destructive signature validation
signatureValue = signatureObject.pop('value')                                          # In Base64URL notation
normalizedSignedData = json.dumps(jsonObject,separators=(',',':'),ensure_ascii=False)  # In Unicode
jsonObject['signature'] = clonedSignatureObject                                        # Restore JSON object
... Signature Validation Code ...
Appendix E: Acknowledgements
During the initial phases of the design process, highly appreciated feedback were provided by Manu Sporny, Jim Klo, Jeffrey Walton, David Chadwick, Jim Schaad, David Waite, Douglas Crockford, Arne Riiber, Brian Campbell, Sergey Beryozkin, and others.
Special thanks go to James Manger who pointed out the ECMAScript [ES6] number serialization scheme as well as reviewing a related Internet draft.
Funding has been provided by PrimeKey Solutions AB and the Swedish Innovation Board (VINNOVA).
Appendix F: References
[ES6]A. Wirfs-Brock, "ECMAScript 2015 Language Specification", ECMA-262, June 2015.
[FIPS-186-4]"FIPS PUB 186-4: Digital Signature Standard (DSS)", U.S. Department of Commerce/National Institute of Standards and Technology, June 2013.
[OPENKEY]"OpenKeyStore Project", https://github.com/cyberphone/openkeystore
[PKIDROID]"WebPKI Suite", https://play.google.com/store/apps/details?id=org.webpki.mobile.android
[RFC3986]T. Berners-Lee, R. Fielding, L. Masinter, "Uniform Resource Identifier (URI): Generic Syntax", RFC 3986, January 2005.
[RFC4514]K. Zeilenga, "Lightweight Directory Access Protocol (LDAP): String Representation of Distinguished Names", RFC 4514, June 2006.
[RFC4648]S. Josefsson, "The Base16, Base32, and Base64 Data Encodings", RFC 4648, October 2006.
[RFC5280]D. Cooper, S. Santesson, S. Farrell, S. Boeyen, R. Housley, W. Polk, "Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile", RFC 5280, May 2008.
[RFC5639]M. Lochter, J. Merkle, "Elliptic Curve Cryptography (ECC) Brainpool Standard Curves and Curve Generation", RFC 5639, March 2010.
[RFC7159]T. Bray, "The JavaScript Object Notation (JSON) Data Interchange Format", RFC 7159, March 2014.
[RFC7468]S. Josefsson, S. Leonard, "Textual Encodings of PKIX, PKCS, and CMS Structures", RFC 7468, April 2015.
[RFC7515]M. Jones, J. Bradley, N. Sakimura, "JSON Web Signature (JWS)", RFC 7515, May 2015.
[SKS]A. Rundgren, "Secure Key Store (SKS) - API and Architecture", Work in progress, https://cyberphone.github.io/doc/security/sks-api-arch.pdf, V1.01, January 2016.
[V8]"Chrome V8", Google Chrome JavaScript Engine, https://developers.google.com/v8/
[WCRYPT]"Web Cryptography API", R. Sleevi, M. Watson, W3C Candidate Recommendation, December 2014. https://www.w3.org/TR/WebCryptoAPI/
[XMLDSIG]D. Eastlake, J. Reagle, D. Solo, F. Hirsch, M. Nystrom, T. Roessler, K. Yiu, "XML Signature Syntax and Processing Version 1.1.", W3C Recommendation, April 2013.
Appendix G: Document History
2013-12-170.3Initial publication in HTML5
2013-12-200.4Changed from Base64 to Base64URL everywhere
2013-12-290.5Added the extensions facility
2014-01-210.51Added clarification to public key parameter representation
2014-01-260.52Added note regarding the signerCertificate option
2014-04-150.53Embedded bigint in JS string making syntax fully JSON compatible
2014-09-170.54Changed canonicalization to normalization
2014-09-230.55Aligned EC parameter representation with [RFC7515]
2014-12-080.56Major upgrade including removal of [XMLDSIG] bloat and adding support for [RFC7515] algorithm identifiers
2014-12-190.57Added an interoperability section
2015-01-120.58Added clarification to signature value representation
2016-01-110.59Added ECMAScript compatibility mode
Appendix H: Author
JCS was developed by Anders Rundgren (anders.rundgren.net@gmail.com) as a part of the OpenKeyStore project [OPENKEY].