Created on 03-08-2022 11:54 AM - edited 03-08-2022 03:29 PM
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!
Created 03-08-2022 12:37 PM
Please have a look at this. It doesn't use PGP but it's a similar encryption implementation using Groovy:
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.
Created 03-08-2022 03:27 PM
Thanks, but i must use PGP, with the public and private keys and all that.
Created 03-08-2022 03:28 PM
What's the error you're getting?
André
Created 03-08-2022 03:31 PM
When i try to decrypt it with kleoptatra, it throws the diagnostic message "gpg: [don't know]: 1st length byte missing".
Created 03-08-2022 06:53 PM
Can you share your flow definition?
André
Created 03-08-2022 10:21 PM
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.
This is what you need, right? I can upload the xml somewhere if you like.
Thank you!