1. Digging the code
The first step is to reverse the code from NodeRED, simple steps
1. Clone the sources
git clone https://github.com/node-red/node-red.git
2. Dig with grep in the code
grep -ril crypto packages/
packages/node_modules/@node-red/editor-api/lib/auth/index.js
packages/node_modules/@node-red/editor-api/lib/auth/strategies.js
packages/node_modules/@node-red/editor-api/lib/auth/tokens.js
packages/node_modules/@node-red/editor-api/lib/editor/comms.js
packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/monaco.js
packages/node_modules/@node-red/editor-client/src/types/README.md
packages/node_modules/@node-red/editor-client/src/types/node/crypto.d.ts
packages/node_modules/@node-red/editor-client/src/types/node/tls.d.ts
packages/node_modules/@node-red/editor-client/src/vendor/monaco/dist/editor.js
packages/node_modules/@node-red/editor-client/src/vendor/monaco/dist/html.worker.js
packages/node_modules/@node-red/editor-client/src/vendor/monaco/dist/ts.worker.js
packages/node_modules/@node-red/nodes/core/common/60-link.js
packages/node_modules/@node-red/nodes/core/network/21-httprequest.js
packages/node_modules/@node-red/runtime/lib/nodes/credentials.js
packages/node_modules/@node-red/runtime/lib/storage/index.js
packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/git/authServer.js
packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/index.js
packages/node_modules/node-red/red.js
- Find the interesting parts
packages/node_modules/@node-red/runtime/lib/nodes/credentials.js
file name looks promising.
function decryptCredentials(key,credentials) {
var creds = credentials["$"];
var initVector = Buffer.from(creds.substring(0, 32),'hex');
creds = creds.substring(32);
var decipher = crypto.createDecipheriv(encryptionAlgorithm, key, initVector);
var decrypted = decipher.update(creds, 'base64', 'utf8') + decipher.final('utf8');
return JSON.parse(decrypted);
}
and
var defaultKey;
try {
defaultKey = settings.get('_credentialSecret');
} catch(err) {
}
if (defaultKey) {
defaultKey = crypto.createHash('sha256').update(defaultKey).digest();
encryptionKeyType = "system";
}
and
// Check if we have a generated _credSecret to decrypt with and remove
if (defaultKey) {
log.debug("red/runtime/nodes/credentials.load : default key present. Will migrate");
if (credentialsEncrypted) {
try {
credentials = decryptCredentials(defaultKey,credentials)
} catch(err) {
credentials = {};
log.warn(log._("nodes.credentials.error",{message:err.toString()}))
var error = new Error("Failed to decrypt credentials");
error.code = "credentials_load_failed";
throw error;
}
}
dirty = true;
removeDefaultKey = true;
}
- The key come from
_credentialSecret
which is in the file.config.runtime.json
- Then the key is hashed as sha256 and pass to
decryptCredentials()
function decryptCredentials()
extract the 32 first characters and taken them as the Initial Vector- The end of creds string is a base64 encoded, and the content is encrypted aes
2. Openssl is a pain
The command openssl enc -base64 -d
DOESN’T DO THE SAME AS base64 -d
Because you need -A
one line weird option.
openssl enc -base64 -d -A
does the same as base64 -d
So if you need to decrypt a encoded base64 payload you need the -a and -A options like
openssl enc -aes-256-ctr -d -a -A
Our key is jq -j '._credentialSecret' $1/.config.runtime.json | sha256sum | cut -c 1-64
( pay attention to the -j
option of jq
command for not passing a breakline to sha256sum).
Our IV is jq -r '.["$"]' $1/flows_cred.json | cut -c 1-32
.
And our aes data is jq '.["$"]' -j $1/flows_cred.json | cut -c 33-
.
3. Final script
#!/bin/bash
#
# Decrypt flows_cred.json from a NodeRED data directory
#
# Usage
# ./node-red-decrypt-flows-cred.sh ./node_red_data
#
jq '.["$"]' -j $1/flows_cred.json | \
cut -c 33- | \
openssl enc -aes-256-ctr -d -base64 -A -iv `jq -r '.["$"]' $1/flows_cred.json | cut -c 1-32` -K `jq -j '._credentialSecret' $1/.config.runtime.json | sha256sum | cut -c 1-64`
0 Comments