pythonjavaencryptionaespbkdf2

Java vs Python AES PBKDF2


I'm attempting to decrypt some values in an Apache Nifi (1.23.1) flow.xml.gz (or flow.json.gz) file in Python. From doing some patching of the Java files, I've found the following information:

Field Value
Algorithm NIFI_PBKDF2_AES_GCM_256
Password 1+LFssX4whxz9lOPQ9OS7g4NvQzbCe8j
binary as described in CipherPropertyEncryptor.java 697a84312aac99fbe2315f0637c960352242e26ffd3e2c33c298f06730fffa9687cb0d845f57c1a645
cipherbinary as described in CipherPropertyEncryptor.java 2242e26ffd3e2c33c298f06730fffa9687cb0d845f57c1a645
Salt NiFi Static Salt
Iterations 160,000
Decrypted Value Changeit!

When trying to write a decryption program in Python, I used https://asecuritysite.com/encryption/aes_gcm2 as a guide.

I have the following code, which results in a ValueError: MAC check failed

# https://asecuritysite.com/encryption/aes_gcm2

from Crypto.Cipher import AES
import hashlib
from Crypto.Protocol.KDF import PBKDF2
from Crypto.Hash import SHA256, SHA512

# plaintext='Changeit!'
password='1+LFssX4whxz9lOPQ9OS7g4NvQzbCe8j'
salt = b'NiFi Static Salt'

def decrypt(ciphertext, key, mode):
  (ciphertext, authTag, nonce) = ciphertext
  encobj = AES.new(key,  mode, nonce)
  return(encobj.decrypt_and_verify(ciphertext, authTag))

key = hashlib.sha256(password.encode()).digest()

# /home/nifi/nifi/nifi-commons/nifi-security-crypto-key/src/main/java/org/apache/nifi/security/crypto/key/pbkdf2/Pbkdf2DerivedKeyProvider.java says sha512 hmac
# /home/nifi/nifi/nifi-nar-bundles/nifi-cipher-bundle/nifi-cipher-processors/src/main/java/org/apache/nifi/processors/cipher/DecryptContent.java key length bytes 16
key = PBKDF2(password, salt, 16, count=160000, hmac_hash_module=SHA512)
# key = PBKDF2(password, salt, 32, count=160000, hmac_hash_module=SHA256) # from original code example

# 697a84312aac99fbe2315f0637c960352242e26ffd3e2c33c298f06730fffa9687cb0d845f57c1a645 from a real run, should decrypt to Changeit!
#                                 2242e26ffd3e2c33c298f06730fffa9687cb0d845f57c1a645 cipherbinary from java

# in java, IV in /home/nifi/nifi/nifi-commons/nifi-property-encryptor/src/main/java/org/apache/nifi/encrypt/KeyedCipherPropertyEncryptor.java is 697a84312aac99fbe2315f0637c96035   
ciphertext = (b'697a84312aac99fbe2315f0637c96035', b'2242e26ffd3e2c33c2', b'98f06730fffa9687cb0d845f57c1a645')

res = decrypt(ciphertext,key,AES.MODE_GCM)
print ("\n\nDecrypted:\t",res.decode())

Java Vs python AES PBKDF2. Why different output? also looks to be relevant, but w/o the original code it's hard to follow what the issues was and how it was resolved.

How can I fix this decryption method to get the correct value?


Solution

  • Nonce, ciphertext and tag must be hex decoded. In addition, the values for the actual ciphertext, the authentication tag, and the nonce are incorrectly assigned (correct: ciphertext -> 0x2242..., tag -> 0x98f0..., nonce -> 0x697a...). And the key generated with PBKDF2 must be 32 bytes (not 16 bytes).

    The following code, which fixes these bugs, decrypts the ciphertext correctly:

    from Crypto.Cipher import AES
    from Crypto.Hash import SHA512
    from Crypto.Protocol.KDF import PBKDF2
    
    # plaintext='Changeit!'
    password='1+LFssX4whxz9lOPQ9OS7g4NvQzbCe8j'
    salt = b'NiFi Static Salt'
    
    def decrypt(ciphertext, key, mode):
      (ciphertext, authTag, nonce) = ciphertext
      encobj = AES.new(key,  mode, nonce)
      return(encobj.decrypt_and_verify(ciphertext, authTag))
      
    key = PBKDF2(password, salt, 32, count=160000, hmac_hash_module=SHA512) # 32 bytes key
    ciphertext = (
        bytes.fromhex('2242e26ffd3e2c33c2'),                # actual ciphertext 
        bytes.fromhex('98f06730fffa9687cb0d845f57c1a645'),  # authentication tag
        bytes.fromhex('697a84312aac99fbe2315f0637c96035')   # nonce
    ) 
    
    res = decrypt(ciphertext, key, AES.MODE_GCM)
    print ("\n\nDecrypted:\t",res.decode()) # Decrypted:   Changeit!