- Python 100%
| LICENSE | ||
| nifi-decrypt.py | ||
| README.md | ||
| requirements-minimal.txt | ||
| requirements.txt | ||
nifi-decrypt
Recover plaintext sensitive properties from Apache NiFi flow files.
A Python reimplementation of NiFi's internal PropertyEncryptor (covering both the legacy and modern KDF schemes) so you can decrypt enc{...} values from flow.xml.gz and flow.json.gz without standing up a JVM or running the official encrypt-config toolkit.
This tool was totally vibe coded, so take it with a grain of salt.
Use cases
- Pentesting / red team engagements where you've recovered NiFi config files and want to extract secrets without running NiFi itself.
- Disaster recovery when you've lost access to a NiFi instance but still hold the flow file and
nifi.sensitive.props.key. - Migration / audit scenarios where you need to inspect what credentials a flow is referencing.
Scope and ethics
This tool only decrypts data when you already possess the symmetric key (nifi.sensitive.props.key). It does not break NiFi's encryption — it just performs the same decryption NiFi would perform if it were running. Use it on systems you own or are authorized to assess.
Supported algorithms
All values of nifi.sensitive.props.algorithm from NiFi 1.6 through 1.x:
| Algorithm | Notes |
|---|---|
NIFI_PBKDF2_AES_GCM_256 |
Default since NiFi 1.14 |
NIFI_PBKDF2_AES_GCM_128 |
|
NIFI_ARGON2_AES_GCM_256 |
Requires argon2-cffi |
NIFI_ARGON2_AES_GCM_128 |
Requires argon2-cffi |
NIFI_BCRYPT_AES_GCM_256 |
Requires bcrypt |
NIFI_BCRYPT_AES_GCM_128 |
Requires bcrypt |
NIFI_SCRYPT_AES_GCM_256 |
|
NIFI_SCRYPT_AES_GCM_128 |
|
PBEWITHMD5AND256BITAES-CBC-OPENSSL |
Legacy, pre-1.14 |
PBEWITHMD5AND128BITAES-CBC-OPENSSL |
Legacy |
Both flow.xml.gz and flow.json.gz are supported, gzipped or plain.
Install
git clone https://github.com/<you>/nifi-decrypt
cd nifi-decrypt
pip install -r requirements.txt
If you know your target uses PBKDF2 or the legacy scheme (the most common cases), requirements-minimal.txt is enough and avoids pulling in argon2-cffi and bcrypt.
Usage
The simplest invocation: pass the flow file and nifi.properties. The script reads the key and algorithm out of the properties file automatically.
python3 nifi_decrypt.py /path/to/flow.xml.gz --properties /path/to/nifi.properties
If you only have the key string (e.g. lifted from somewhere other than the canonical nifi.properties):
python3 nifi_decrypt.py flow.xml.gz --key 'TUHh+YHA30zmdlcA8xq/elNBLPkO03Nl'
The default algorithm when --key is used (without --algorithm) is NIFI_PBKDF2_AES_GCM_256, since that is the NiFi 1.14+ default.
Other options
--algorithm, -a Override the algorithm (e.g. NIFI_ARGON2_AES_GCM_256)
--format, -f Force a parser: auto (default), xml, json, regex
--show-blob Print the full enc{...} hex along with each plaintext
--format regex skips structural parsing and just scans for enc{...} patterns. Useful if you have a custom or partially-corrupted flow file that breaks the XML/JSON parser.
Caveats
Bootstrap-protected keys. If your nifi.properties shows nifi.sensitive.props.key.protected=aes/gcm/256 (or similar), the key value itself is encrypted with the bootstrap root key from bootstrap.conf (nifi.bootstrap.sensitive.key). The script detects this and fails with a clear error. You'll need to unwrap the bootstrap layer first (different format, raw 256-bit key, no PBKDF2). PRs welcome.
NiFi 2.x. The script targets the 1.x scheme. NiFi 2.x dropped flow.xml.gz in favor of flow.json.gz but the encryption inside enc{...} appears to be the same scheme. It hasn't been tested against a real 2.x flow file — if you run into issues, please open an issue with a redacted sample.
Format detection edge cases. XML vs JSON is sniffed from the first non-whitespace byte. If your file has a BOM or leading whitespace that confuses this, force the parser with --format xml or --format json.
Verification
The script's PBKDF2 implementation has been verified end-to-end against a real flow.xml.gz blob from a NiFi 1.21 instance. The other three modern KDFs and the legacy CBC path have been round-trip tested against synthetic samples encrypted with the same logic. If you run this against real samples encrypted with Argon2/bcrypt/scrypt, please consider sending an (anonymized) test vector — additional ground-truth coverage is always welcome.
License
MIT.