Github user alopresto commented on a diff in the pull request:

    https://github.com/apache/nifi/pull/1294#discussion_r96315594
  
    --- Diff: 
nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/EncryptAttributes.java
 ---
    @@ -0,0 +1,508 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You under the Apache License, Version 2.0
    + * (the "License"); you may not use this file except in compliance with
    + * the License.  You may obtain a copy of the License at
    + *
    + *     http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +
    +package org.apache.nifi.processors.standard;
    +
    +import org.apache.commons.codec.binary.Base64;
    +import org.apache.commons.codec.binary.Hex;
    +import org.apache.nifi.annotation.behavior.DynamicProperty;
    +import org.apache.nifi.annotation.behavior.EventDriven;
    +import org.apache.nifi.annotation.behavior.InputRequirement;
    +import org.apache.nifi.annotation.behavior.SideEffectFree;
    +import org.apache.nifi.annotation.behavior.SupportsBatching;
    +import org.apache.nifi.annotation.documentation.CapabilityDescription;
    +import org.apache.nifi.annotation.documentation.Tags;
    +import org.apache.nifi.annotation.lifecycle.OnScheduled;
    +import org.apache.nifi.components.AllowableValue;
    +import org.apache.nifi.components.PropertyDescriptor;
    +import org.apache.nifi.components.PropertyValue;
    +import org.apache.nifi.components.ValidationContext;
    +import org.apache.nifi.components.ValidationResult;
    +import org.apache.nifi.expression.AttributeExpression;
    +import org.apache.nifi.flowfile.FlowFile;
    +import org.apache.nifi.flowfile.attributes.CoreAttributes;
    +import org.apache.nifi.logging.ComponentLog;
    +import org.apache.nifi.processor.AbstractProcessor;
    +import org.apache.nifi.processor.ProcessContext;
    +import org.apache.nifi.processor.ProcessSession;
    +import org.apache.nifi.processor.ProcessorInitializationContext;
    +import org.apache.nifi.processor.Relationship;
    +import org.apache.nifi.processor.exception.ProcessException;
    +import org.apache.nifi.processor.util.StandardValidators;
    +import 
org.apache.nifi.processors.standard.util.crypto.EncryptProcessorUtils;
    +import 
org.apache.nifi.processors.standard.util.crypto.EncryptProcessorUtils.Encryptor;
    +import org.apache.nifi.processors.standard.util.crypto.KeyedEncryptor;
    +import 
org.apache.nifi.processors.standard.util.crypto.OpenPGPKeyBasedEncryptor;
    +import 
org.apache.nifi.processors.standard.util.crypto.OpenPGPPasswordBasedEncryptor;
    +import 
