From a2148847a90d5b6aaf7b7722c8dfbab73cf80b65 Mon Sep 17 00:00:00 2001 From: Newton Joshua Date: Thu, 29 Aug 2019 11:03:42 +0530 Subject: [PATCH 01/15] Update encrypt.js --- lib/jwe/encrypt.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jwe/encrypt.js b/lib/jwe/encrypt.js index 80affcf..a19f13a 100644 --- a/lib/jwe/encrypt.js +++ b/lib/jwe/encrypt.js @@ -10,7 +10,7 @@ var lodash = require("lodash"), generateCEK = require("./helpers").generateCEK, JWK = require("../jwk"), slice = require("./helpers").slice, - zlib = require("zlib"), + zlib = require('browserify-zlib'), CONSTANTS = require("../algorithms/constants"); var assign = lodash.assign; From d5b34127ed93e4d82fccf07ac72e6320b419f679 Mon Sep 17 00:00:00 2001 From: Newton Joshua Date: Thu, 29 Aug 2019 11:04:06 +0530 Subject: [PATCH 02/15] Update decrypt.js --- lib/jwe/decrypt.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jwe/decrypt.js b/lib/jwe/decrypt.js index 1d15d1c..347cf5d 100644 --- a/lib/jwe/decrypt.js +++ b/lib/jwe/decrypt.js @@ -9,7 +9,7 @@ var base64url = require("../util/base64url"), AlgConfig = require("../util/algconfig"), JWK = require("../jwk"), merge = require("../util/merge"), - zlib = require("zlib"); + zlib = require("browserify-zlib"); var DEFAULT_OPTIONS = { algorithms: "*" From 9b69daaead960c2fc5b8ea36eb332370dc4be87a Mon Sep 17 00:00:00 2001 From: Newton Joshua Date: Thu, 29 Aug 2019 11:04:21 +0530 Subject: [PATCH 03/15] Update encrypt.js --- lib/jwe/encrypt.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jwe/encrypt.js b/lib/jwe/encrypt.js index a19f13a..4933f05 100644 --- a/lib/jwe/encrypt.js +++ b/lib/jwe/encrypt.js @@ -10,7 +10,7 @@ var lodash = require("lodash"), generateCEK = require("./helpers").generateCEK, JWK = require("../jwk"), slice = require("./helpers").slice, - zlib = require('browserify-zlib'), + zlib = require("browserify-zlib"), CONSTANTS = require("../algorithms/constants"); var assign = lodash.assign; From 0579bc330f0713863464a176a21094a1a2179268 Mon Sep 17 00:00:00 2001 From: Newton Joshua Date: Thu, 29 Aug 2019 11:24:32 +0530 Subject: [PATCH 04/15] Update package.json --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 0bd5984..b0c6cca 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,8 @@ "lodash": "^4.17.11", "long": "^4.0.0", "node-forge": "^0.8.1", - "uuid": "^3.3.2" + "uuid": "^3.3.2", + "browserify-zlib": "^0.2.0" }, "devDependencies": { "bowser": "^1.9.3", From 7d6d4448376ca52e5b8a29cdb3f564f8c3d78f9a Mon Sep 17 00:00:00 2001 From: Newton Joshua Date: Thu, 29 Aug 2019 11:27:04 +0530 Subject: [PATCH 05/15] Update README.md --- README.md | 796 +----------------------------------------------------- 1 file changed, 3 insertions(+), 793 deletions(-) diff --git a/README.md b/README.md index 2293912..32cbe2b 100644 --- a/README.md +++ b/README.md @@ -1,796 +1,6 @@ -# node-jose # +# node-jose-browserify # -[![Greenkeeper badge](https://badges.greenkeeper.io/cisco/node-jose.svg)](https://greenkeeper.io/) -[![Build Status](https://travis-ci.org/cisco/node-jose.svg?branch=master)](https://travis-ci.org/cisco/node-jose) +Emulates Cisco's node-jose module for the browser. -A JavaScript implementation of the JSON Object Signing and Encryption (JOSE) for current web browsers and node.js-based servers. This library implements (wherever possible) all algorithms, formats, and options in [JWS](https://tools.ietf.org/html/rfc7515 "Jones, M., J. Bradley and N. Sakimura, 'JSON Web Signature (JWS)' RFC 7515, May 2015"), [JWE](https://tools.ietf.org/html/rfc7516 "Jones, M. and J. Hildebrand 'JSON Web Encryption (JWE)', RFC 7516, May 2015"), [JWK](https://tools.ietf.org/html/rfc7517 "Jones, M., 'JSON Web Key (JWK)', RFC 7517, May 2015"), and [JWA](https://tools.ietf.org/html/rfc7518 "Jones, M., 'JSON Web Algorithms (JWA)', RFC 7518, May 2015") and uses native cryptographic support ([WebCrypto API](http://www.w3.org/TR/WebCryptoAPI/) or node.js' "[crypto](https://nodejs.org/api/crypto.html)" module) where feasible. +Refer to https://github.com/cisco/node-jose for documentation. - - - - -- [Installing](#installing) -- [Basics](#basics) -- [Keys and Key Stores](#keys-and-key-stores) - - [Obtaining a KeyStore](#obtaining-a-keystore) - - [Exporting a KeyStore](#exporting-a-keystore) - - [Retrieving Keys](#retrieving-keys) - - [Searching for Keys](#searching-for-keys) - - [Managing Keys](#managing-keys) - - [Importing and Exporting a Single Key](#importing-and-exporting-a-single-key) - - [Obtaining a Key's Thumbprint](#obtaining-a-keys-thumbprint) -- [Signatures](#signatures) - - [Keys Used for Signing and Verifying](#keys-used-for-signing-and-verifying) - - [Signing Content](#signing-content) - - [Verifying a JWS](#verifying-a-jws) - - [Allowing (or Disallowing) Signature Algorithms](#allowing-or-disallowing-signature-algorithms) - - [Handling `crit` Header Members](#handling-crit-header-members) -- [Encryption](#encryption) - - [Keys Used for Encrypting and Decrypting](#keys-used-for-encrypting-and-decrypting) - - [Encrypting Content](#encrypting-content) - - [Decrypting a JWE](#decrypting-a-jwe) - - [Allowing (or Disallowing) Encryption Algorithms](#allowing-or-disallowing-encryption-algorithms) - - [Handling `crit` Header Members](#handling-crit-header-members-1) -- [Useful Utilities](#useful-utilities) - - [Converting to Buffer](#converting-to-buffer) - - [URI-Safe Base64](#uri-safe-base64) - - [Random Bytes](#random-bytes) - - - -## Installing ## - -To install the latest from [NPM](https://npmjs.com/): - -```shell - npm install node-jose -``` - -Or to install a specific release: - -```shell - npm install node-jose@0.3.0 -``` - -Alternatively, the latest unpublished code can be installed directly from the repository: - -```shell - npm install git+https://github.com/cisco/node-jose.git -``` - -## Basics ## - -Require the library as normal: - -```javascript -var jose = require('node-jose'); -``` - -This library uses [Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) for nearly every operation. - -This library supports [Browserify](http://browserify.org/) and [Webpack](https://webpack.github.io/). To use in a web browser, `require('node-jose')` and bundle with the rest of your app. - -The content to be signed/encrypted -- or returned from being verified/decrypted -- are [Buffer](https://nodejs.org/api/buffer.html) objects. - -## Keys and Key Stores ## - -The `jose.JWK` namespace deals with JWK and JWK-sets. - -* `jose.JWK.Key` is a logical representation of a JWK, and is the "raw" entry point for various cryptographic operations (e.g., sign, verify, encrypt, decrypt). -* `jose.JWK.KeyStore` represents a collection of Keys. - -Creating a JWE or JWS ultimately require one or more explicit Key objects. - -Processing a JWE or JWS relies on a KeyStore. - -### Obtaining a KeyStore ### -To create an empty keystore: - -```javascript -keystore = jose.JWK.createKeyStore(); -``` - -To import a JWK-set as a keystore: - -```javascript -// {input} is a String or JSON object representing the JWK-set -jose.JWK.asKeyStore(input). - then(function(result) { - // {result} is a jose.JWK.KeyStore - keystore = result; - }); -``` - -### Exporting a KeyStore ### - -To export the public keys of a keystore as a JWK-set: - -```javascript -output = keystore.toJSON(); -``` - -To export **all** the keys of a keystore: - -```javascript -output = keystore.toJSON(true); -``` - -### Retrieving Keys ### - -To retrieve a key from a keystore: - -```javascript -// by 'kid' -key = keystore.get(kid); -``` - -This retrieves the first key that matches the given {kid}. If multiple keys have the same {kid}, you can further narrow what to retrieve: - -```javascript -// ... and by 'kty' -key = keystore.get(kid, { kty: 'RSA' }); - -// ... and by 'use' -key = keystore.get(kid, { use: 'enc' }); - -// ... and by 'alg' -key = keystore.get(kid, { use: 'RSA-OAEP' }); - -// ... and by 'kty' and 'use' -key = keystore.get(kid, { kty: 'RSA', use: 'enc' }); - -// same as above, but with a single {props} argument -key = keystore.get({ kid: kid, kty: 'RSA', use: 'enc' }); -``` - -### Searching for Keys ### - -To retrieve all the keys from a keystore: - -```javascript -everything = keystore.all(); -``` - -`all()` can be filtered much like `get()`: - -```javascript -// filter by 'kid' -everything = keystore.all({ kid: kid }); - -// filter by 'kty' -everything = keystore.all({ kty: 'RSA' }); - -// filter by 'use' -everything = keystore.all({ use: 'enc' }); - -// filter by 'alg' -everything = keystore.all({ alg: 'RSA-OAEP' }); - -// filter by 'kid' + 'kty' + 'alg' -everything = keystore.all({ kid: kid, kty: 'RSA', alg: 'RSA-OAEP' }); -``` - -### Managing Keys ### - -To import an existing Key (as a JSON object or Key instance): - -```javascript -// input is either a: -// * jose.JWK.Key to copy from; or -// * JSON object representing a JWK; or -keystore.add(input). - then(function(result) { - // {result} is a jose.JWK.Key - key = result; - }); -``` - -To import and existing Key from a PEM or DER: - -```javascript -// input is either a: -// * String serialization of a JSON JWK/(base64-encoded) PEM/(binary-encoded) DER -// * Buffer of a JSON JWK/(base64-encoded) PEM/(binary-encoded) DER -// form is either a: -// * "json" for a JSON stringified JWK -// * "private" for a DER encoded 'raw' private key -// * "pkcs8" for a DER encoded (unencrypted!) PKCS8 private key -// * "public" for a DER encoded SPKI public key (alternate to 'spki') -// * "spki" for a DER encoded SPKI public key -// * "pkix" for a DER encoded PKIX X.509 certificate -// * "x509" for a DER encoded PKIX X.509 certificate -// * "pem" for a PEM encoded of PKCS8 / SPKI / PKIX -keystore.add(input, form). - then(function(result) { - // {result} is a jose.JWK.Key - }); -``` - -To generate a new Key: - -```javascript -// first argument is the key type (kty) -// second is the key size (in bits) or named curve ('crv') for "EC" -keystore.generate("oct", 256). - then(function(result) { - // {result} is a jose.JWK.Key - key = result; - }); - -// ... with properties -var props = { - kid: 'gBdaS-G8RLax2qgObTD94w', - alg: 'A256GCM', - use: 'enc' -}; -keystore.generate("oct", 256, props). - then(function(result) { - // {result} is a jose.JWK.Key - key = result; - }); -``` - -To remove a Key from its Keystore: -```javascript -keystore.remove(key); -// NOTE: key.keystore does not change!! -``` - -### Importing and Exporting a Single Key ### - -To create a single "stand alone" key: - -```javascript -jose.JWK.createKey("oct", 256, { alg: "A256GCM" }). - then(function(result) { - // {result} is a jose.JWK.Key - // {result.keystore} is a unique jose.JWK.KeyStore - }); -``` - - -To import a single Key: - -```javascript -// where input is either a: -// * jose.JWK.Key instance -// * JSON Object representation of a JWK -jose.JWK.asKey(input). - then(function(result) { - // {result} is a jose.JWK.Key - // {result.keystore} is a unique jose.JWK.KeyStore - }); - -// where input is either a: -// * String serialization of a JSON JWK/(base64-encoded) PEM/(binary-encoded) DER -// * Buffer of a JSON JWK/(base64-encoded) PEM/(binary-encoded) DER -// form is either a: -// * "json" for a JSON stringified JWK -// * "pkcs8" for a DER encoded (unencrypted!) PKCS8 private key -// * "spki" for a DER encoded SPKI public key -// * "pkix" for a DER encoded PKIX X.509 certificate -// * "x509" for a DER encoded PKIX X.509 certificate -// * "pem" for a PEM encoded of PKCS8 / SPKI / PKIX -jose.JWK.asKey(input, form). - then(function(result) { - // {result} is a jose.JWK.Key - // {result.keystore} is a unique jose.JWK.KeyStore - }); -``` - -To export the public portion of a Key as a JWK: - -```javascript -var output = key.toJSON(); -``` - -To export the public **and** private portions of a Key: - -```javascript -var output = key.toJSON(true); -``` - -### Obtaining a Key's Thumbprint ### - -To get or calculate a [RFC 7638](https://tools.ietf.org/html/rfc7638) thumbprint for a key: - -```javascript -// where hash is a supported algorithm, currently one of: -// * SHA-1 -// * SHA-256 -// * SHA-384 -// * SHA-512 -key.thumbprint(hash). - then(function(print) { - // {print} is a Buffer containing the thumbprint binary value - }); -``` - -When importing or generating a key that does not have a "kid" defined, a -"SHA-256" thumbprint is calculated and used as the "kid". - -## Signatures ## - -### Keys Used for Signing and Verifying ### - -When signing content, the key is expected to meet one of the following: - -1. A secret key (e.g, `"kty":"oct"`) -2. The **private** key from a PKI (`"kty":"EC"` or `"kty":"RSA"`) key pair - -When verifying content, the key is expected to meet one of the following: - -1. A secret key (e.g, `"kty":"oct"`) -2. The **public** key from a PKI (`"kty":"EC"` or `"kty":"RSA"`) key pair - - -### Signing Content ### - -At its simplest, to create a JWS: - -```javascript -// {input} is a Buffer -jose.JWS.createSign(key). - update(input). - final(). - then(function(result) { - // {result} is a JSON object -- JWS using the JSON General Serialization - }); -``` - -The JWS is signed using the preferred algorithm appropriate for the given Key. The preferred algorithm is the first item returned by `key.algorithms("sign")`. - -To create a JWS using another serialization format: - -```javascript -jose.JWS.createSign({ format: 'flattened' }, key). - update(input). - final(). - then(function(result) { - // {result} is a JSON object -- JWS using the JSON Flattened Serialization - }); - -jose.JWS.createSign({ format: 'compact' }, key). - update(input). - final(). - then(function(result) { - // {result} is a String -- JWS using the Compact Serialization - }); -``` - -To create a JWS using a specific algorithm: -```javascript -jose.JWS.createSign({ alg: 'PS256' }, key). - update(input). - final(). - then(function(result) { - // .... - }); -``` - -To create a JWS for a specified content type: - -```javascript -jose.JWS.createSign({ fields: { cty: 'jwk+json' } }, key). - update(input). - final(). - then(function(result) { - // .... - }); -``` - -To create a JWS from String content: - -```javascript -jose.JWS.createSign(key). - update(input, "utf8"). - final(). - then(function(result) { - // .... - }); -``` - -To create a JWS with multiple signatures: - -```javascript -// {keys} is an Array of jose.JWK.Key instances -jose.JWS.createSign(keys). - update(input). - final(). - then(function(result) { - // .... - }); -``` - -### Verifying a JWS ### - -To verify a JWS, and retrieve the payload: - -```javascript -jose.JWS.createVerify(keystore). - verify(input). - then(function(result) { - // {result} is a Object with: - // * header: the combined 'protected' and 'unprotected' header members - // * payload: Buffer of the signed content - // * signature: Buffer of the verified signature - // * key: The key used to verify the signature - }); -``` - -To verify using an implied Key: - -```javascript -// {key} can be: -// * jose.JWK.Key -// * JSON object representing a JWK -jose.JWS.createVerify(key). - verify(input). - then(function(result) { - // ... - }); -``` - -To verify using a key embedded in the JWS: - -```javascript -jose.JWS.createVerify(). - verify(input, { allowEmbeddedKey: true }). - then(function(result) { - // ... - }); -``` - -Alternatively, a cached `createVerify()` can be configured to allow an embedded key: - -```javascript -var verifier = jose.JWS.createVerify({ allowEmbeddedKey: true }); - -verifier.verify(input). - then(function(result) { - // ... - }); -``` - -The key can be embedded using either 'jwk' or 'x5c', and can be located in either the JWS Unprotected Header or JWS Protected Header. - -**NOTE:** `verify()` will use the embedded key (if found and permitted) instead of any other key. - -#### Allowing (or Disallowing) Signature Algorithms ### - -To restrict what signature algorithms are allowed when verifying, add the `algorithms` member to the `options` Object. The `algorithms` member is either a string or an array of strings, where the string value(s) can be one of the following: - -* `"*"`: accept all supported algorithms -* **``** (e.g., `"PS256"`): accept the specific algorithm (can have a single '*' to match a range of algorithms) -* **`!`** (e.g., `"!RS256"`): *do not* accept the specific algorithm (can have a single '*' to match a range of algorithms) - -The negation is intended to be used with the wildcard accept string, and disallow takes precedence over allowed. - -To only accept RSA-PSS sigatures: - -```javascript -var opts = { - algorithms: ["PS*"] -}; -jose.JWS.createVerify(key, opts). - verify(input). - then(function(result) { - // ... - }); -``` - -To accept any algorithm, but disallow HMAC-based signatures: - -```javascript -var opts = { - algorithms: ["*", "!HS*"] -}; -jose.JWS.createVerify(key, opts). - verify(input). - then(input). - then(function(result) { - // ... - }); -``` - -#### Handling `crit` Header Members #### - -To accept 'crit' field members, add the `handlers` member to the options Object. The `handlers` member is itself an Object, where its member names are the `crit` header member, and the value is one of: - -* `Function`: takes the JWE decrypt output (just prior to decrypting) and returns a Promise for the processing of the member. -* `Object`: An object with the following `Function` members: - * "prepare" -- takes the JWE decrypt output (just prior to decrypting) and returns a Promise for the processing of the member. - * "complete" -- takes the JWE decrypt output (immediately after decrypting) and returns a Promise for the processing of the member. - -**NOTE** If the handler function returns a promise, the fulfilled value is ignored. It is expected these handler functions will modify the provided value directly. - -To perform additional (pre-verify) processing on a `crit` header member: - -```javascript -var opts = { - handlers: { - "exp": function(jws) { - // {jws} is the JWS verify output, pre-verification - jws.header.exp = new Date(jws.header.exp); - } - } -}; -jose.JWS.createVerify(key, opts). - verify(input). - then(function(result) { - // ... - }); -``` - -To perform additional (post-verify) processing on a `crit` header member: - -```javascript -var opts = { - handlers: { - "exp": { - complete: function(jws) { - // {jws} is the JWS verify output, post-verification - jws.header.exp = new Date(jws.header.exp); - } - } - } -}; -jose.JWS.createVerify(key, opts). - verify(input). - then(function(result) { - // ... - }); -``` - - -## Encryption ## - - -### Keys Used for Encrypting and Decrypting ### - -When encrypting content, the key is expected to meet one of the following: - -1. A secret key (e.g, `"kty":"oct"`) -2. The **public** key from a PKI (`"kty":"EC"` or `"kty":"RSA"`) key pair - -When decrypting content, the key is expected to meet one of the following: - -1. A secret key (e.g, `"kty":"oct"`) -2. The **private** key from a PKI (`"kty":"EC"` or `"kty":"RSA"`) key pair - - -### Encrypting Content ### - -At its simplest, to create a JWE: - -```javascript -// {input} is a Buffer -jose.JWE.createEncrypt(key). - update(input). - final(). - then(function(result) { - // {result} is a JSON Object -- JWE using the JSON General Serialization - }); -``` - -How the JWE content is encrypted depends on the provided Key. - -* If the Key only supports content encryption algorithms, then the preferred algorithm is used to encrypt the content and the key encryption algorithm (i.e., the "alg" member) is set to "dir". The preferred algorithm is the first item returned by `key.algorithms("encrypt")`. -* If the Key supports key management algorithms, then the JWE content is encrypted using "A128CBC-HS256" by default, and the Content Encryption Key is encrypted using the preferred algorithms for the given Key. The preferred algorithm is the first item returned by `key.algorithms("wrap")`. - - -To create a JWE using a different serialization format: - -```javascript -jose.JWE.createEncrypt({ format: 'compact' }, key). - update(input). - final(). - then(function(result) { - // {result} is a String -- JWE using the Compact Serialization - }); - -jose.JWE.createEncrypt({ format: 'flattened' }, key). - update(input). - final(). - then(function(result) { - // {result} is a JSON Object -- JWE using the JSON Flattened Serialization - }); -``` - -To create a JWE and compressing the content before encrypting: - -```javascript -jose.JWE.createEncrypt({ zip: true }, key). - update(input). - final(). - then(function(result) { - // .... - }); -``` - -To create a JWE for a specific content type: - -```javascript -jose.JWE.createEncrypt({ fields: { cty : 'jwk+json' } }, key). - update(input). - final(). - then(function(result) { - // .... - }); -``` - -To create a JWE with multiple recipients: - -```javascript -// {keys} is an Array of jose.JWK.Key instances -jose.JWE.createEncrypt(keys). - update(input). - final(). - then(function(result) { - // .... - }); -``` - -### Decrypting a JWE ### - -To decrypt a JWE, and retrieve the plaintext: - -```javascript -jose.JWE.createDecrypt(keystore). - decrypt(input). - then(function(result) { - // {result} is a Object with: - // * header: the combined 'protected' and 'unprotected' header members - // * protected: an array of the member names from the "protected" member - // * key: Key used to decrypt - // * payload: Buffer of the decrypted content - // * plaintext: Buffer of the decrypted content (alternate) - }); -``` - -To decrypt a JWE using an implied key: - -```javascript -jose.JWE.createDecrypt(key). - decrypt(input). - then(function(result) { - // .... - }); -``` - -#### Allowing (or Disallowing) Encryption Algorithms ### - -To restrict what encryption algorithms are allowed when verifying, add the `algorithms` member to the `options` Object. The `algorithms` member is either a string or an array of strings, where the string value(s) can be one of the following: - -* `"*"`: accept all supported algorithms -* **``** (e.g., `"A128KW"`): accept the specific algorithm (can have a single '*' to match a range of similar algorithms) -* **`!`** (e.g., `"!RSA1_5"`): *do not* accept the specific algorithm (can have a single '*' to match a range of similar algorithms) - -The negation is intended to be used with the wildcard accept string, and disallow takes precedence over allowed. - -To only accept "dir" and AES-GCM encryption: - -```javascript -var opts = { - algorithms: ["dir", "A*GCM"] -}; -jose.JWE.createDecrypt(key, opts). - decrypt(input). - then(function(result) { - // ... - }); -``` - -To accept any algorithm, but disallow RSA-based encryption: - -```javascript -var opts = { - algorithms: ["*", "!RSA*"] -}; -jose.JWS.createVerify(key, opts). - verify(input). - then(input). - then(function(result) { - // ... - }); -``` - -#### Handling `crit` Header Members #### - -To accept 'crit' field members, add the `handlers` member to the options Object. The `handlers` member is itself an Object, where its member names are the `crit` header member, and the value is one of: - -* `Function`: takes the JWE decrypt output (just prior to decrypting) and returns a Promise for the processing of the member. -* `Object`: An object with the following `Function` members: - * "prepare" -- takes the JWE decrypt output (just prior to decrypting) and returns a Promise for the processing of the member. - * "complete" -- takes the JWE decrypt output (immediately after decrypting) and returns a Promise for the processing of the member. - -**NOTE** If the handler function returns a promise, the fulfilled value is ignored. It is expected these handler functions will modify the provided value directly. - -To perform additional (pre-decrypt) processing on a `crit` header member: - -```javascript -var opts = { - handlers: { - "exp": function(jwe) { - // {jwe} is the JWE decrypt output, pre-decryption - jwe.header.exp = new Date(jwe.header.exp); - } - } -}; -jose.JWE.createDecrypt(key, opts). - decrypt(input). - then(function(result) { - // ... - }); -``` - -To perform additional (post-decrypt) processing on a `crit` header member: - -```javascript -var opts = { - handlers: { - "exp": { - complete: function(jwe) { - // {jwe} is the JWE decrypt output, post-decryption - jwe.header.exp = new Date(jwe.header.exp); - } - } - } -}; -jose.JWE.createDecrypt(key, opts). - decrypt(input). - then(function(result) { - // ... - }); -``` - -## Useful Utilities ## - -### Converting to Buffer ### - -To convert a [Typed Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays), [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer), or Array of Numbers to a Buffer: - -```javascript -buff = jose.util.asBuffer(input); -``` - -### URI-Safe Base64 ### - -This exposes [urlsafe-base64](https://github.com/RGBboy/urlsafe-base64)'s `encode` and `decode` methods as `encode` and `decode` (respectively). - -To convert from a Buffer to a base64uri-encoded String: - -```javascript -var output = jose.util.base64url.encode(input); -``` - -To convert a String to a base64uri-encoded String: - -```javascript -// explicit encoding -output = jose.util.base64url.encode(input, "utf8"); - -// implied "utf8" encoding -output = jose.util.base64url.encode(input); -``` - -To convert a base64uri-encoded String to a Buffer: - -```javascript -var output = jose.util.base64url.decode(input); -``` - -### Random Bytes ### - -To generate a Buffer of octets, regardless of platform: - -```javascript -// argument is size (in bytes) -var rnd = jose.util.randomBytes(32); -``` - -This function uses: - -* `crypto.randomBytes()` on node.js -* `crypto.getRandomValues()` on modern browsers -* A PRNG based on AES and SHA-1 for older platforms From c225ad80b7893222751fe4b203d6291edcb8c24b Mon Sep 17 00:00:00 2001 From: Newton Joshua Date: Thu, 29 Aug 2019 11:41:00 +0530 Subject: [PATCH 06/15] Update package.json --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index b0c6cca..e958f60 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "node-jose", + "name": "node-jose-browserify", "version": "1.1.3", - "description": "A JavaScript implementation of the JSON Object Signing and Encryption (JOSE) for current web browsers and node.js-based servers", + "description": "Emulates Cisco's node-jose module for the browser.", "keywords": [ "crypto", "jose", @@ -12,10 +12,10 @@ ], "repository": { "type": "git", - "url": "https://github.com/cisco/node-jose.git" + "url": "https://github.com/cisco/node-jose-browserify.git" }, "license": "Apache-2.0", - "author": "Cisco Systems, Inc. ", + "author": "Newton Joshua, Inc. ", "contributors": [ "Matthew A. Miller ", "Ian W. Remmel ", From 304cb9d72c2495db6c73a4d4321685d7720b4f9d Mon Sep 17 00:00:00 2001 From: Newton Joshua Date: Thu, 29 Aug 2019 12:41:55 +0530 Subject: [PATCH 07/15] Buffer is not defined in browser --- lib/index.js | 2 ++ package.json | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/index.js b/lib/index.js index baa1f5e..2eab948 100644 --- a/lib/index.js +++ b/lib/index.js @@ -8,6 +8,8 @@ if (typeof Promise === "undefined") { require("es6-promise").polyfill(); } +var global = global || window; +global.Buffer = global.Buffer || require("buffer").Buffer; var JWS = require("./jws"); diff --git a/package.json b/package.json index e958f60..1cff916 100644 --- a/package.json +++ b/package.json @@ -31,12 +31,14 @@ }, "dependencies": { "base64url": "^3.0.1", + "browserify-zlib": "^0.2.0", + "buffer": "^5.4.2", "es6-promise": "^4.2.6", "lodash": "^4.17.11", "long": "^4.0.0", "node-forge": "^0.8.1", - "uuid": "^3.3.2", - "browserify-zlib": "^0.2.0" + "stream-browserify": "^2.0.2", + "uuid": "^3.3.2" }, "devDependencies": { "bowser": "^1.9.3", From 62445084e73f68f4c02b6500bce1473ce89912e7 Mon Sep 17 00:00:00 2001 From: Newton Joshua Date: Thu, 29 Aug 2019 12:42:54 +0530 Subject: [PATCH 08/15] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1cff916..4d9d70b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-jose-browserify", - "version": "1.1.3", + "version": "1.1.4", "description": "Emulates Cisco's node-jose module for the browser.", "keywords": [ "crypto", From 94b6ae65ed65df3777491475eebda993f982257d Mon Sep 17 00:00:00 2001 From: Newton Joshua Date: Thu, 29 Aug 2019 13:50:46 +0530 Subject: [PATCH 09/15] include process in window object --- lib/index.js | 1 + lib/jwe/decrypt.js | 2 +- lib/jwe/encrypt.js | 2 +- package.json | 5 +++-- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/index.js b/lib/index.js index 2eab948..8232927 100644 --- a/lib/index.js +++ b/lib/index.js @@ -10,6 +10,7 @@ if (typeof Promise === "undefined") { } var global = global || window; global.Buffer = global.Buffer || require("buffer").Buffer; +global.process = global.process || require("process"); var JWS = require("./jws"); diff --git a/lib/jwe/decrypt.js b/lib/jwe/decrypt.js index 347cf5d..1af885b 100644 --- a/lib/jwe/decrypt.js +++ b/lib/jwe/decrypt.js @@ -9,7 +9,7 @@ var base64url = require("../util/base64url"), AlgConfig = require("../util/algconfig"), JWK = require("../jwk"), merge = require("../util/merge"), - zlib = require("browserify-zlib"); + zlib = require("process").browser ? require("browserify-zlib") : require("zlib"); var DEFAULT_OPTIONS = { algorithms: "*" diff --git a/lib/jwe/encrypt.js b/lib/jwe/encrypt.js index 4933f05..5e38127 100644 --- a/lib/jwe/encrypt.js +++ b/lib/jwe/encrypt.js @@ -10,7 +10,7 @@ var lodash = require("lodash"), generateCEK = require("./helpers").generateCEK, JWK = require("../jwk"), slice = require("./helpers").slice, - zlib = require("browserify-zlib"), + zlib = require("process").browser ? require("browserify-zlib") : require("zlib"), CONSTANTS = require("../algorithms/constants"); var assign = lodash.assign; diff --git a/package.json b/package.json index 4d9d70b..5551807 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-jose-browserify", - "version": "1.1.4", + "version": "1.1.5", "description": "Emulates Cisco's node-jose module for the browser.", "keywords": [ "crypto", @@ -12,7 +12,7 @@ ], "repository": { "type": "git", - "url": "https://github.com/cisco/node-jose-browserify.git" + "url": "https://github.com/NewtonJoshua/node-jose-browserify" }, "license": "Apache-2.0", "author": "Newton Joshua, Inc. ", @@ -37,6 +37,7 @@ "lodash": "^4.17.11", "long": "^4.0.0", "node-forge": "^0.8.1", + "process": "^0.11.10", "stream-browserify": "^2.0.2", "uuid": "^3.3.2" }, From 057de453232f89ae9f2cca16df0bb77c8b8eed2b Mon Sep 17 00:00:00 2001 From: Newton Joshua Date: Thu, 29 Aug 2019 14:01:13 +0530 Subject: [PATCH 10/15] support browser and node --- lib/jwe/decrypt.js | 2 +- lib/jwe/encrypt.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/jwe/decrypt.js b/lib/jwe/decrypt.js index 1af885b..5bef163 100644 --- a/lib/jwe/decrypt.js +++ b/lib/jwe/decrypt.js @@ -9,7 +9,7 @@ var base64url = require("../util/base64url"), AlgConfig = require("../util/algconfig"), JWK = require("../jwk"), merge = require("../util/merge"), - zlib = require("process").browser ? require("browserify-zlib") : require("zlib"); + zlib = require("zlib") || require("browserify-zlib"); var DEFAULT_OPTIONS = { algorithms: "*" diff --git a/lib/jwe/encrypt.js b/lib/jwe/encrypt.js index 5e38127..8af6a51 100644 --- a/lib/jwe/encrypt.js +++ b/lib/jwe/encrypt.js @@ -10,7 +10,7 @@ var lodash = require("lodash"), generateCEK = require("./helpers").generateCEK, JWK = require("../jwk"), slice = require("./helpers").slice, - zlib = require("process").browser ? require("browserify-zlib") : require("zlib"), + zlib = require("zlib") || require("browserify-zlib"), CONSTANTS = require("../algorithms/constants"); var assign = lodash.assign; From 472f7a3de7eac939bb66ebb1aa1ad9fa44c41d81 Mon Sep 17 00:00:00 2001 From: Newton Joshua Date: Thu, 29 Aug 2019 14:42:01 +0530 Subject: [PATCH 11/15] angular and node --- lib/jwe/decrypt.js | 10 ++++++++-- lib/jwe/encrypt.js | 8 +++++++- package.json | 2 +- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/jwe/decrypt.js b/lib/jwe/decrypt.js index 5bef163..6d2d0d9 100644 --- a/lib/jwe/decrypt.js +++ b/lib/jwe/decrypt.js @@ -8,8 +8,14 @@ var base64url = require("../util/base64url"), AlgConfig = require("../util/algconfig"), JWK = require("../jwk"), - merge = require("../util/merge"), - zlib = require("zlib") || require("browserify-zlib"); + merge = require("../util/merge"); + +var zlib; +try { + zlib = require("zlib") +} catch(e) { + zlib = require("browserify-zlib"); +} var DEFAULT_OPTIONS = { algorithms: "*" diff --git a/lib/jwe/encrypt.js b/lib/jwe/encrypt.js index 8af6a51..39031ec 100644 --- a/lib/jwe/encrypt.js +++ b/lib/jwe/encrypt.js @@ -10,9 +10,15 @@ var lodash = require("lodash"), generateCEK = require("./helpers").generateCEK, JWK = require("../jwk"), slice = require("./helpers").slice, - zlib = require("zlib") || require("browserify-zlib"), CONSTANTS = require("../algorithms/constants"); +var zlib; +try { + zlib = require("zlib") +} catch(e) { + zlib = require("browserify-zlib"); +} + var assign = lodash.assign; var clone = lodash.clone; diff --git a/package.json b/package.json index 5551807..b84cb80 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-jose-browserify", - "version": "1.1.5", + "version": "1.1.9", "description": "Emulates Cisco's node-jose module for the browser.", "keywords": [ "crypto", From e0312cb11e8f6e50c600bdd1bccb48bde64e6016 Mon Sep 17 00:00:00 2001 From: Newton Joshua Date: Thu, 29 Aug 2019 15:23:53 +0530 Subject: [PATCH 12/15] add comments --- README.md | 830 ++++++++++++++++++++++++++++++++++++++++++++- lib/index.js | 2 + lib/jwe/decrypt.js | 1 + lib/jwe/encrypt.js | 1 + package.json | 16 +- 5 files changed, 837 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 32cbe2b..1245615 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,830 @@ -# node-jose-browserify # +# node-jose # -Emulates Cisco's node-jose module for the browser. +[![Greenkeeper badge](https://badges.greenkeeper.io/cisco/node-jose.svg)](https://greenkeeper.io/) +[![Build Status](https://travis-ci.org/cisco/node-jose.svg?branch=master)](https://travis-ci.org/cisco/node-jose) -Refer to https://github.com/cisco/node-jose for documentation. +A JavaScript implementation of the JSON Object Signing and Encryption (JOSE) for current web browsers and node.js-based servers. This library implements (wherever possible) all algorithms, formats, and options in [JWS](https://tools.ietf.org/html/rfc7515 "Jones, M., J. Bradley and N. Sakimura, 'JSON Web Signature (JWS)' RFC 7515, May 2015"), [JWE](https://tools.ietf.org/html/rfc7516 "Jones, M. and J. Hildebrand 'JSON Web Encryption (JWE)', RFC 7516, May 2015"), [JWK](https://tools.ietf.org/html/rfc7517 "Jones, M., 'JSON Web Key (JWK)', RFC 7517, May 2015"), and [JWA](https://tools.ietf.org/html/rfc7518 "Jones, M., 'JSON Web Algorithms (JWA)', RFC 7518, May 2015") and uses native cryptographic support ([WebCrypto API](http://www.w3.org/TR/WebCryptoAPI/) or node.js' "[crypto](https://nodejs.org/api/crypto.html)" module) where feasible. + + + + +- [Installing](#installing) +- [Basics](#basics) +- [Keys and Key Stores](#keys-and-key-stores) + - [Obtaining a KeyStore](#obtaining-a-keystore) + - [Exporting a KeyStore](#exporting-a-keystore) + - [Retrieving Keys](#retrieving-keys) + - [Searching for Keys](#searching-for-keys) + - [Managing Keys](#managing-keys) + - [Importing and Exporting a Single Key](#importing-and-exporting-a-single-key) + - [Obtaining a Key's Thumbprint](#obtaining-a-keys-thumbprint) +- [Signatures](#signatures) + - [Keys Used for Signing and Verifying](#keys-used-for-signing-and-verifying) + - [Signing Content](#signing-content) + - [Verifying a JWS](#verifying-a-jws) + - [Allowing (or Disallowing) Signature Algorithms](#allowing-or-disallowing-signature-algorithms) + - [Handling `crit` Header Members](#handling-crit-header-members) +- [Encryption](#encryption) + - [Keys Used for Encrypting and Decrypting](#keys-used-for-encrypting-and-decrypting) + - [Encrypting Content](#encrypting-content) + - [Decrypting a JWE](#decrypting-a-jwe) + - [Allowing (or Disallowing) Encryption Algorithms](#allowing-or-disallowing-encryption-algorithms) + - [Handling `crit` Header Members](#handling-crit-header-members-1) +- [Useful Utilities](#useful-utilities) + - [Converting to Buffer](#converting-to-buffer) + - [URI-Safe Base64](#uri-safe-base64) + - [Random Bytes](#random-bytes) +- [Angular Usage](#angular-usage) + + + +## Installing ## + +To install the latest from [NPM](https://npmjs.com/): + +```shell + npm install node-jose +``` + +Or to install a specific release: + +```shell + npm install node-jose@0.3.0 +``` + +Alternatively, the latest unpublished code can be installed directly from the repository: + +```shell + npm install git+https://github.com/cisco/node-jose.git +``` + +## Basics ## + +Require the library as normal: + +```javascript +var jose = require('node-jose'); +``` + +This library uses [Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) for nearly every operation. + +This library supports [Browserify](http://browserify.org/) and [Webpack](https://webpack.github.io/). To use in a web browser, `require('node-jose')` and bundle with the rest of your app. + +The content to be signed/encrypted -- or returned from being verified/decrypted -- are [Buffer](https://nodejs.org/api/buffer.html) objects. + +## Keys and Key Stores ## + +The `jose.JWK` namespace deals with JWK and JWK-sets. + +* `jose.JWK.Key` is a logical representation of a JWK, and is the "raw" entry point for various cryptographic operations (e.g., sign, verify, encrypt, decrypt). +* `jose.JWK.KeyStore` represents a collection of Keys. + +Creating a JWE or JWS ultimately require one or more explicit Key objects. + +Processing a JWE or JWS relies on a KeyStore. + +### Obtaining a KeyStore ### +To create an empty keystore: + +```javascript +keystore = jose.JWK.createKeyStore(); +``` + +To import a JWK-set as a keystore: + +```javascript +// {input} is a String or JSON object representing the JWK-set +jose.JWK.asKeyStore(input). + then(function(result) { + // {result} is a jose.JWK.KeyStore + keystore = result; + }); +``` + +### Exporting a KeyStore ### + +To export the public keys of a keystore as a JWK-set: + +```javascript +output = keystore.toJSON(); +``` + +To export **all** the keys of a keystore: + +```javascript +output = keystore.toJSON(true); +``` + +### Retrieving Keys ### + +To retrieve a key from a keystore: + +```javascript +// by 'kid' +key = keystore.get(kid); +``` + +This retrieves the first key that matches the given {kid}. If multiple keys have the same {kid}, you can further narrow what to retrieve: + +```javascript +// ... and by 'kty' +key = keystore.get(kid, { kty: 'RSA' }); + +// ... and by 'use' +key = keystore.get(kid, { use: 'enc' }); + +// ... and by 'alg' +key = keystore.get(kid, { use: 'RSA-OAEP' }); + +// ... and by 'kty' and 'use' +key = keystore.get(kid, { kty: 'RSA', use: 'enc' }); + +// same as above, but with a single {props} argument +key = keystore.get({ kid: kid, kty: 'RSA', use: 'enc' }); +``` + +### Searching for Keys ### + +To retrieve all the keys from a keystore: + +```javascript +everything = keystore.all(); +``` + +`all()` can be filtered much like `get()`: + +```javascript +// filter by 'kid' +everything = keystore.all({ kid: kid }); + +// filter by 'kty' +everything = keystore.all({ kty: 'RSA' }); + +// filter by 'use' +everything = keystore.all({ use: 'enc' }); + +// filter by 'alg' +everything = keystore.all({ alg: 'RSA-OAEP' }); + +// filter by 'kid' + 'kty' + 'alg' +everything = keystore.all({ kid: kid, kty: 'RSA', alg: 'RSA-OAEP' }); +``` + +### Managing Keys ### + +To import an existing Key (as a JSON object or Key instance): + +```javascript +// input is either a: +// * jose.JWK.Key to copy from; or +// * JSON object representing a JWK; or +keystore.add(input). + then(function(result) { + // {result} is a jose.JWK.Key + key = result; + }); +``` + +To import and existing Key from a PEM or DER: + +```javascript +// input is either a: +// * String serialization of a JSON JWK/(base64-encoded) PEM/(binary-encoded) DER +// * Buffer of a JSON JWK/(base64-encoded) PEM/(binary-encoded) DER +// form is either a: +// * "json" for a JSON stringified JWK +// * "private" for a DER encoded 'raw' private key +// * "pkcs8" for a DER encoded (unencrypted!) PKCS8 private key +// * "public" for a DER encoded SPKI public key (alternate to 'spki') +// * "spki" for a DER encoded SPKI public key +// * "pkix" for a DER encoded PKIX X.509 certificate +// * "x509" for a DER encoded PKIX X.509 certificate +// * "pem" for a PEM encoded of PKCS8 / SPKI / PKIX +keystore.add(input, form). + then(function(result) { + // {result} is a jose.JWK.Key + }); +``` + +To generate a new Key: + +```javascript +// first argument is the key type (kty) +// second is the key size (in bits) or named curve ('crv') for "EC" +keystore.generate("oct", 256). + then(function(result) { + // {result} is a jose.JWK.Key + key = result; + }); + +// ... with properties +var props = { + kid: 'gBdaS-G8RLax2qgObTD94w', + alg: 'A256GCM', + use: 'enc' +}; +keystore.generate("oct", 256, props). + then(function(result) { + // {result} is a jose.JWK.Key + key = result; + }); +``` + +To remove a Key from its Keystore: +```javascript +keystore.remove(key); +// NOTE: key.keystore does not change!! +``` + +### Importing and Exporting a Single Key ### + +To create a single "stand alone" key: + +```javascript +jose.JWK.createKey("oct", 256, { alg: "A256GCM" }). + then(function(result) { + // {result} is a jose.JWK.Key + // {result.keystore} is a unique jose.JWK.KeyStore + }); +``` + + +To import a single Key: + +```javascript +// where input is either a: +// * jose.JWK.Key instance +// * JSON Object representation of a JWK +jose.JWK.asKey(input). + then(function(result) { + // {result} is a jose.JWK.Key + // {result.keystore} is a unique jose.JWK.KeyStore + }); + +// where input is either a: +// * String serialization of a JSON JWK/(base64-encoded) PEM/(binary-encoded) DER +// * Buffer of a JSON JWK/(base64-encoded) PEM/(binary-encoded) DER +// form is either a: +// * "json" for a JSON stringified JWK +// * "pkcs8" for a DER encoded (unencrypted!) PKCS8 private key +// * "spki" for a DER encoded SPKI public key +// * "pkix" for a DER encoded PKIX X.509 certificate +// * "x509" for a DER encoded PKIX X.509 certificate +// * "pem" for a PEM encoded of PKCS8 / SPKI / PKIX +jose.JWK.asKey(input, form). + then(function(result) { + // {result} is a jose.JWK.Key + // {result.keystore} is a unique jose.JWK.KeyStore + }); +``` + +To export the public portion of a Key as a JWK: + +```javascript +var output = key.toJSON(); +``` + +To export the public **and** private portions of a Key: + +```javascript +var output = key.toJSON(true); +``` + +### Obtaining a Key's Thumbprint ### + +To get or calculate a [RFC 7638](https://tools.ietf.org/html/rfc7638) thumbprint for a key: + +```javascript +// where hash is a supported algorithm, currently one of: +// * SHA-1 +// * SHA-256 +// * SHA-384 +// * SHA-512 +key.thumbprint(hash). + then(function(print) { + // {print} is a Buffer containing the thumbprint binary value + }); +``` + +When importing or generating a key that does not have a "kid" defined, a +"SHA-256" thumbprint is calculated and used as the "kid". + +## Signatures ## + +### Keys Used for Signing and Verifying ### + +When signing content, the key is expected to meet one of the following: + +1. A secret key (e.g, `"kty":"oct"`) +2. The **private** key from a PKI (`"kty":"EC"` or `"kty":"RSA"`) key pair + +When verifying content, the key is expected to meet one of the following: + +1. A secret key (e.g, `"kty":"oct"`) +2. The **public** key from a PKI (`"kty":"EC"` or `"kty":"RSA"`) key pair + + +### Signing Content ### + +At its simplest, to create a JWS: + +```javascript +// {input} is a Buffer +jose.JWS.createSign(key). + update(input). + final(). + then(function(result) { + // {result} is a JSON object -- JWS using the JSON General Serialization + }); +``` + +The JWS is signed using the preferred algorithm appropriate for the given Key. The preferred algorithm is the first item returned by `key.algorithms("sign")`. + +To create a JWS using another serialization format: + +```javascript +jose.JWS.createSign({ format: 'flattened' }, key). + update(input). + final(). + then(function(result) { + // {result} is a JSON object -- JWS using the JSON Flattened Serialization + }); + +jose.JWS.createSign({ format: 'compact' }, key). + update(input). + final(). + then(function(result) { + // {result} is a String -- JWS using the Compact Serialization + }); +``` + +To create a JWS using a specific algorithm: +```javascript +jose.JWS.createSign({ alg: 'PS256' }, key). + update(input). + final(). + then(function(result) { + // .... + }); +``` + +To create a JWS for a specified content type: + +```javascript +jose.JWS.createSign({ fields: { cty: 'jwk+json' } }, key). + update(input). + final(). + then(function(result) { + // .... + }); +``` + +To create a JWS from String content: + +```javascript +jose.JWS.createSign(key). + update(input, "utf8"). + final(). + then(function(result) { + // .... + }); +``` + +To create a JWS with multiple signatures: + +```javascript +// {keys} is an Array of jose.JWK.Key instances +jose.JWS.createSign(keys). + update(input). + final(). + then(function(result) { + // .... + }); +``` + +### Verifying a JWS ### + +To verify a JWS, and retrieve the payload: + +```javascript +jose.JWS.createVerify(keystore). + verify(input). + then(function(result) { + // {result} is a Object with: + // * header: the combined 'protected' and 'unprotected' header members + // * payload: Buffer of the signed content + // * signature: Buffer of the verified signature + // * key: The key used to verify the signature + }); +``` + +To verify using an implied Key: + +```javascript +// {key} can be: +// * jose.JWK.Key +// * JSON object representing a JWK +jose.JWS.createVerify(key). + verify(input). + then(function(result) { + // ... + }); +``` + +To verify using a key embedded in the JWS: + +```javascript +jose.JWS.createVerify(). + verify(input, { allowEmbeddedKey: true }). + then(function(result) { + // ... + }); +``` + +Alternatively, a cached `createVerify()` can be configured to allow an embedded key: + +```javascript +var verifier = jose.JWS.createVerify({ allowEmbeddedKey: true }); + +verifier.verify(input). + then(function(result) { + // ... + }); +``` + +The key can be embedded using either 'jwk' or 'x5c', and can be located in either the JWS Unprotected Header or JWS Protected Header. + +**NOTE:** `verify()` will use the embedded key (if found and permitted) instead of any other key. + +#### Allowing (or Disallowing) Signature Algorithms ### + +To restrict what signature algorithms are allowed when verifying, add the `algorithms` member to the `options` Object. The `algorithms` member is either a string or an array of strings, where the string value(s) can be one of the following: + +* `"*"`: accept all supported algorithms +* **``** (e.g., `"PS256"`): accept the specific algorithm (can have a single '*' to match a range of algorithms) +* **`!`** (e.g., `"!RS256"`): *do not* accept the specific algorithm (can have a single '*' to match a range of algorithms) + +The negation is intended to be used with the wildcard accept string, and disallow takes precedence over allowed. + +To only accept RSA-PSS sigatures: + +```javascript +var opts = { + algorithms: ["PS*"] +}; +jose.JWS.createVerify(key, opts). + verify(input). + then(function(result) { + // ... + }); +``` + +To accept any algorithm, but disallow HMAC-based signatures: + +```javascript +var opts = { + algorithms: ["*", "!HS*"] +}; +jose.JWS.createVerify(key, opts). + verify(input). + then(input). + then(function(result) { + // ... + }); +``` + +#### Handling `crit` Header Members #### + +To accept 'crit' field members, add the `handlers` member to the options Object. The `handlers` member is itself an Object, where its member names are the `crit` header member, and the value is one of: + +* `Function`: takes the JWE decrypt output (just prior to decrypting) and returns a Promise for the processing of the member. +* `Object`: An object with the following `Function` members: + * "prepare" -- takes the JWE decrypt output (just prior to decrypting) and returns a Promise for the processing of the member. + * "complete" -- takes the JWE decrypt output (immediately after decrypting) and returns a Promise for the processing of the member. + +**NOTE** If the handler function returns a promise, the fulfilled value is ignored. It is expected these handler functions will modify the provided value directly. + +To perform additional (pre-verify) processing on a `crit` header member: + +```javascript +var opts = { + handlers: { + "exp": function(jws) { + // {jws} is the JWS verify output, pre-verification + jws.header.exp = new Date(jws.header.exp); + } + } +}; +jose.JWS.createVerify(key, opts). + verify(input). + then(function(result) { + // ... + }); +``` + +To perform additional (post-verify) processing on a `crit` header member: + +```javascript +var opts = { + handlers: { + "exp": { + complete: function(jws) { + // {jws} is the JWS verify output, post-verification + jws.header.exp = new Date(jws.header.exp); + } + } + } +}; +jose.JWS.createVerify(key, opts). + verify(input). + then(function(result) { + // ... + }); +``` + + +## Encryption ## + + +### Keys Used for Encrypting and Decrypting ### + +When encrypting content, the key is expected to meet one of the following: + +1. A secret key (e.g, `"kty":"oct"`) +2. The **public** key from a PKI (`"kty":"EC"` or `"kty":"RSA"`) key pair + +When decrypting content, the key is expected to meet one of the following: + +1. A secret key (e.g, `"kty":"oct"`) +2. The **private** key from a PKI (`"kty":"EC"` or `"kty":"RSA"`) key pair + + +### Encrypting Content ### + +At its simplest, to create a JWE: + +```javascript +// {input} is a Buffer +jose.JWE.createEncrypt(key). + update(input). + final(). + then(function(result) { + // {result} is a JSON Object -- JWE using the JSON General Serialization + }); +``` + +How the JWE content is encrypted depends on the provided Key. + +* If the Key only supports content encryption algorithms, then the preferred algorithm is used to encrypt the content and the key encryption algorithm (i.e., the "alg" member) is set to "dir". The preferred algorithm is the first item returned by `key.algorithms("encrypt")`. +* If the Key supports key management algorithms, then the JWE content is encrypted using "A128CBC-HS256" by default, and the Content Encryption Key is encrypted using the preferred algorithms for the given Key. The preferred algorithm is the first item returned by `key.algorithms("wrap")`. + + +To create a JWE using a different serialization format: + +```javascript +jose.JWE.createEncrypt({ format: 'compact' }, key). + update(input). + final(). + then(function(result) { + // {result} is a String -- JWE using the Compact Serialization + }); + +jose.JWE.createEncrypt({ format: 'flattened' }, key). + update(input). + final(). + then(function(result) { + // {result} is a JSON Object -- JWE using the JSON Flattened Serialization + }); +``` + +To create a JWE and compressing the content before encrypting: + +```javascript +jose.JWE.createEncrypt({ zip: true }, key). + update(input). + final(). + then(function(result) { + // .... + }); +``` + +To create a JWE for a specific content type: + +```javascript +jose.JWE.createEncrypt({ fields: { cty : 'jwk+json' } }, key). + update(input). + final(). + then(function(result) { + // .... + }); +``` + +To create a JWE with multiple recipients: + +```javascript +// {keys} is an Array of jose.JWK.Key instances +jose.JWE.createEncrypt(keys). + update(input). + final(). + then(function(result) { + // .... + }); +``` + +### Decrypting a JWE ### + +To decrypt a JWE, and retrieve the plaintext: + +```javascript +jose.JWE.createDecrypt(keystore). + decrypt(input). + then(function(result) { + // {result} is a Object with: + // * header: the combined 'protected' and 'unprotected' header members + // * protected: an array of the member names from the "protected" member + // * key: Key used to decrypt + // * payload: Buffer of the decrypted content + // * plaintext: Buffer of the decrypted content (alternate) + }); +``` + +To decrypt a JWE using an implied key: + +```javascript +jose.JWE.createDecrypt(key). + decrypt(input). + then(function(result) { + // .... + }); +``` + +#### Allowing (or Disallowing) Encryption Algorithms ### + +To restrict what encryption algorithms are allowed when verifying, add the `algorithms` member to the `options` Object. The `algorithms` member is either a string or an array of strings, where the string value(s) can be one of the following: + +* `"*"`: accept all supported algorithms +* **``** (e.g., `"A128KW"`): accept the specific algorithm (can have a single '*' to match a range of similar algorithms) +* **`!`** (e.g., `"!RSA1_5"`): *do not* accept the specific algorithm (can have a single '*' to match a range of similar algorithms) + +The negation is intended to be used with the wildcard accept string, and disallow takes precedence over allowed. + +To only accept "dir" and AES-GCM encryption: + +```javascript +var opts = { + algorithms: ["dir", "A*GCM"] +}; +jose.JWE.createDecrypt(key, opts). + decrypt(input). + then(function(result) { + // ... + }); +``` + +To accept any algorithm, but disallow RSA-based encryption: + +```javascript +var opts = { + algorithms: ["*", "!RSA*"] +}; +jose.JWS.createVerify(key, opts). + verify(input). + then(input). + then(function(result) { + // ... + }); +``` + +#### Handling `crit` Header Members #### + +To accept 'crit' field members, add the `handlers` member to the options Object. The `handlers` member is itself an Object, where its member names are the `crit` header member, and the value is one of: + +* `Function`: takes the JWE decrypt output (just prior to decrypting) and returns a Promise for the processing of the member. +* `Object`: An object with the following `Function` members: + * "prepare" -- takes the JWE decrypt output (just prior to decrypting) and returns a Promise for the processing of the member. + * "complete" -- takes the JWE decrypt output (immediately after decrypting) and returns a Promise for the processing of the member. + +**NOTE** If the handler function returns a promise, the fulfilled value is ignored. It is expected these handler functions will modify the provided value directly. + +To perform additional (pre-decrypt) processing on a `crit` header member: + +```javascript +var opts = { + handlers: { + "exp": function(jwe) { + // {jwe} is the JWE decrypt output, pre-decryption + jwe.header.exp = new Date(jwe.header.exp); + } + } +}; +jose.JWE.createDecrypt(key, opts). + decrypt(input). + then(function(result) { + // ... + }); +``` + +To perform additional (post-decrypt) processing on a `crit` header member: + +```javascript +var opts = { + handlers: { + "exp": { + complete: function(jwe) { + // {jwe} is the JWE decrypt output, post-decryption + jwe.header.exp = new Date(jwe.header.exp); + } + } + } +}; +jose.JWE.createDecrypt(key, opts). + decrypt(input). + then(function(result) { + // ... + }); +``` + +## Useful Utilities ## + +### Converting to Buffer ### + +To convert a [Typed Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays), [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer), or Array of Numbers to a Buffer: + +```javascript +buff = jose.util.asBuffer(input); +``` + +### URI-Safe Base64 ### + +This exposes [urlsafe-base64](https://github.com/RGBboy/urlsafe-base64)'s `encode` and `decode` methods as `encode` and `decode` (respectively). + +To convert from a Buffer to a base64uri-encoded String: + +```javascript +var output = jose.util.base64url.encode(input); +``` + +To convert a String to a base64uri-encoded String: + +```javascript +// explicit encoding +output = jose.util.base64url.encode(input, "utf8"); + +// implied "utf8" encoding +output = jose.util.base64url.encode(input); +``` + +To convert a base64uri-encoded String to a Buffer: + +```javascript +var output = jose.util.base64url.decode(input); +``` + +### Random Bytes ### + +To generate a Buffer of octets, regardless of platform: + +```javascript +// argument is size (in bytes) +var rnd = jose.util.randomBytes(32); +``` + +This function uses: + +* `crypto.randomBytes()` on node.js +* `crypto.getRandomValues()` on modern browsers +* A PRNG based on AES and SHA-1 for older platforms + + +### Angular Usage ### + +Import and use as any other package. All the methods will be supported in the browser, just as in node.js. + +```javascript +import * as jose from 'node-jose'; +``` + +The following changes are required to make few node-modules available in the browser + +- angular compilerOptions for stream + + In `tsconfig.json` , *compilerOptions* add + ```json + "paths": { + "stream": ["../node_modules/stream-browserify/index.js"] + } + ``` + This is to avoid the below error + > ERROR in ./node_modules/browserify-zlib/lib/index.js. + > Module not found: Error: Can't resolve 'stream' in '***/node_modules/browserify-zlib/lib' + +- polyfil for global object + + In `polyfills.ts` add + ``` + // Polyfill for node-jose + (window as any)['global'] = window; + ``` + This is to avoid the below error + > Uncaught ReferenceError: global is not defined \ No newline at end of file diff --git a/lib/index.js b/lib/index.js index 8232927..7309fbc 100644 --- a/lib/index.js +++ b/lib/index.js @@ -9,7 +9,9 @@ if (typeof Promise === "undefined") { require("es6-promise").polyfill(); } var global = global || window; +// Browser Support: make node's Buffer API available globally n the browser global.Buffer = global.Buffer || require("buffer").Buffer; +// Browser Support: make node's Process API available globally n the browser global.process = global.process || require("process"); var JWS = require("./jws"); diff --git a/lib/jwe/decrypt.js b/lib/jwe/decrypt.js index 6d2d0d9..ef5026c 100644 --- a/lib/jwe/decrypt.js +++ b/lib/jwe/decrypt.js @@ -11,6 +11,7 @@ var base64url = require("../util/base64url"), merge = require("../util/merge"); var zlib; +// Browser Support: If 'zlib' can't be resolved use browserify-zlib that emulates Node's zlib module for the browser try { zlib = require("zlib") } catch(e) { diff --git a/lib/jwe/encrypt.js b/lib/jwe/encrypt.js index 39031ec..6925bec 100644 --- a/lib/jwe/encrypt.js +++ b/lib/jwe/encrypt.js @@ -13,6 +13,7 @@ var lodash = require("lodash"), CONSTANTS = require("../algorithms/constants"); var zlib; +// Browser Support: If 'zlib' can't be resolved use browserify-zlib that emulates Node's zlib module for the browser try { zlib = require("zlib") } catch(e) { diff --git a/package.json b/package.json index b84cb80..96be550 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "node-jose-browserify", - "version": "1.1.9", - "description": "Emulates Cisco's node-jose module for the browser.", + "name": "node-jose", + "version": "1.1.4", + "description": "A JavaScript implementation of the JSON Object Signing and Encryption (JOSE) for current web browsers and node.js-based servers", "keywords": [ "crypto", "jose", @@ -12,10 +12,10 @@ ], "repository": { "type": "git", - "url": "https://github.com/NewtonJoshua/node-jose-browserify" + "url": "https://github.com/cisco/node-jose.git" }, "license": "Apache-2.0", - "author": "Newton Joshua, Inc. ", + "author": "Cisco Systems, Inc. ", "contributors": [ "Matthew A. Miller ", "Ian W. Remmel ", @@ -31,14 +31,10 @@ }, "dependencies": { "base64url": "^3.0.1", - "browserify-zlib": "^0.2.0", - "buffer": "^5.4.2", "es6-promise": "^4.2.6", "lodash": "^4.17.11", "long": "^4.0.0", "node-forge": "^0.8.1", - "process": "^0.11.10", - "stream-browserify": "^2.0.2", "uuid": "^3.3.2" }, "devDependencies": { @@ -77,4 +73,4 @@ "webpack-stream": "^4.0.0", "yargs": "^11.1.0" } -} +} \ No newline at end of file From 90f7753c3ca0c266ece61485e2beda8c0e7817a5 Mon Sep 17 00:00:00 2001 From: Newton Joshua Date: Thu, 29 Aug 2019 15:28:13 +0530 Subject: [PATCH 13/15] Update package.json --- package.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/package.json b/package.json index 96be550..ce898e9 100644 --- a/package.json +++ b/package.json @@ -31,10 +31,14 @@ }, "dependencies": { "base64url": "^3.0.1", + "browserify-zlib": "^0.2.0", + "buffer": "^5.4.2", "es6-promise": "^4.2.6", "lodash": "^4.17.11", "long": "^4.0.0", "node-forge": "^0.8.1", + "process": "^0.11.10", + "stream-browserify": "^2.0.2", "uuid": "^3.3.2" }, "devDependencies": { From d8315c02404f30cd7456bc6acf02283560ae5da5 Mon Sep 17 00:00:00 2001 From: Newton Joshua Date: Thu, 29 Aug 2019 15:46:06 +0530 Subject: [PATCH 14/15] handle windows in node.js --- lib/index.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/index.js b/lib/index.js index 7309fbc..8c858c7 100644 --- a/lib/index.js +++ b/lib/index.js @@ -8,11 +8,18 @@ if (typeof Promise === "undefined") { require("es6-promise").polyfill(); } -var global = global || window; -// Browser Support: make node's Buffer API available globally n the browser -global.Buffer = global.Buffer || require("buffer").Buffer; -// Browser Support: make node's Process API available globally n the browser -global.process = global.process || require("process"); + +try { // for browsers + var global = global || window; + // Browser Support: make node's Buffer API available globally in the browser + global.Buffer = global.Buffer || require("buffer").Buffer; + // Browser Support: make node's Process API available globally in the browser + global.process = global.process || require("process"); +} +catch (e) { + // "window is not defined" for node.js +} + var JWS = require("./jws"); From 0c83843eecccb9e00cec9a9f181b0813b6085d61 Mon Sep 17 00:00:00 2001 From: Newton Joshua Date: Thu, 29 Aug 2019 15:57:13 +0530 Subject: [PATCH 15/15] Update index.js --- lib/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/index.js b/lib/index.js index 8c858c7..3d9a056 100644 --- a/lib/index.js +++ b/lib/index.js @@ -11,9 +11,9 @@ if (typeof Promise === "undefined") { try { // for browsers var global = global || window; - // Browser Support: make node's Buffer API available globally in the browser + // Browser Support: make buffer, that emulates node's Buffer API, available globally in the browser global.Buffer = global.Buffer || require("buffer").Buffer; - // Browser Support: make node's Process API available globally in the browser + // Browser Support: make process, that emulates node's Process API, available globally in the browser global.process = global.process || require("process"); } catch (e) {