From a4c599468bdcc5fc23ad3660629c71ab68fbb10d Mon Sep 17 00:00:00 2001 From: "Phelan, Ross" Date: Thu, 14 Jul 2022 16:21:06 +0100 Subject: [PATCH 1/2] Adding ability to chose dataEncoding for publicKeyFingerPrint when doing JWE encryption Removing duplicate code --- README.md | 6 ++--- lib/mcapi/crypto/jwe-crypto.js | 15 ++++++++++-- .../encryption/field-level-encryption.js | 9 +------ lib/mcapi/encryption/jwe-encryption.js | 9 +------ lib/mcapi/utils/utils.js | 12 ++++++++++ test/jwe-crypto.test.js | 24 ++++++++++++++++--- test/mock/jwe-config.js | 1 + 7 files changed, 52 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 9a9a563..90d4e99 100644 --- a/README.md +++ b/README.md @@ -290,7 +290,7 @@ const config = { ], mode: "JWE", encryptedValueFieldName: "encryptedData", - publicKeyFingerprintType: "certificate", + publicKeyFingerprintType: "publicKey", encryptionCertificate: "./path/to/public.cert", privateKey: "./path/to/your/private.key", }; @@ -394,7 +394,7 @@ const config = { ], mode: "JWE", encryptedValueFieldName: "encryptedData", - publicKeyFingerprintType: "certificate", + publicKeyFingerprintType: "publicKey", encryptionCertificate: "./path/to/public.cert", privateKey: "./path/to/your/private.key", }; @@ -443,7 +443,7 @@ const config = { ], mode: "JWE", encryptedValueFieldName: "encryptedData", - publicKeyFingerprintType: "certificate", + publicKeyFingerprintType: "publicKey", encryptionCertificate: "./path/to/public.cert", privateKey: "./path/to/your/private.key", }; diff --git a/lib/mcapi/crypto/jwe-crypto.js b/lib/mcapi/crypto/jwe-crypto.js index 2cfc311..39eaccc 100644 --- a/lib/mcapi/crypto/jwe-crypto.js +++ b/lib/mcapi/crypto/jwe-crypto.js @@ -205,7 +205,7 @@ function computePublicFingerprint(config, encryptionCertificate) { return utils.computePublicFingerprint( config, forge.pki.certificateFromPem(encryptionCertificate), - c.BASE64 + config.dataEncoding ); } else { return null; @@ -232,6 +232,7 @@ function isValidConfig(config) { */ function validateFingerprint(config, contains) { const propertiesFingerprint = ["publicKeyFingerprintType"]; + const propertiesOptionalDataEncoding = ["dataEncoding"]; const propertiesOptionalFingerprint = ["publicKeyFingerprint"]; if ( !contains(propertiesOptionalFingerprint) && @@ -239,7 +240,17 @@ function validateFingerprint(config, contains) { config[propertiesFingerprint[0]] !== "publicKey" ) { throw Error( - "Config not valid: propertiesFingerprint should be: 'certificate' or 'publicKey'" + "Config not valid: publicKeyFingerprintType should be: 'certificate' or 'publicKey'" + ); + } + if ( + !contains(propertiesOptionalFingerprint) && + config[propertiesFingerprint[0]] === "certificate" && + !(config[propertiesOptionalDataEncoding[0]] === c.BASE64 || + config[propertiesOptionalDataEncoding[0]] === c.HEX) + ) { + throw Error( + "Config not valid: if publicKeyFingerprintType is 'certificate' dataEncoding must be either 'base64' or 'hex'" ); } } diff --git a/lib/mcapi/encryption/field-level-encryption.js b/lib/mcapi/encryption/field-level-encryption.js index adc5914..f836fa1 100644 --- a/lib/mcapi/encryption/field-level-encryption.js +++ b/lib/mcapi/encryption/field-level-encryption.js @@ -103,14 +103,7 @@ function encryptBody(path, body) { const elem = utils.elemFromPath(path.element, body); if (elem && elem.node) { const encryptedData = this.crypto.encryptData({ data: elem.node }); - body = utils.mutateObjectProperty(path.obj, encryptedData, body); - // delete encrypted field if not overridden - if ( - !utils.isJsonRoot(path.obj) && - path.element !== path.obj + "." + this.config.encryptedValueFieldName - ) { - utils.deleteNode(path.element, body); - } + body = utils.addEncryptedDataToBody(encryptedData, path, this.config.encryptedValueFieldName, body); } return body; } diff --git a/lib/mcapi/encryption/jwe-encryption.js b/lib/mcapi/encryption/jwe-encryption.js index 736baf5..6b2f3e4 100644 --- a/lib/mcapi/encryption/jwe-encryption.js +++ b/lib/mcapi/encryption/jwe-encryption.js @@ -67,14 +67,7 @@ function encryptBody(path, body) { const elem = utils.elemFromPath(path.element, body); if (elem && elem.node) { const encryptedData = this.crypto.encryptData({ data: elem.node }); - body = utils.mutateObjectProperty(path.obj, encryptedData, body); - // delete encrypted field if not overridden - if ( - !utils.isJsonRoot(path.obj) && - path.element !== path.obj + "." + this.config.encryptedValueFieldName - ) { - utils.deleteNode(path.element, body); - } + body = utils.addEncryptedDataToBody(encryptedData, path, this.config.encryptedValueFieldName, body); } return body; } diff --git a/lib/mcapi/utils/utils.js b/lib/mcapi/utils/utils.js index 0771d3f..3eefacb 100644 --- a/lib/mcapi/utils/utils.js +++ b/lib/mcapi/utils/utils.js @@ -415,5 +415,17 @@ function hasEncryptionParam(encParams, bodyMap) { return encParams && encParams.length === 1 && bodyMap && bodyMap[0]; } +module.exports.addEncryptedDataToBody = function(encryptedData, path, encryptedValueFieldName, body) { + body = this.mutateObjectProperty(path.obj, encryptedData, body); + if ( + !isJsonRoot(path.obj) && + path.element !== path.obj + "." + encryptedValueFieldName + ) { + this.deleteNode(path.element, body); + } + return body; +}; + + diff --git a/test/jwe-crypto.test.js b/test/jwe-crypto.test.js index 3329e1d..ee5afe3 100644 --- a/test/jwe-crypto.test.js +++ b/test/jwe-crypto.test.js @@ -92,7 +92,7 @@ describe("JWE Crypto", () => { delete config["publicKeyFingerprintType"]; assert.throws( () => new Crypto(config), - /Config not valid: propertiesFingerprint should be: 'certificate' or 'publicKey'/ + /Config not valid: publicKeyFingerprintType should be: 'certificate' or 'publicKey'/ ); }); @@ -108,16 +108,34 @@ describe("JWE Crypto", () => { config.publicKeyFingerprintType = "foobar"; assert.throws( () => new Crypto(config), - /Config not valid: propertiesFingerprint should be: 'certificate' or 'publicKey'/ + /Config not valid: publicKeyFingerprintType should be: 'certificate' or 'publicKey'/ ); }); - it("with right publicKeyFingerprintType: certificate", () => { + it("with right publicKeyFingerprintType: certificate and dataEncoding: base64", () => { const config = JSON.parse(JSON.stringify(testConfig)); config.publicKeyFingerprintType = "certificate"; + config.dataEncoding = "base64"; assert.doesNotThrow(() => new Crypto(config)); }); + it("with right publicKeyFingerprintType: certificate and dataEncoding: hex", () => { + const config = JSON.parse(JSON.stringify(testConfig)); + config.publicKeyFingerprintType = "certificate"; + config.dataEncoding = "hex"; + assert.doesNotThrow(() => new Crypto(config)); + }); + + it("with right publicKeyFingerprintType: certificate and dataEncoding: null", () => { + const config = JSON.parse(JSON.stringify(testConfig)); + config.publicKeyFingerprintType = "certificate"; + delete config["dataEncoding"]; + assert.throws( + () => new Crypto(config), + /Config not valid: if publicKeyFingerprintType is 'certificate' dataEncoding must be either 'base64' or 'hex'/ + ); + }); + it("with right publicKeyFingerprintType: publicKey", () => { const config = JSON.parse(JSON.stringify(testConfig)); config.publicKeyFingerprintType = "publicKey"; diff --git a/test/mock/jwe-config.js b/test/mock/jwe-config.js index 597794b..482e80d 100644 --- a/test/mock/jwe-config.js +++ b/test/mock/jwe-config.js @@ -64,6 +64,7 @@ module.exports = { mode: "JWE", encryptedValueFieldName: "encryptedData", publicKeyFingerprintType: "certificate", + dataEncoding: "base64", encryptionCertificate: "./test/res/test_certificate.cert", privateKey: "./test/res/test_key.der", }; From fcad0286386864af5c3594f7ad4d6e16f732dace Mon Sep 17 00:00:00 2001 From: "Phelan, Ross" Date: Thu, 14 Jul 2022 18:16:46 +0100 Subject: [PATCH 2/2] If publicKeyFingerprintType not defined defaults to pulling the publicKeyFingerprint out as a publicKey --- README.md | 3 --- lib/mcapi/crypto/jwe-crypto.js | 18 ++++++++++++------ lib/mcapi/utils/utils.js | 6 +++--- test/jwe-crypto.test.js | 22 +++++++++++++--------- 4 files changed, 28 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 90d4e99..7682b98 100644 --- a/README.md +++ b/README.md @@ -290,7 +290,6 @@ const config = { ], mode: "JWE", encryptedValueFieldName: "encryptedData", - publicKeyFingerprintType: "publicKey", encryptionCertificate: "./path/to/public.cert", privateKey: "./path/to/your/private.key", }; @@ -394,7 +393,6 @@ const config = { ], mode: "JWE", encryptedValueFieldName: "encryptedData", - publicKeyFingerprintType: "publicKey", encryptionCertificate: "./path/to/public.cert", privateKey: "./path/to/your/private.key", }; @@ -443,7 +441,6 @@ const config = { ], mode: "JWE", encryptedValueFieldName: "encryptedData", - publicKeyFingerprintType: "publicKey", encryptionCertificate: "./path/to/public.cert", privateKey: "./path/to/your/private.key", }; diff --git a/lib/mcapi/crypto/jwe-crypto.js b/lib/mcapi/crypto/jwe-crypto.js index 39eaccc..6f71755 100644 --- a/lib/mcapi/crypto/jwe-crypto.js +++ b/lib/mcapi/crypto/jwe-crypto.js @@ -202,11 +202,17 @@ function getPrivateKey(config) { */ function computePublicFingerprint(config, encryptionCertificate) { if (config && encryptionCertificate) { - return utils.computePublicFingerprint( - config, - forge.pki.certificateFromPem(encryptionCertificate), - config.dataEncoding - ); + if(config.publicKeyFingerprintType) { + return utils.computePublicFingerprint( + config, + forge.pki.certificateFromPem(encryptionCertificate), + config.dataEncoding + ); + } else { + return utils.publicKeyFingerprint( + forge.pki.certificateFromPem(encryptionCertificate) + ); + } } else { return null; } @@ -235,7 +241,7 @@ function validateFingerprint(config, contains) { const propertiesOptionalDataEncoding = ["dataEncoding"]; const propertiesOptionalFingerprint = ["publicKeyFingerprint"]; if ( - !contains(propertiesOptionalFingerprint) && + contains(propertiesFingerprint) && config[propertiesFingerprint[0]] !== "certificate" && config[propertiesFingerprint[0]] !== "publicKey" ) { diff --git a/lib/mcapi/utils/utils.js b/lib/mcapi/utils/utils.js index 3eefacb..8d68c9a 100644 --- a/lib/mcapi/utils/utils.js +++ b/lib/mcapi/utils/utils.js @@ -273,7 +273,7 @@ module.exports.computePublicFingerprint = function ( ); break; case "publicKey": - fingerprint = publicKeyFingerprint(encryptionCertificate); + fingerprint = this.publicKeyFingerprint(encryptionCertificate); break; } } @@ -289,13 +289,13 @@ function publicCertificateFingerprint(publicCertificate, encoding) { return bytesToString(md.digest().getBytes(), encoding); } -function publicKeyFingerprint(publicCertificate) { +module.exports.publicKeyFingerprint = function(publicCertificate) { return forge.pki.getPublicKeyFingerprint(publicCertificate.publicKey, { type: "SubjectPublicKeyInfo", md: createMessageDigest("SHA-256"), encoding: c.HEX, }); -} +}; function createMessageDigest(digest) { switch (digest.toUpperCase()) { diff --git a/test/jwe-crypto.test.js b/test/jwe-crypto.test.js index ee5afe3..2fabf33 100644 --- a/test/jwe-crypto.test.js +++ b/test/jwe-crypto.test.js @@ -87,15 +87,6 @@ describe("JWE Crypto", () => { }); }); - it("without publicKeyFingerprintType", () => { - const config = JSON.parse(JSON.stringify(testConfig)); - delete config["publicKeyFingerprintType"]; - assert.throws( - () => new Crypto(config), - /Config not valid: publicKeyFingerprintType should be: 'certificate' or 'publicKey'/ - ); - }); - it("without publicKeyFingerprintType, but providing the publicKeyFingerprint", () => { const config = JSON.parse(JSON.stringify(testConfig)); delete config["publicKeyFingerprintType"]; @@ -267,5 +258,18 @@ describe("JWE Crypto", () => { }) ); }); + + it("compute public fingerprint: defaults to publicKey with publicKeyFingerprintType set", () => { + const strippedConfig = JSON.parse(JSON.stringify(testConfig)); + delete strippedConfig["publicKeyFingerprintType"]; + delete strippedConfig["dataEncoding"]; + + assert.ok( + "80810fc13a8319fcf0e2ec322c82a4c304b782cc3ce671176343cfe8160c2279", + computePublicFingerprint.call(crypto, { + strippedConfig + }) + ); + }); }); });