org.apache.nifi.processors.standard.util.crypto.PasswordBasedEncryptor;
    +import org.apache.nifi.security.util.EncryptionMethod;
    +import org.apache.nifi.security.util.KeyDerivationFunction;
    +import org.bouncycastle.jce.provider.BouncyCastleProvider;
    +
    +import java.io.ByteArrayInputStream;
    +import java.io.ByteArrayOutputStream;
    +import java.io.IOException;
    +import java.io.InputStream;
    +import java.nio.charset.StandardCharsets;
    +import java.security.Security;
    +import java.text.Normalizer;
    +import java.util.ArrayList;
    +import java.util.Collection;
    +import java.util.Collections;
    +import java.util.HashMap;
    +import java.util.HashSet;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.Set;
    +import java.util.regex.Pattern;
    +
    +/**
    + * Provides functionality of encrypting attributes with various algorithms.
    + * Note. It'll not modify filename or uuid as they are sensitive and are
    + * internally used by either Algorithm itself or FlowFile repo.
    + */
    +@EventDriven
    +@SideEffectFree
    +@SupportsBatching
    +@InputRequirement(InputRequirement.Requirement.INPUT_REQUIRED)
    +@Tags({"encryption", "decryption", "password", "JCE", "OpenPGP", "PGP", 
"GPG", "regex",
    +        "regexp", "Attribute Expression Language"})
    +@CapabilityDescription("Encrypts or Decrypts a FlowFile attributes using 
either symmetric encryption with a password " +
    +        "and randomly generated salt, or asymmetric encryption using a 
public and secret key. Different options are " +
    +        "available to provide list of attributes. Default options are: 
'all-attributes'/'core-attributes/" +
    +        "'all-except-core-attributes'. You can also add custom properties 
containing expression language condition. " +
    +        "These conditions will be evaluated and only those attributes will 
be considered for which the condition " +
    +        "is \'true\'. You can also provide RegEx to select a group of 
attributes. RegEx and Expression Language conditions" +
    +        "can be combined for advanced filtering of attribute list")
    +@DynamicProperty(name = "Attribute Name", value = "Attribute Expression 
Language", description = "Evaluates expression language " +
    +        "as boolean expression, if attribute exist and boolean condition 
evaluates to true, then it'll be considered " +
    +        "for encryption/decryption")
    +public class EncryptAttributes extends AbstractProcessor {
    +
    +    public static final String ENCRYPT_MODE = "Encrypt";
    +    public static final String DECRYPT_MODE = "Decrypt";
    +
    +    public static final String WEAK_CRYPTO_ALLOWED_NAME = "allowed";
    +    public static final String WEAK_CRYPTO_NOT_ALLOWED_NAME = 
"not-allowed";
    +    public static final String ALL_ATTR = "All Attributes";
    +    public static final String CORE_ATTR = "Core Attributes";
    +    public static final String ALL_EXCEPT_CORE_ATTR = "All Except Core 
Attributes";
    +    public static final String CUSTOM_ATTR = "Custom Attributes";
    +
    +    private static final AllowableValue ALL_ATTR_ALLOWABLE_VALUE = new 
AllowableValue(ALL_ATTR, ALL_ATTR,
    +            "All attributes will be considered for encryption/decryption. 
Note: \'uuid\' attribute will be ignored. " +
    +                    "If using PGP algo for encryption/decryption then 
\'filename\' will be ignored");
    +    private static final AllowableValue CORE_ATTR_ALLOWABLE_VALUE = new 
AllowableValue(CORE_ATTR, CORE_ATTR,
    +            "Core attributes will be considered for encryption/decryption. 
Note: \'uuid\' attribute will be ignored.");
    +    private static final AllowableValue 
ALL_EXCEPT_CORE_ATTR_ALLOWABLE_VALUE = new AllowableValue(ALL_EXCEPT_CORE_ATTR,
    +            CORE_ATTR, "All attributes except core attributes will be 
considered for encryption/decryption.");
    +    private static final AllowableValue CUSTOM_ATTR_ALLOWABLE_VALUE = new 
AllowableValue(CUSTOM_ATTR, CUSTOM_ATTR,
    +            "Custom filters can applied on attribute list via providing 
RegEx in provied property or can add " +
    +                    "Custom Expression Language conditions which will 
consider only those attributes to which it evaluates " +
    +                    "to true. Note: \'uuid\' ignored and if using PGP 
encryption/decryption the \'filename\' will also be ignored");
    +
    +    public static final PropertyDescriptor ATTRS_TO_ENCRYPT = new 
PropertyDescriptor.Builder()
    +            .name("attributes-to-encrypt")
    +            .displayName("Attributes to Encrypt")
    +            .description("Choose the attributes you would like to encrypt. 
You can also dynamic properties " +
    +                    "with Expression Language condition, if matches then 
it'll be encrypted otherwise ignored.")
    +            .required(true)
    +            .allowableValues(ALL_EXCEPT_CORE_ATTR_ALLOWABLE_VALUE, 
ALL_ATTR_ALLOWABLE_VALUE, CORE_ATTR_ALLOWABLE_VALUE,
    +                    CUSTOM_ATTR_ALLOWABLE_VALUE)
    +            .defaultValue(ALL_ATTR_ALLOWABLE_VALUE.getValue())
    +            .build();
    +    public static final PropertyDescriptor MODE = new 
PropertyDescriptor.Builder()
    +            .name("mode")
    +            .displayName("Mode")
    +            .description("Specifies whether the content should be 
encrypted or decrypted")
    +            .required(true)
    +            .allowableValues(ENCRYPT_MODE, DECRYPT_MODE)
    +            .defaultValue(ENCRYPT_MODE)
    +            .build();
    +    public static final PropertyDescriptor KEY_DERIVATION_FUNCTION = new 
PropertyDescriptor.Builder()
    +            .name(EncryptProcessorUtils.KEY_DERIVATION_FUNCTION)
    +            .displayName("Key Derivation Function")
    +            .description("Specifies the key derivation function to 
generate the key from the password (and salt)")
    +            .required(true)
    +            
.allowableValues(EncryptProcessorUtils.buildKeyDerivationFunctionAllowableValues())
    +            
.defaultValue(KeyDerivationFunction.OPENSSL_EVP_BYTES_TO_KEY.name())
    +            .build();
    +    public static final PropertyDescriptor ENCRYPTION_ALGORITHM = new 
PropertyDescriptor.Builder()
    +            .name(EncryptProcessorUtils.ENCRYPTION_ALGORITHM)
    +            .displayName("Encryption Algorithm")
    +            .description("The Encryption Algorithm to use")
    +            .required(true)
    +            
.allowableValues(EncryptProcessorUtils.buildEncryptionMethodAllowableValues())
    +            .defaultValue(EncryptionMethod.MD5_128AES.name())
    +            .build();
    +    public static final PropertyDescriptor PASSWORD = new 
PropertyDescriptor.Builder()
    +            .name(EncryptProcessorUtils.PASSWORD)
    +            .displayName("Password")
    +            .description("The Password to use for encrypting or decrypting 
the data")
    +            .required(false)
    +            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
    +            .sensitive(true)
    +            .build();
    +    public static final PropertyDescriptor PUBLIC_KEYRING = new 
PropertyDescriptor.Builder()
    +            .name(EncryptProcessorUtils.PRIVATE_KEYRING)
    +            .displayName("Public Keyring File")
    +            .description("In a PGP encrypt mode, this keyring contains the 
public key of the recipient")
    +            .required(false)
    +            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
    +            .build();
    +    public static final PropertyDescriptor PUBLIC_KEY_USERID = new 
PropertyDescriptor.Builder()
    +            .name(EncryptProcessorUtils.PUBLIC_KEY_USERID)
    +            .displayName("Public Key User Id")
    +            .description("In a PGP encrypt mode, this user id of the 
recipient")
    +            .required(false)
    +            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
    +            .build();
    +    public static final PropertyDescriptor PRIVATE_KEYRING = new 
PropertyDescriptor.Builder()
    +            .name(EncryptProcessorUtils.PRIVATE_KEYRING)
    +            .displayName("Private Keyring File")
    +            .description("In a PGP decrypt mode, this keyring contains the 
private key of the recipient")
    +            .required(false)
    +            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
    +            .build();
    +    public static final PropertyDescriptor PRIVATE_KEYRING_PASSPHRASE = 
new PropertyDescriptor.Builder()
    +            .name(EncryptProcessorUtils.PRIVATE_KEYRING_PASSPHRASE)
    +            .displayName("Private Keyring Passphrase")
    +            .description("In a PGP decrypt mode, this is the private 
keyring passphrase")
    +            .required(false)
    +            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
    +            .sensitive(true)
    +            .build();
    +    public static final PropertyDescriptor RAW_KEY_HEX = new 
PropertyDescriptor.Builder()
    +            .name(EncryptProcessorUtils.RAW_KEY_HEX)
    +            .displayName("Raw Key (hexadecimal)")
    +            .description("In keyed encryption, this is the raw key, 
encoded in hexadecimal")
    +            .required(false)
    +            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
    +            .sensitive(true)
    +            .build();
    +    public static final PropertyDescriptor ALLOW_WEAK_CRYPTO = new 
PropertyDescriptor.Builder()
    +            .name("allow-weak-crypto")
    +            .displayName("Allow insecure cryptographic modes")
    +            .description("Overrides the default behavior to prevent unsafe 
combinations of encryption algorithms and short passwords on JVMs with limited 
strength cryptographic jurisdiction policies")
    +            .required(true)
    +            
.allowableValues(EncryptProcessorUtils.buildWeakCryptoAllowableValues())
    +            
.defaultValue(EncryptProcessorUtils.buildDefaultWeakCryptoAllowableValue().getValue())
    +            .build();
    +    public static final PropertyDescriptor ATTR_SELECT_REG_EX = new 
PropertyDescriptor.Builder()
    +            .name("attribute-select-regex")
    +            .displayName("Attributes Selection RegEx")
    +            .description("If " + 
CUSTOM_ATTR_ALLOWABLE_VALUE.getDisplayName() + " is selcted then provied a 
RegEx to select " +
    +                    "attributes matching a specific pattern. Only those 
attributes will be encrypted/decrypted " +
    +                    "who matches against given regex pattern")
    +            .addValidator(StandardValidators.REGULAR_EXPRESSION_VALIDATOR)
    +            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
    +            .defaultValue(".*")
    +            .required(false)
    +            .build();
    +
    +    public static final Relationship REL_SUCCESS = new 
Relationship.Builder().name("success")
    +            .description("Any FlowFile that is successfully encrypted or 
decrypted will be routed to success").build();
    +
    +    public static final Relationship REL_FAILURE = new 
Relationship.Builder().name("failure")
    +            .description("Any FlowFile that cannot be encrypted or 
decrypted will be routed to failure").build();
    +    private List<PropertyDescriptor> properties;
    +
    +    private Set<Relationship> relationships;
    +
    +    private volatile Map<String, PropertyValue> propMap = new HashMap<>();
    +
    +    static {
    +        // add BouncyCastle encryption providers
    +        Security.addProvider(new BouncyCastleProvider());
    +    }
    +
    +    @Override
    +    protected void init(final ProcessorInitializationContext context) {
    +        final List<PropertyDescriptor> properties = new ArrayList<>();
    +        properties.add(ATTRS_TO_ENCRYPT);
    +        properties.add(MODE);
    +        properties.add(KEY_DERIVATION_FUNCTION);
    +        properties.add(ENCRYPTION_ALGORITHM);
    +        properties.add(ALLOW_WEAK_CRYPTO);
    +        properties.add(PASSWORD);
    +        properties.add(RAW_KEY_HEX);
    +        properties.add(PUBLIC_KEYRING);
    +        properties.add(PUBLIC_KEY_USERID);
    +        properties.add(PRIVATE_KEYRING);
    +        properties.add(PRIVATE_KEYRING_PASSPHRASE);
    +        properties.add(ATTR_SELECT_REG_EX);
    +        this.properties = Collections.unmodifiableList(properties);
    +
    +        final Set<Relationship> relationships = new HashSet<>();
    +        relationships.add(REL_SUCCESS);
    +        relationships.add(REL_FAILURE);
    +        this.relationships = Collections.unmodifiableSet(relationships);
    +    }
    +
    +    @Override
    +    public Set<Relationship> getRelationships() {
    +        return relationships;
    +    }
    +
    +    @Override
    +    protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
    +        return properties;
    +    }
    +
    +
    +    @Override
    +    protected Collection<ValidationResult> customValidate(final 
ValidationContext context) {
    +        final List<ValidationResult> validationResults = new 
ArrayList<>(super.customValidate(context));
    +        final String methodValue = 
context.getProperty(ENCRYPTION_ALGORITHM).getValue();
    +        final EncryptionMethod encryptionMethod = 
EncryptionMethod.valueOf(methodValue);
    +        final String algorithm = encryptionMethod.getAlgorithm();
    +        final String password = context.getProperty(PASSWORD).getValue();
    +        final KeyDerivationFunction kdf = 
KeyDerivationFunction.valueOf(context.getProperty(KEY_DERIVATION_FUNCTION).getValue());
    +        final String keyHex = context.getProperty(RAW_KEY_HEX).getValue();
    +        if (EncryptProcessorUtils.isPGPAlgorithm(algorithm)) {
    +            final boolean encrypt = 
context.getProperty(MODE).getValue().equalsIgnoreCase(ENCRYPT_MODE);
    +            final String publicKeyring = 
context.getProperty(PUBLIC_KEYRING).getValue();
    +            final String publicUserId = 
context.getProperty(PUBLIC_KEY_USERID).getValue();
    +            final String privateKeyring = 
context.getProperty(PRIVATE_KEYRING).getValue();
    +            final String privateKeyringPassphrase = 
context.getProperty(PRIVATE_KEYRING_PASSPHRASE).getValue();
    +            
validationResults.addAll(EncryptProcessorUtils.validatePGP(encryptionMethod, 
password, encrypt, publicKeyring, publicUserId, privateKeyring, 
privateKeyringPassphrase));
    +        } else { // Not PGP
    +            if (encryptionMethod.isKeyedCipher()) { // Raw key
    +                
validationResults.addAll(EncryptProcessorUtils.validateKeyed(encryptionMethod, 
kdf, keyHex));
    +            } else { // PBE
    +                boolean allowWeakCrypto = 
context.getProperty(ALLOW_WEAK_CRYPTO).getValue().equalsIgnoreCase(WEAK_CRYPTO_ALLOWED_NAME);
    +                
validationResults.addAll(EncryptProcessorUtils.validatePBE(encryptionMethod, 
kdf, password, allowWeakCrypto));
    +            }
    +        }
    +        return validationResults;
    +    }
    +
    +    @Override
    +    protected PropertyDescriptor 
getSupportedDynamicPropertyDescriptor(String propertyDescriptorName) {
    +        return new PropertyDescriptor.Builder()
    +                .name(propertyDescriptorName)
    +                
.addValidator(StandardValidators.createAttributeExpressionLanguageValidator(AttributeExpression.ResultType.BOOLEAN,
 false))
    +                
.addValidator(StandardValidators.ATTRIBUTE_KEY_PROPERTY_NAME_VALIDATOR)
    +                .dynamic(true)
    +                .required(false)
    +                .expressionLanguageSupported(true)
    +                .build();
    +    }
    +
    +    /**
    +     * Performs decryption with given input string and encryptor.
    +     * The input must be of Base64 encoded string.
    +     *
    +     * @param str       Base64 encoded encrypted String
    +     * @param encryptor Encryptor which will be used for decryption
    +     * @return decrypted string of charset US-ASCII
    +     * @throws Exception exception if couldn't process streams converted 
from strings
    +     */
    +    private String performDecryption(String str, Encryptor encryptor) 
throws Exception {
    +        //Initialize string and streams
    +        byte[] encryptedBytes = str.getBytes(StandardCharsets.US_ASCII);
    +        byte[] decodedBytes = Base64.decodeBase64(encryptedBytes);
    +        String decryptedStr;
    +
    +        try (InputStream in = new ByteArrayInputStream(decodedBytes);
    +             ByteArrayOutputStream out = new ByteArrayOutputStream()) {
    +            encryptor.getDecryptionCallback().process(in, out);
    +            decryptedStr = new String(out.toByteArray(), 
StandardCharsets.US_ASCII);
    +        } catch (IOException e) {
    +            throw new ProcessException(e);
    +        }
    +
    +        return decryptedStr;
    +    }
    +
    +    /**
    +     * Performs encryption with given input string. The final encrypted 
string is
    +     * encoded to Base64 to prevent data loss
    +     *
    +     * @param str       String to be encrypted
    +     * @param encryptor Encryptor which will be used for encryption
    +     * @return Base64 encode string after performing encryption
    +     * @throws Exception exception if couldn't process streams converted 
from strings
    +     */
    +    private String performEncryption(String str, Encryptor encryptor) 
throws Exception {
    +        String encodedEncryptedStr;
    +
    +        try (InputStream in = new 
ByteArrayInputStream(str.getBytes(StandardCharsets.US_ASCII));
    +             ByteArrayOutputStream out = new ByteArrayOutputStream()) {
    +            encryptor.getEncryptionCallback().process(in, out);
    +            byte[] encryptedData = out.toByteArray();
    +            encodedEncryptedStr = Base64.encodeBase64String(encryptedData);
    +        } catch (IOException e) {
    +            throw new ProcessException(e);
    +        }
    +        return encodedEncryptedStr;
    +    }
    +
    +    private Set<String> getAttrToEncrypt(FlowFile flowFile, PropertyValue 
attrsToEncryptProp,
    +                                         PropertyValue attrSelectRegEx, 
String algorithm, boolean encrypt) {
    +        String attrsToEncryptPropVal = attrsToEncryptProp.getValue();
    +        String regex = attrSelectRegEx.getValue();
    +        Set<String> flowFileAttrs = flowFile.getAttributes().keySet();
    +        Set<String> attrsToEncrypt;
    +
    +        if (attrsToEncryptPropVal.equals(CORE_ATTR)) {
    +            attrsToEncrypt = new HashSet<>();
    +        } else {
    +            attrsToEncrypt = new HashSet<>(flowFileAttrs);
    +        }
    +
    +        if (attrsToEncryptPropVal.equals(ALL_EXCEPT_CORE_ATTR)
    +                || attrsToEncryptPropVal.equals(CORE_ATTR)) {
    +            //traverse core attributes and add/remove as per the prop 
value.
    +            for (CoreAttributes attr : CoreAttributes.values()) {
    +                if (flowFileAttrs.contains(attr.key()) && 
attrsToEncryptPropVal.equals(ALL_EXCEPT_CORE_ATTR)) {
    +                    attrsToEncrypt.remove(attr.key());
    +                } else if (flowFileAttrs.contains(attr.key()) && 
attrsToEncryptPropVal.equals(CORE_ATTR)) {
    +                    attrsToEncrypt.add(attr.key());
    +                }
    +            }
    +        }
    +
    +        if (attrsToEncryptPropVal.equals(CUSTOM_ATTR)) {
    +
    +            //get list of all the attributes matching regex
    +            if (regex != null && !regex.equals(".*")) {
    +                attrsToEncrypt.clear();
    +                Pattern pattern = Pattern.compile(regex);
    +                for (String str : flowFileAttrs) {
    +                    if (pattern.matcher(str).matches()) {
    +                        attrsToEncrypt.add(str);
    +                    }
    +                }
    +            }
    +
    +            //check if property-key is present in attrsToEncrypt and if 
expression-lang condition
    +            //return true then encrypt/decrypt it.
    +            if (!propMap.isEmpty()) {
    +                HashSet<String> attrsToEncryptClone = new 
HashSet<>(attrsToEncrypt);
    +                for(String attr: attrsToEncryptClone) {
    +                    if (propMap.containsKey(attr)) {
    +                        boolean matches = 
propMap.get(attr).evaluateAttributeExpressions(flowFile).asBoolean();
    +                        if (!matches){
    +                            attrsToEncrypt.remove(attr);
    +                            getLogger().warn("{} expression-language 
expression evaluates to false",
    +                                    new 
Object[]{propMap.get(attr).getValue()});
    +                        }
    +                    } else {
    +                        attrsToEncrypt.remove(attr);
    +                    }
    +                }
    +
    +            }
    +        }
    +
    +        attrsToEncrypt.remove(CoreAttributes.UUID.key());
    +        if (EncryptProcessorUtils.isPGPAlgorithm(algorithm)) {
    +            attrsToEncrypt.remove(CoreAttributes.FILENAME.key());
    +            getLogger().info("Removing filename from {}cryption because of 
{} algorithm",
    +                    new Object[]{(encrypt)?"en":"de", algorithm});
    +        }
    +        return attrsToEncrypt;
    +    }
    +
    +    private Map<String, String> buildNewAttributes(FlowFile flowFile, 
PropertyValue attrList,
    +                                                   PropertyValue 
attrSelectRegex, String algorithm,
    +                                                   Encryptor encryptor, 
boolean encrypt) throws Exception {
    +
    +        Map<String, String> oldAttrs = flowFile.getAttributes();
    +        Map<String, String> newAttrs = new HashMap<>();
    +        Set<String> attrToEncrypt = getAttrToEncrypt(flowFile, attrList, 
attrSelectRegex, algorithm, encrypt);
    +
    +        for (String attr : attrToEncrypt) {
    +            String attrVal = oldAttrs.get(attr);
    +            String encryptedVal = (encrypt) ? performEncryption(attrVal, 
encryptor) : performDecryption(attrVal, encryptor);
    +            newAttrs.put(attr, encryptedVal);
    +            getLogger().debug("{}crypted {} from '{}' to '{}'",
    --- End diff --
    
    If you **must** be able to uniquely identify the input/output value of the 
attribute, use a secure hash function (SHA-256/384/512) before logging to allow 
for a priori knowledge reconstruction later. 


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at [email protected] or file a JIRA ticket
with INFRA.
---

Reply via email to