alopresto commented on a change in pull request #4077: NIFI-5346 Introduces new PGP controller service and PGP processors. URL: https://github.com/apache/nifi/pull/4077#discussion_r384893311
########## File path: nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/pgp/PGPControllerService.java ########## @@ -0,0 +1,893 @@ +/* + * 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.pgp; + +import org.apache.nifi.annotation.documentation.CapabilityDescription; +import org.apache.nifi.annotation.documentation.Tags; +import org.apache.nifi.components.PropertyDescriptor; +import org.apache.nifi.components.ValidationContext; +import org.apache.nifi.components.ValidationResult; +import org.apache.nifi.context.PropertyContext; +import org.apache.nifi.controller.AbstractControllerService; +import org.apache.nifi.controller.ControllerServiceInitializationContext; +import org.apache.nifi.expression.ExpressionLanguageScope; +import org.apache.nifi.processor.util.StandardValidators; +import org.apache.nifi.processors.standard.util.PGPUtil; +import org.apache.nifi.reporting.InitializationException; +import org.bouncycastle.bcpg.ArmoredOutputStream; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openpgp.PGPCompressedData; +import org.bouncycastle.openpgp.PGPCompressedDataGenerator; +import org.bouncycastle.openpgp.PGPEncryptedData; +import org.bouncycastle.openpgp.PGPEncryptedDataGenerator; +import org.bouncycastle.openpgp.PGPEncryptedDataList; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPLiteralData; +import org.bouncycastle.openpgp.PGPLiteralDataGenerator; +import org.bouncycastle.openpgp.PGPObjectFactory; +import org.bouncycastle.openpgp.PGPPBEEncryptedData; +import org.bouncycastle.openpgp.PGPPrivateKey; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData; +import org.bouncycastle.openpgp.PGPPublicKeyRing; +import org.bouncycastle.openpgp.PGPSecretKey; +import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; +import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.openpgp.PGPSignatureGenerator; +import org.bouncycastle.openpgp.PGPSignatureList; +import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory; +import org.bouncycastle.openpgp.jcajce.JcaPGPPublicKeyRingCollection; +import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator; +import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory; +import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor; +import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider; +import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory; +import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; +import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; +import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; +import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.bouncycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder; +import org.bouncycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator; +import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; +import org.bouncycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder; +import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder; +import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator; +import org.bouncycastle.util.io.Streams; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.security.Security; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.zip.Deflater; + + +/** + * This class defines an implementation of {@link PGPService} suitable for use with the PGP processors in this package. + * + * In addition to basic Controller Service functionality this class exposes high-level, stream-based cryptographic operations + * to clients. These operations are encrypt, decrypt, sign, and verify. + * + * This implementation also provides the options builder methods. These methods build the various options instances that + * correspond to the cryptographic operations. These option instances allow the controller service to combine the properties + * of the processors with the properties of the controller, and then use that combination to parameterize the given operation. + * Note that this also keeps the keys and pass-phrases out of processor code completely. + * + * This class also has a set of private methods for reading key material from disk or input streams. These methods are + * not exposed to the processor clients (but are only package-private at the moment). Note that the key handling methods + * support caching. + * + * The net effect of combining these behaviors is to reduce the significant coupling to the BC/PGP libraries to just + * this class. This was done intentionally to make maintenance and auditing easier. + * + */ +@CapabilityDescription("Defines cryptographic key material and cryptographic operations for PGP processors.") +@Tags({"pgp", "gpg", "encryption", "credentials", "provider"}) +public class PGPControllerService extends AbstractControllerService implements PGPService { + final static String CONTROLLER_NAME = "PGP Key Material Controller Service"; + + // Uncertain if these could ever collide, so err on the side of caution can keep them distinct: + private final static Map<Integer, PGPPublicKeys> publicKeyCache = new HashMap<>(); + private final static Map<Integer, PGPSecretKeys> secretKeyCache = new HashMap<>(); + + static { + Security.addProvider(new BouncyCastleProvider()); + } + + public static final PropertyDescriptor PUBLIC_KEYRING_FILE = new PropertyDescriptor.Builder() + .name("public-keyring-file") + .displayName("Public Key or Keyring File") + .description("PGP public key or keyring file.") + .addValidator(StandardValidators.FILE_EXISTS_VALIDATOR) + .build(); + + public static final PropertyDescriptor PUBLIC_KEYRING_TEXT = new PropertyDescriptor.Builder() + .name("public-keyring-text") + .displayName("Public Key or Keyring Text") + .description("PGP public key or keyring as text (also called Armored text or ASCII text).") + .addValidator(StandardValidators.NON_EMPTY_VALIDATOR) + .build(); + + public static final PropertyDescriptor PUBLIC_KEY_USER_ID = new PropertyDescriptor.Builder() + .name("public-key-user-id") + .displayName("Public Key User ID") + .description("Public Key user ID (also called ID or Name) for the key within the public key keyring.") + .addValidator(StandardValidators.NON_EMPTY_VALIDATOR) + .build(); + + public static final PropertyDescriptor SECRET_KEYRING_FILE = new PropertyDescriptor.Builder() + .name("secret-keyring-file") + .displayName("Secret Key or Keyring File") + .description("PGP secret key or keyring file.") + .addValidator(StandardValidators.FILE_EXISTS_VALIDATOR) + .build(); + + public static final PropertyDescriptor SECRET_KEYRING_TEXT = new PropertyDescriptor.Builder() + .name("secret-keyring-text") + .displayName("Secret Key or Keyring Text") + .description("PGP secret key or keyring as text (also called Armored text or ASCII text).") + .addValidator(StandardValidators.NON_EMPTY_VALIDATOR) + .sensitive(true) + .build(); + + public static final PropertyDescriptor SECRET_KEY_USER_ID = new PropertyDescriptor.Builder() + .name("secret-key-user-id") + .displayName("Secret Key User ID") + .description("Secret Key user ID (also called ID or Name) for the key within the secret keyring.") + .addValidator(StandardValidators.NON_EMPTY_VALIDATOR) + .build(); + + public static final PropertyDescriptor PRIVATE_KEY_PASS_PHRASE = new PropertyDescriptor.Builder() Review comment: We should be consistent with naming & casing. I suggest: * `passphrase`/`somePassphrase` in code * "passphrase"/"Passphrase" in human-readable text ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org With regards, Apache Git Services