Our Community is getting an upgrade! To get everything ready for the relaunch, we’ll be placing the site in read-only mode starting September 21st.
We really appreciate your understanding while we get things set up behind the scenes. Catch up on all the exciting details about the move here.
Need help or have questions? Drop us a line at [email protected]

Support Questions

Find answers, ask questions, and share your expertise
Announcements
Share your experience with Cloudera on G2 and get a $25 Amazon Gift card.
Hi, I'm CLEO! Something exciting is coming to the Community. Stay Tuned!

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

avatar
Visitor

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
Visitor

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
Visitor

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
Visitor

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!