Support Questions

Find answers, ask questions, and share your expertise

building a encrypt Content-like script for NiFi with PGP Encryption

avatar
Explorer

I need to encrypt some content with PGP, but i can't use the processor Encrypt Content because we require that the "Keyring file route" and the "passphrase" must be entered through attributes (We must use an old version of NiFi). I make this script using the repo of NiFi and modifying the things that i need and using a single script file considering how the processor works. My problem is that the result can't be decrypted backwards.
I'm using Nifi 1.13.2
With Groovy as language
and Bouncy Castle (bcpg-jdk15on-1.70 and bcprov-jdk15on-1.70)
And my code is:

 

import org.bouncycastle.bcpg.ArmoredOutputStream
import org.bouncycastle.openpgp.PGPCompressedData
import org.bouncycastle.openpgp.PGPCompressedDataGenerator
import org.bouncycastle.openpgp.PGPEncryptedData
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator
import org.bouncycastle.openpgp.PGPException
import org.bouncycastle.openpgp.PGPLiteralData
import org.bouncycastle.openpgp.PGPPublicKey
import org.bouncycastle.openpgp.PGPLiteralDataGenerator
import org.bouncycastle.openpgp.operator.PGPKeyEncryptionMethodGenerator
import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator
import org.bouncycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder
import org.bouncycastle.openpgp.PGPPublicKeyRing
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator
import org.bouncycastle.jce.provider.BouncyCastleProvider

import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.security.SecureRandom
import java.util.Date
import java.util.zip.Deflater

FlowFile flowFile = session.get()

if (!flowFile) {
    return
}

final int BUFFER_SIZE = 65536;
final int BLOCK_SIZE = 4096;

try {			
	flowFile = session.write(flowFile, { inputStream, outputStream ->
	output = new ArmoredOutputStream(outputStream)
	int cipher = PGPEncryptedData.AES_256
	String provider = "BC"
	filename = "encrypted.txt"
	publicKeyUserId = flowFile.getAttribute('PublicKeyUserId')
	publicKeyringFile = flowFile.getAttribute('publicKeyRoute')
	publicKey = getPublicKey(publicKeyUserId, publicKeyringFile)

	try {
    	PGPEncryptedDataGenerator encryptedDataGenerator = new PGPEncryptedDataGenerator(
        new JcePGPDataEncryptorBuilder(cipher).setWithIntegrityPacket(true).setSecureRandom(new SecureRandom()).setProvider(provider))

		encryptedDataGenerator.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(publicKey).setProvider(provider))
		
		OutputStream encryptedOut = encryptedDataGenerator.open(output, new byte[BUFFER_SIZE])

		PGPCompressedDataGenerator compressedDataGenerator = new PGPCompressedDataGenerator(PGPCompressedData.ZIP, Deflater.BEST_SPEED)

		OutputStream compressedOut = compressedDataGenerator.open(encryptedOut, new byte[BUFFER_SIZE])
		PGPLiteralDataGenerator literalDataGenerator = new PGPLiteralDataGenerator()

		OutputStream literalOut = literalDataGenerator.open(compressedOut, PGPLiteralData.BINARY, filename, new Date(), new byte[BUFFER_SIZE])
		
		final byte[] buffer = new byte[BLOCK_SIZE];
        int len;
        while ((len = inputStream.read(buffer)) >= 0) {
        	literalOut.write(buffer, 0, len);
        }
	} finally {
    	output.close();
    }

} as StreamCallback)

session.transfer(flowFile, REL_SUCCESS)
} catch (Exception e) {
    log.error("There was an error encrypting the attributes: ${e.getMessage()}")
    session.transfer(flowFile, REL_FAILURE)
}

static PGPPublicKey getPublicKey(String userId, String publicKeyringFile){
	FileInputStream keyInputStream = new FileInputStream(publicKeyringFile)
    // Form the PublicKeyRing collection (1.53 way with fingerprint calculator)
    PGPPublicKeyRingCollection pgpPublicKeyRingCollection = new PGPPublicKeyRingCollection(keyInputStream, new BcKeyFingerprintCalculator());
    // Iterate over all public keyrings
    Iterator<PGPPublicKeyRing> iter = pgpPublicKeyRingCollection.getKeyRings();
    PGPPublicKeyRing keyRing;
    while (iter.hasNext()) {
    	keyRing = iter.next();

		// Iterate over each public key in this keyring
        Iterator<PGPPublicKey> keyIter = keyRing.getPublicKeys();
        while (keyIter.hasNext()) {
        	PGPPublicKey publicKey = keyIter.next();

			// Iterate over each userId attached to the public key
            Iterator userIdIterator = publicKey.getUserIDs();
            while (userIdIterator.hasNext()) {
            	String id = (String) userIdIterator.next();
                if (userId.equalsIgnoreCase(id)) {
                	return publicKey;
                }
            }
		}
	}
}

 


 Can anyone help me making this work?
Thank you!

6 REPLIES 6

avatar
Super Guru

@FJATP 

 

Please have a look at this.  It doesn't use PGP but it's a similar encryption implementation using Groovy:

 

https://community.cloudera.com/t5/Support-Questions/How-to-encrypt-a-column-using-nifi/m-p/336023/hi...

 

In that case I used parameters but you can replace them with variables/attributes.

 

Let me know if that helps.

 

André

--

Was your question answered? Make sure to mark the answer as the accepted solution.
If you find a reply useful, say thanks by clicking on the thumbs up button.

 

--
Was your question answered? Please take some time to click on "Accept as Solution" below this post.
If you find a reply useful, say thanks by clicking on the thumbs up button.

avatar
Explorer

Thanks, but i must use PGP, with the public and private keys and all that. 

avatar
Super Guru

What's the error you're getting?

 

André

--
Was your question answered? Please take some time to click on "Accept as Solution" below this post.
If you find a reply useful, say thanks by clicking on the thumbs up button.

avatar
Explorer

When i try to decrypt it with kleoptatra, it throws the diagnostic message "gpg: [don't know]: 1st length byte missing".

avatar
Super Guru

@FJATP 

 

Can you share your flow definition?

 

André

 

--
Was your question answered? Please take some time to click on "Accept as Solution" below this post.
If you find a reply useful, say thanks by clicking on the thumbs up button.

avatar
Explorer

In the test version that i build is something like this: it creates the flow (In the original is a query from a database), adds the custom attributes (like the filename, the location of the public key, etc, those i get them from another table), it encrypts the contents and finally the encrypted content is transferred to an ftp.
FJATP_0-1646806629891.png

This is what you need, right? I can upload the xml somewhere if you like.

Thank you!