http://git-wip-us.apache.org/repos/asf/nifi/blob/7d242076/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/BcryptCipherProvider.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/BcryptCipherProvider.java b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/BcryptCipherProvider.java deleted file mode 100644 index 125aea7..0000000 --- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/BcryptCipherProvider.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * 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.util.crypto; - -import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.processor.exception.ProcessException; -import org.apache.nifi.processors.standard.util.crypto.bcrypt.BCrypt; -import org.apache.nifi.security.util.EncryptionMethod; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.crypto.Cipher; -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.util.Arrays; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class BcryptCipherProvider extends RandomIVPBECipherProvider { - private static final Logger logger = LoggerFactory.getLogger(BcryptCipherProvider.class); - - private final int workFactor; - /** - * This can be calculated automatically using the code {@see BcryptCipherProviderGroovyTest#calculateMinimumWorkFactor} or manually updated by a maintainer - */ - private static final int DEFAULT_WORK_FACTOR = 12; - private static final int DEFAULT_SALT_LENGTH = 16; - - private static final Pattern BCRYPT_SALT_FORMAT = Pattern.compile("^\\$\\d\\w\\$\\d{2}\\$[\\w\\/\\.]{22}"); - - /** - * Instantiates a Bcrypt cipher provider with the default work factor 12 (2^12 key expansion rounds). - */ - public BcryptCipherProvider() { - this(DEFAULT_WORK_FACTOR); - } - - /** - * Instantiates a Bcrypt cipher provider with the specified work factor w (2^w key expansion rounds). - * - * @param workFactor the (log) number of key expansion rounds [4..30] - */ - public BcryptCipherProvider(int workFactor) { - this.workFactor = workFactor; - if (workFactor < DEFAULT_WORK_FACTOR) { - logger.warn("The provided work factor {} is below the recommended minimum {}", workFactor, DEFAULT_WORK_FACTOR); - } - } - - /** - * Returns an initialized cipher for the specified algorithm. The key is derived by the KDF of the implementation. The IV is provided externally to allow for non-deterministic IVs, as IVs - * deterministically derived from the password are a potential vulnerability and compromise semantic security. See - * <a href="http://crypto.stackexchange.com/a/3970/12569">Ilmari Karonen's answer on Crypto Stack Exchange</a> - * - * @param encryptionMethod the {@link EncryptionMethod} - * @param password the secret input - * @param salt the complete salt (e.g. {@code "$2a$10$gUVbkVzp79H8YaCOsCVZNu".getBytes(StandardCharsets.UTF_8)}) - * @param iv the IV - * @param keyLength the desired key length in bits - * @param encryptMode true for encrypt, false for decrypt - * @return the initialized cipher - * @throws Exception if there is a problem initializing the cipher - */ - @Override - public Cipher getCipher(EncryptionMethod encryptionMethod, String password, byte[] salt, byte[] iv, int keyLength, boolean encryptMode) throws Exception { - try { - return getInitializedCipher(encryptionMethod, password, salt, iv, keyLength, encryptMode); - } catch (IllegalArgumentException e) { - throw e; - } catch (Exception e) { - throw new ProcessException("Error initializing the cipher", e); - } - } - - @Override - Logger getLogger() { - return logger; - } - - /** - * Returns an initialized cipher for the specified algorithm. The key (and IV if necessary) are derived by the KDF of the implementation. - * - * The IV can be retrieved by the calling method using {@link Cipher#getIV()}. - * - * @param encryptionMethod the {@link EncryptionMethod} - * @param password the secret input - * @param salt the complete salt (e.g. {@code "$2a$10$gUVbkVzp79H8YaCOsCVZNu".getBytes(StandardCharsets.UTF_8)}) - * @param keyLength the desired key length in bits - * @param encryptMode true for encrypt, false for decrypt - * @return the initialized cipher - * @throws Exception if there is a problem initializing the cipher - */ - @Override - public Cipher getCipher(EncryptionMethod encryptionMethod, String password, byte[] salt, int keyLength, boolean encryptMode) throws Exception { - return getCipher(encryptionMethod, password, salt, new byte[0], keyLength, encryptMode); - } - - protected Cipher getInitializedCipher(EncryptionMethod encryptionMethod, String password, byte[] salt, byte[] iv, int keyLength, boolean encryptMode) throws Exception { - if (encryptionMethod == null) { - throw new IllegalArgumentException("The encryption method must be specified"); - } - if (!encryptionMethod.isCompatibleWithStrongKDFs()) { - throw new IllegalArgumentException(encryptionMethod.name() + " is not compatible with Bcrypt"); - } - - if (StringUtils.isEmpty(password)) { - throw new IllegalArgumentException("Encryption with an empty password is not supported"); - } - - String algorithm = encryptionMethod.getAlgorithm(); - String provider = encryptionMethod.getProvider(); - - final String cipherName = CipherUtility.parseCipherFromAlgorithm(algorithm); - if (!CipherUtility.isValidKeyLength(keyLength, cipherName)) { - throw new IllegalArgumentException(String.valueOf(keyLength) + " is not a valid key length for " + cipherName); - } - - String bcryptSalt = formatSaltForBcrypt(salt); - - String hash = BCrypt.hashpw(password, bcryptSalt); - - /* The SHA-512 hash is required in order to derive a key longer than 184 bits (the resulting size of the Bcrypt hash) and ensuring the avalanche effect causes higher key entropy (if all - derived keys follow a consistent pattern, it weakens the strength of the encryption) */ - MessageDigest digest = MessageDigest.getInstance("SHA-512", provider); - byte[] dk = digest.digest(hash.getBytes(StandardCharsets.UTF_8)); - dk = Arrays.copyOf(dk, keyLength / 8); - SecretKey tempKey = new SecretKeySpec(dk, algorithm); - - KeyedCipherProvider keyedCipherProvider = new AESKeyedCipherProvider(); - return keyedCipherProvider.getCipher(encryptionMethod, tempKey, iv, encryptMode); - } - - private String formatSaltForBcrypt(byte[] salt) { - if (salt == null || salt.length == 0) { - throw new IllegalArgumentException("The salt cannot be empty. To generate a salt, use BcryptCipherProvider#generateSalt()"); - } - - String rawSalt = new String(salt, StandardCharsets.UTF_8); - Matcher matcher = BCRYPT_SALT_FORMAT.matcher(rawSalt); - - if (matcher.find()) { - return rawSalt; - } else { - throw new IllegalArgumentException("The salt must be of the format $2a$10$gUVbkVzp79H8YaCOsCVZNu. To generate a salt, use BcryptCipherProvider#generateSalt()"); - } - } - - @Override - public byte[] generateSalt() { - return BCrypt.gensalt(workFactor).getBytes(StandardCharsets.UTF_8); - } - - @Override - public int getDefaultSaltLength() { - return DEFAULT_SALT_LENGTH; - } - - protected int getWorkFactor() { - return workFactor; - } -}
http://git-wip-us.apache.org/repos/asf/nifi/blob/7d242076/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/CipherProvider.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/CipherProvider.java b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/CipherProvider.java deleted file mode 100644 index 46e815f..0000000 --- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/CipherProvider.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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.util.crypto; - -/** - * Marker interface for cipher providers. - */ -public interface CipherProvider { -} http://git-wip-us.apache.org/repos/asf/nifi/blob/7d242076/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/CipherProviderFactory.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/CipherProviderFactory.java b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/CipherProviderFactory.java deleted file mode 100644 index e04a7b4..0000000 --- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/CipherProviderFactory.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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.util.crypto; - -import org.apache.nifi.processor.exception.ProcessException; -import org.apache.nifi.security.util.KeyDerivationFunction; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.HashMap; -import java.util.Map; - -public class CipherProviderFactory { - private static final Logger logger = LoggerFactory.getLogger(CipherProviderFactory.class); - - private static Map<KeyDerivationFunction, Class<? extends CipherProvider>> registeredCipherProviders; - - static { - registeredCipherProviders = new HashMap<>(); - registeredCipherProviders.put(KeyDerivationFunction.NIFI_LEGACY, NiFiLegacyCipherProvider.class); - registeredCipherProviders.put(KeyDerivationFunction.OPENSSL_EVP_BYTES_TO_KEY, OpenSSLPKCS5CipherProvider.class); - registeredCipherProviders.put(KeyDerivationFunction.PBKDF2, PBKDF2CipherProvider.class); - registeredCipherProviders.put(KeyDerivationFunction.BCRYPT, BcryptCipherProvider.class); - registeredCipherProviders.put(KeyDerivationFunction.SCRYPT, ScryptCipherProvider.class); - registeredCipherProviders.put(KeyDerivationFunction.NONE, AESKeyedCipherProvider.class); - } - - public static CipherProvider getCipherProvider(KeyDerivationFunction kdf) { - logger.debug("{} KDFs registered", registeredCipherProviders.size()); - - if (registeredCipherProviders.containsKey(kdf)) { - Class<? extends CipherProvider> clazz = registeredCipherProviders.get(kdf); - try { - return clazz.newInstance(); - } catch (Exception e) { - logger.error("Error instantiating new {} with default parameters for {}", clazz.getName(), kdf.getName()); - throw new ProcessException("Error instantiating cipher provider"); - } - } - - throw new IllegalArgumentException("No cipher provider registered for " + kdf.getName()); - } -} http://git-wip-us.apache.org/repos/asf/nifi/blob/7d242076/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/CipherUtility.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/CipherUtility.java b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/CipherUtility.java deleted file mode 100644 index 6295c1d..0000000 --- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/CipherUtility.java +++ /dev/null @@ -1,329 +0,0 @@ -/* - * 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.util.crypto; - -import org.apache.commons.codec.binary.Base64; -import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.processor.exception.ProcessException; -import org.apache.nifi.security.util.EncryptionMethod; -import org.apache.nifi.stream.io.ByteArrayOutputStream; -import org.apache.nifi.stream.io.StreamUtils; - -import javax.crypto.Cipher; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class CipherUtility { - - public static final int BUFFER_SIZE = 65536; - private static final Pattern KEY_LENGTH_PATTERN = Pattern.compile("([\\d]+)BIT"); - - private static final Map<String, Integer> MAX_PASSWORD_LENGTH_BY_ALGORITHM; - - static { - Map<String, Integer> aMap = new HashMap<>(); - /** - * These values were determined empirically by running {@link NiFiLegacyCipherProviderGroovyTest#testShouldDetermineDependenceOnUnlimitedStrengthCrypto()} - *, which evaluates each algorithm in a try/catch harness with increasing password size until it throws an exception. - * This was performed on a JVM without the Unlimited Strength Jurisdiction cryptographic policy files installed. - */ - aMap.put("PBEWITHMD5AND128BITAES-CBC-OPENSSL", 16); - aMap.put("PBEWITHMD5AND192BITAES-CBC-OPENSSL", 16); - aMap.put("PBEWITHMD5AND256BITAES-CBC-OPENSSL", 16); - aMap.put("PBEWITHMD5ANDDES", 16); - aMap.put("PBEWITHMD5ANDRC2", 16); - aMap.put("PBEWITHSHA1ANDRC2", 16); - aMap.put("PBEWITHSHA1ANDDES", 16); - aMap.put("PBEWITHSHAAND128BITAES-CBC-BC", 7); - aMap.put("PBEWITHSHAAND192BITAES-CBC-BC", 7); - aMap.put("PBEWITHSHAAND256BITAES-CBC-BC", 7); - aMap.put("PBEWITHSHAAND40BITRC2-CBC", 7); - aMap.put("PBEWITHSHAAND128BITRC2-CBC", 7); - aMap.put("PBEWITHSHAAND40BITRC4", 7); - aMap.put("PBEWITHSHAAND128BITRC4", 7); - aMap.put("PBEWITHSHA256AND128BITAES-CBC-BC", 7); - aMap.put("PBEWITHSHA256AND192BITAES-CBC-BC", 7); - aMap.put("PBEWITHSHA256AND256BITAES-CBC-BC", 7); - aMap.put("PBEWITHSHAAND2-KEYTRIPLEDES-CBC", 7); - aMap.put("PBEWITHSHAAND3-KEYTRIPLEDES-CBC", 7); - aMap.put("PBEWITHSHAANDTWOFISH-CBC", 7); - MAX_PASSWORD_LENGTH_BY_ALGORITHM = Collections.unmodifiableMap(aMap); - } - - /** - * Returns the cipher algorithm from the full algorithm name. Useful for getting key lengths, etc. - * <p/> - * Ex: PBEWITHMD5AND128BITAES-CBC-OPENSSL -> AES - * - * @param algorithm the full algorithm name - * @return the generic cipher name or the full algorithm if one cannot be extracted - */ - public static String parseCipherFromAlgorithm(final String algorithm) { - if (StringUtils.isEmpty(algorithm)) { - return algorithm; - } - String formattedAlgorithm = algorithm.toUpperCase(); - - // This is not optimal but the algorithms do not have a standard format - final String AES = "AES"; - final String TDES = "TRIPLEDES"; - final String TDES_ALTERNATE = "DESEDE"; - final String DES = "DES"; - final String RC4 = "RC4"; - final String RC2 = "RC2"; - final String TWOFISH = "TWOFISH"; - final List<String> SYMMETRIC_CIPHERS = Arrays.asList(AES, TDES, TDES_ALTERNATE, DES, RC4, RC2, TWOFISH); - - // The algorithms contain "TRIPLEDES" but the cipher name is "DESede" - final String ACTUAL_TDES_CIPHER = "DESede"; - - for (String cipher : SYMMETRIC_CIPHERS) { - if (formattedAlgorithm.contains(cipher)) { - if (cipher.equals(TDES) || cipher.equals(TDES_ALTERNATE)) { - return ACTUAL_TDES_CIPHER; - } else { - return cipher; - } - } - } - - return algorithm; - } - - /** - * Returns the cipher key length from the full algorithm name. Useful for getting key lengths, etc. - * <p/> - * Ex: PBEWITHMD5AND128BITAES-CBC-OPENSSL -> 128 - * - * @param algorithm the full algorithm name - * @return the key length or -1 if one cannot be extracted - */ - public static int parseKeyLengthFromAlgorithm(final String algorithm) { - int keyLength = parseActualKeyLengthFromAlgorithm(algorithm); - if (keyLength != -1) { - return keyLength; - } else { - // Key length not explicitly named in algorithm - String cipher = parseCipherFromAlgorithm(algorithm); - return getDefaultKeyLengthForCipher(cipher); - } - } - - private static int parseActualKeyLengthFromAlgorithm(final String algorithm) { - Matcher matcher = KEY_LENGTH_PATTERN.matcher(algorithm); - if (matcher.find()) { - return Integer.parseInt(matcher.group(1)); - } else { - return -1; - } - } - - /** - * Returns true if the provided key length is a valid key length for the provided cipher family. Does not reflect if the Unlimited Strength Cryptography Jurisdiction Policies are installed. - * Does not reflect if the key length is correct for a specific combination of cipher and PBE-derived key length. - * <p/> - * Ex: - * <p/> - * 256 is valid for {@code AES/CBC/PKCS7Padding} but not {@code PBEWITHMD5AND128BITAES-CBC-OPENSSL}. However, this method will return {@code true} for both because it only gets the cipher - * family, {@code AES}. - * <p/> - * 64, AES -> false - * [128, 192, 256], AES -> true - * - * @param keyLength the key length in bits - * @param cipher the cipher family - * @return true if this key length is valid - */ - public static boolean isValidKeyLength(int keyLength, final String cipher) { - if (StringUtils.isEmpty(cipher)) { - return false; - } - return getValidKeyLengthsForAlgorithm(cipher).contains(keyLength); - } - - /** - * Returns true if the provided key length is a valid key length for the provided algorithm. Does not reflect if the Unlimited Strength Cryptography Jurisdiction Policies are installed. - * <p/> - * Ex: - * <p/> - * 256 is valid for {@code AES/CBC/PKCS7Padding} but not {@code PBEWITHMD5AND128BITAES-CBC-OPENSSL}. - * <p/> - * 64, AES/CBC/PKCS7Padding -> false - * [128, 192, 256], AES/CBC/PKCS7Padding -> true - * <p/> - * 128, PBEWITHMD5AND128BITAES-CBC-OPENSSL -> true - * [192, 256], PBEWITHMD5AND128BITAES-CBC-OPENSSL -> false - * - * @param keyLength the key length in bits - * @param algorithm the specific algorithm - * @return true if this key length is valid - */ - public static boolean isValidKeyLengthForAlgorithm(int keyLength, final String algorithm) { - if (StringUtils.isEmpty(algorithm)) { - return false; - } - return getValidKeyLengthsForAlgorithm(algorithm).contains(keyLength); - } - - public static List<Integer> getValidKeyLengthsForAlgorithm(String algorithm) { - List<Integer> validKeyLengths = new ArrayList<>(); - if (StringUtils.isEmpty(algorithm)) { - return validKeyLengths; - } - - // Some algorithms specify a single key size - int keyLength = parseActualKeyLengthFromAlgorithm(algorithm); - if (keyLength != -1) { - validKeyLengths.add(keyLength); - return validKeyLengths; - } - - // The algorithm does not specify a key size - String cipher = parseCipherFromAlgorithm(algorithm); - switch (cipher.toUpperCase()) { - case "DESEDE": - // 3DES keys have the cryptographic strength of 7/8 because of parity bits, but are often represented with n*8 bytes - return Arrays.asList(56, 64, 112, 128, 168, 192); - case "DES": - return Arrays.asList(56, 64); - case "RC2": - case "RC4": - case "RC5": - /** These ciphers can have arbitrary length keys but that's a really bad idea, {@see http://crypto.stackexchange.com/a/9963/12569}. - * Also, RC* is deprecated and should be considered insecure */ - for (int i = 40; i <= 2048; i++) { - validKeyLengths.add(i); - } - return validKeyLengths; - case "AES": - case "TWOFISH": - return Arrays.asList(128, 192, 256); - default: - return validKeyLengths; - } - } - - private static int getDefaultKeyLengthForCipher(String cipher) { - if (StringUtils.isEmpty(cipher)) { - return -1; - } - cipher = cipher.toUpperCase(); - switch (cipher) { - case "DESEDE": - return 112; - case "DES": - return 64; - case "RC2": - case "RC4": - case "RC5": - default: - return 128; - } - } - - public static void processStreams(Cipher cipher, InputStream in, OutputStream out) { - try { - final byte[] buffer = new byte[BUFFER_SIZE]; - int len; - while ((len = in.read(buffer)) > 0) { - final byte[] decryptedBytes = cipher.update(buffer, 0, len); - if (decryptedBytes != null) { - out.write(decryptedBytes); - } - } - - out.write(cipher.doFinal()); - } catch (Exception e) { - throw new ProcessException(e); - } - } - - public static byte[] readBytesFromInputStream(InputStream in, String label, int limit, byte[] delimiter) throws IOException, ProcessException { - if (in == null) { - throw new IllegalArgumentException("Cannot read " + label + " from null InputStream"); - } - - // If the value is not detected within the first n bytes, throw an exception - in.mark(limit); - - // The first n bytes of the input stream contain the value up to the custom delimiter - ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); - byte[] stoppedBy = StreamUtils.copyExclusive(in, bytesOut, limit + delimiter.length, delimiter); - - if (stoppedBy != null) { - byte[] bytes = bytesOut.toByteArray(); - return bytes; - } - - // If no delimiter was found, reset the cursor - in.reset(); - return null; - } - - public static void writeBytesToOutputStream(OutputStream out, byte[] value, String label, byte[] delimiter) throws IOException { - if (out == null) { - throw new IllegalArgumentException("Cannot write " + label + " to null OutputStream"); - } - out.write(value); - out.write(delimiter); - } - - public static String encodeBase64NoPadding(final byte[] bytes) { - String base64UrlNoPadding = Base64.encodeBase64URLSafeString(bytes); - base64UrlNoPadding = base64UrlNoPadding.replaceAll("-", "+"); - base64UrlNoPadding = base64UrlNoPadding.replaceAll("_", "/"); - return base64UrlNoPadding; - } - - public static boolean passwordLengthIsValidForAlgorithmOnLimitedStrengthCrypto(final int passwordLength, EncryptionMethod encryptionMethod) { - if (encryptionMethod == null) { - throw new IllegalArgumentException("Cannot evaluate an empty encryption method algorithm"); - } - - return passwordLength <= getMaximumPasswordLengthForAlgorithmOnLimitedStrengthCrypto(encryptionMethod); - } - - public static int getMaximumPasswordLengthForAlgorithmOnLimitedStrengthCrypto(EncryptionMethod encryptionMethod) { - if (encryptionMethod == null) { - throw new IllegalArgumentException("Cannot evaluate an empty encryption method algorithm"); - } - - if (MAX_PASSWORD_LENGTH_BY_ALGORITHM.containsKey(encryptionMethod.getAlgorithm())) { - return MAX_PASSWORD_LENGTH_BY_ALGORITHM.get(encryptionMethod.getAlgorithm()); - } else { - return -1; - } - } - - public static byte[] concatBytes(byte[]... arrays) throws IOException { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - for (byte[] bytes : arrays) { - outputStream.write(bytes); - } - - return outputStream.toByteArray(); - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi/blob/7d242076/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/KeyedCipherProvider.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/KeyedCipherProvider.java b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/KeyedCipherProvider.java deleted file mode 100644 index f0fa4fc..0000000 --- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/KeyedCipherProvider.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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.util.crypto; - -import org.apache.nifi.processor.exception.ProcessException; -import org.apache.nifi.security.util.EncryptionMethod; - -import javax.crypto.Cipher; -import javax.crypto.SecretKey; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.charset.StandardCharsets; - -public abstract class KeyedCipherProvider implements CipherProvider { - static final byte[] IV_DELIMITER = "NiFiIV".getBytes(StandardCharsets.UTF_8); - // This is 16 bytes for AES but can vary for other ciphers - static final int MAX_IV_LIMIT = 16; - - /** - * Returns an initialized cipher for the specified algorithm. The IV is provided externally to allow for non-deterministic IVs, as IVs - * deterministically derived from the password are a potential vulnerability and compromise semantic security. See - * <a href="http://crypto.stackexchange.com/a/3970/12569">Ilmari Karonen's answer on Crypto Stack Exchange</a> - * - * @param encryptionMethod the {@link EncryptionMethod} - * @param key the key - * @param iv the IV or nonce - * @param encryptMode true for encrypt, false for decrypt - * @return the initialized cipher - * @throws Exception if there is a problem initializing the cipher - */ - abstract Cipher getCipher(EncryptionMethod encryptionMethod, SecretKey key, byte[] iv, boolean encryptMode) throws Exception; - - /** - * Returns an initialized cipher for the specified algorithm. The IV will be generated internally (for encryption). If decryption is requested, it will throw an exception. - * - * @param encryptionMethod the {@link EncryptionMethod} - * @param key the key - * @param encryptMode true for encrypt, false for decrypt - * @return the initialized cipher - * @throws Exception if there is a problem initializing the cipher or if decryption is requested - */ - abstract Cipher getCipher(EncryptionMethod encryptionMethod, SecretKey key, boolean encryptMode) throws Exception; - - /** - * Generates a new random IV of the correct length. - * - * @return the IV - */ - abstract byte[] generateIV(); - - public byte[] readIV(InputStream in) throws IOException, ProcessException { - return CipherUtility.readBytesFromInputStream(in, "IV", MAX_IV_LIMIT, IV_DELIMITER); - } - - public void writeIV(byte[] iv, OutputStream out) throws IOException { - CipherUtility.writeBytesToOutputStream(out, iv, "IV", IV_DELIMITER); - } -} http://git-wip-us.apache.org/repos/asf/nifi/blob/7d242076/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/KeyedEncryptor.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/KeyedEncryptor.java b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/KeyedEncryptor.java deleted file mode 100644 index c573d3d..0000000 --- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/KeyedEncryptor.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * 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.util.crypto; - -import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.processor.exception.ProcessException; -import org.apache.nifi.processor.io.StreamCallback; -import org.apache.nifi.processors.standard.EncryptContent.Encryptor; -import org.apache.nifi.security.util.EncryptionMethod; -import org.apache.nifi.security.util.KeyDerivationFunction; - -import javax.crypto.Cipher; -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.security.NoSuchAlgorithmException; - -public class KeyedEncryptor implements Encryptor { - - private EncryptionMethod encryptionMethod; - private SecretKey key; - private byte[] iv; - - private static final int DEFAULT_MAX_ALLOWED_KEY_LENGTH = 128; - - private static boolean isUnlimitedStrengthCryptographyEnabled; - - // Evaluate an unlimited strength algorithm to determine if we support the capability we have on the system - static { - try { - isUnlimitedStrengthCryptographyEnabled = (Cipher.getMaxAllowedKeyLength("AES") > DEFAULT_MAX_ALLOWED_KEY_LENGTH); - } catch (NoSuchAlgorithmException e) { - // if there are issues with this, we default back to the value established - isUnlimitedStrengthCryptographyEnabled = false; - } - } - - public KeyedEncryptor(final EncryptionMethod encryptionMethod, final SecretKey key) { - this(encryptionMethod, key == null ? new byte[0] : key.getEncoded(), new byte[0]); - } - - public KeyedEncryptor(final EncryptionMethod encryptionMethod, final SecretKey key, final byte[] iv) { - this(encryptionMethod, key == null ? new byte[0] : key.getEncoded(), iv); - } - - public KeyedEncryptor(final EncryptionMethod encryptionMethod, final byte[] keyBytes) { - this(encryptionMethod, keyBytes, new byte[0]); - } - - public KeyedEncryptor(final EncryptionMethod encryptionMethod, final byte[] keyBytes, final byte[] iv) { - super(); - try { - if (encryptionMethod == null) { - throw new IllegalArgumentException("Cannot instantiate a keyed encryptor with null encryption method"); - } - if (!encryptionMethod.isKeyedCipher()) { - throw new IllegalArgumentException("Cannot instantiate a keyed encryptor with encryption method " + encryptionMethod.name()); - } - this.encryptionMethod = encryptionMethod; - if (keyBytes == null || keyBytes.length == 0) { - throw new IllegalArgumentException("Cannot instantiate a keyed encryptor with empty key"); - } - if (!CipherUtility.isValidKeyLengthForAlgorithm(keyBytes.length * 8, encryptionMethod.getAlgorithm())) { - throw new IllegalArgumentException("Cannot instantiate a keyed encryptor with key of length " + keyBytes.length); - } - String cipherName = CipherUtility.parseCipherFromAlgorithm(encryptionMethod.getAlgorithm()); - this.key = new SecretKeySpec(keyBytes, cipherName); - - this.iv = iv; - } catch (Exception e) { - throw new ProcessException(e); - } - } - - public static int getMaxAllowedKeyLength(final String algorithm) { - if (StringUtils.isEmpty(algorithm)) { - return DEFAULT_MAX_ALLOWED_KEY_LENGTH; - } - String parsedCipher = CipherUtility.parseCipherFromAlgorithm(algorithm); - try { - return Cipher.getMaxAllowedKeyLength(parsedCipher); - } catch (NoSuchAlgorithmException e) { - // Default algorithm max key length on unmodified JRE - return DEFAULT_MAX_ALLOWED_KEY_LENGTH; - } - } - - public static boolean supportsUnlimitedStrength() { - return isUnlimitedStrengthCryptographyEnabled; - } - - @Override - public StreamCallback getEncryptionCallback() throws ProcessException { - return new EncryptCallback(); - } - - @Override - public StreamCallback getDecryptionCallback() throws ProcessException { - return new DecryptCallback(); - } - - private class DecryptCallback implements StreamCallback { - - public DecryptCallback() { - } - - @Override - public void process(final InputStream in, final OutputStream out) throws IOException { - // Initialize cipher provider - KeyedCipherProvider cipherProvider = (KeyedCipherProvider) CipherProviderFactory.getCipherProvider(KeyDerivationFunction.NONE); - - // Generate cipher - try { - Cipher cipher; - // The IV could have been set by the constructor, but if not, read from the cipher stream - if (iv.length == 0) { - iv = cipherProvider.readIV(in); - } - cipher = cipherProvider.getCipher(encryptionMethod, key, iv, false); - CipherUtility.processStreams(cipher, in, out); - } catch (Exception e) { - throw new ProcessException(e); - } - } - } - - private class EncryptCallback implements StreamCallback { - - public EncryptCallback() { - } - - @Override - public void process(final InputStream in, final OutputStream out) throws IOException { - // Initialize cipher provider - KeyedCipherProvider cipherProvider = (KeyedCipherProvider) CipherProviderFactory.getCipherProvider(KeyDerivationFunction.NONE); - - // Generate cipher - try { - Cipher cipher = cipherProvider.getCipher(encryptionMethod, key, iv, true); - cipherProvider.writeIV(cipher.getIV(), out); - CipherUtility.processStreams(cipher, in, out); - } catch (Exception e) { - throw new ProcessException(e); - } - } - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi/blob/7d242076/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/NiFiLegacyCipherProvider.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/NiFiLegacyCipherProvider.java b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/NiFiLegacyCipherProvider.java deleted file mode 100644 index c462629..0000000 --- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/NiFiLegacyCipherProvider.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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.util.crypto; - -import org.apache.nifi.processor.exception.ProcessException; -import org.apache.nifi.security.util.EncryptionMethod; -import org.apache.nifi.stream.io.StreamUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.crypto.Cipher; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.security.SecureRandom; - -/** - * Provides a cipher initialized with the original NiFi key derivation process for password-based encryption (MD5 @ 1000 iterations). This is not a secure - * {@link org.apache.nifi.security.util.KeyDerivationFunction} (KDF) and should no longer be used. - * It is provided only for backward-compatibility with legacy data. A strong KDF should be selected for any future use. - * - * @see BcryptCipherProvider - * @see ScryptCipherProvider - * @see PBKDF2CipherProvider - */ -@Deprecated -public class NiFiLegacyCipherProvider extends OpenSSLPKCS5CipherProvider implements PBECipherProvider { - private static final Logger logger = LoggerFactory.getLogger(NiFiLegacyCipherProvider.class); - - // Legacy magic number value - private static final int ITERATION_COUNT = 1000; - - /** - * Returns an initialized cipher for the specified algorithm. The key (and IV if necessary) are derived using the NiFi legacy code, based on @see org.apache.nifi.processors.standard.util.crypto - * .OpenSSLPKCS5CipherProvider#getCipher(java.lang.String, java.lang.String, java.lang.String, byte[], boolean) [essentially {@code MD5(password || salt) * 1000 }]. - * - * @param encryptionMethod the {@link EncryptionMethod} - * @param password the secret input - * @param salt the salt - * @param keyLength the desired key length in bits (ignored because OpenSSL ciphers provide key length in algorithm name) - * @param encryptMode true for encrypt, false for decrypt - * @return the initialized cipher - * @throws Exception if there is a problem initializing the cipher - */ - @Override - public Cipher getCipher(EncryptionMethod encryptionMethod, String password, byte[] salt, int keyLength, boolean encryptMode) throws Exception { - try { - // This method is defined in the OpenSSL implementation and just uses a locally-overridden iteration count - return getInitializedCipher(encryptionMethod, password, salt, encryptMode); - } catch (IllegalArgumentException e) { - throw e; - } catch (Exception e) { - throw new ProcessException("Error initializing the cipher", e); - } - } - - public byte[] generateSalt(EncryptionMethod encryptionMethod) { - byte[] salt = new byte[calculateSaltLength(encryptionMethod)]; - new SecureRandom().nextBytes(salt); - return salt; - } - - protected void validateSalt(EncryptionMethod encryptionMethod, byte[] salt) { - final int saltLength = calculateSaltLength(encryptionMethod); - if (salt.length != saltLength && salt.length != 0) { - throw new IllegalArgumentException("Salt must be " + saltLength + " bytes or empty"); - } - } - - private int calculateSaltLength(EncryptionMethod encryptionMethod) { - try { - Cipher cipher = Cipher.getInstance(encryptionMethod.getAlgorithm(), encryptionMethod.getProvider()); - return cipher.getBlockSize() > 0 ? cipher.getBlockSize() : getDefaultSaltLength(); - } catch (Exception e) { - logger.warn("Encountered exception determining salt length from encryption method {}", encryptionMethod.getAlgorithm(), e); - final int defaultSaltLength = getDefaultSaltLength(); - logger.warn("Returning default length: {} bytes", defaultSaltLength); - return defaultSaltLength; - } - } - - @Override - public byte[] readSalt(InputStream in) throws IOException, ProcessException { - return readSalt(EncryptionMethod.AES_CBC, in); - } - - /** - * Returns the salt provided as part of the cipher stream, or throws an exception if one cannot be detected. - * This method is only implemented by {@link NiFiLegacyCipherProvider} because the legacy salt generation was dependent on the cipher block size. - * - * @param encryptionMethod the encryption method - * @param in the cipher InputStream - * @return the salt - */ - public byte[] readSalt(EncryptionMethod encryptionMethod, InputStream in) throws IOException { - if (in == null) { - throw new IllegalArgumentException("Cannot read salt from null InputStream"); - } - - // The first 8-16 bytes (depending on the cipher blocksize) of the input stream are the salt - final int saltLength = calculateSaltLength(encryptionMethod); - if (in.available() < saltLength) { - throw new ProcessException("The cipher stream is too small to contain the salt"); - } - byte[] salt = new byte[saltLength]; - StreamUtils.fillBuffer(in, salt); - return salt; - } - - @Override - public void writeSalt(byte[] salt, OutputStream out) throws IOException { - if (out == null) { - throw new IllegalArgumentException("Cannot write salt to null OutputStream"); - } - out.write(salt); - } - - @Override - protected int getIterationCount() { - return ITERATION_COUNT; - } -} http://git-wip-us.apache.org/repos/asf/nifi/blob/7d242076/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/OpenPGPKeyBasedEncryptor.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/OpenPGPKeyBasedEncryptor.java b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/OpenPGPKeyBasedEncryptor.java deleted file mode 100644 index f0f8631..0000000 --- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/OpenPGPKeyBasedEncryptor.java +++ /dev/null @@ -1,380 +0,0 @@ -/* - * 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.util.crypto; - -import org.apache.nifi.processor.exception.ProcessException; -import org.apache.nifi.processor.io.StreamCallback; -import org.apache.nifi.processors.standard.EncryptContent; -import org.apache.nifi.processors.standard.EncryptContent.Encryptor; -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.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.PGPOnePassSignatureList; -import org.bouncycastle.openpgp.PGPPrivateKey; -import org.bouncycastle.openpgp.PGPPublicKey; -import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData; -import org.bouncycastle.openpgp.PGPPublicKeyRing; -import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; -import org.bouncycastle.openpgp.PGPSecretKey; -import org.bouncycastle.openpgp.PGPSecretKeyRing; -import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; -import org.bouncycastle.openpgp.PGPUtil; -import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory; -import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor; -import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory; -import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; -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.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.security.NoSuchProviderException; -import java.security.SecureRandom; -import java.util.Date; -import java.util.Iterator; -import java.util.zip.Deflater; - -import static org.apache.nifi.processors.standard.util.PGPUtil.BLOCK_SIZE; -import static org.apache.nifi.processors.standard.util.PGPUtil.BUFFER_SIZE; - -public class OpenPGPKeyBasedEncryptor implements Encryptor { - private static final Logger logger = LoggerFactory.getLogger(OpenPGPPasswordBasedEncryptor.class); - - private String algorithm; - private String provider; - // TODO: This can hold either the secret or public keyring path - private String keyring; - private String userId; - private char[] passphrase; - private String filename; - - public OpenPGPKeyBasedEncryptor(final String algorithm, final String provider, final String keyring, final String userId, final char[] passphrase, final String filename) { - this.algorithm = algorithm; - this.provider = provider; - this.keyring = keyring; - this.userId = userId; - this.passphrase = passphrase; - this.filename = filename; - } - - @Override - public StreamCallback getEncryptionCallback() throws Exception { - return new OpenPGPEncryptCallback(algorithm, provider, keyring, userId, filename); - } - - @Override - public StreamCallback getDecryptionCallback() throws Exception { - return new OpenPGPDecryptCallback(provider, keyring, passphrase); - } - - /** - * Returns true if the passphrase is valid. - * <p> - * This is used in the EncryptContent custom validation to check if the passphrase can extract a private key from the secret key ring. After BC was upgraded from 1.46 to 1.53, the API changed - * so this is performed differently but the functionality is equivalent. - * - * @param provider the provider name - * @param secretKeyringFile the file path to the keyring - * @param passphrase the passphrase - * @return true if the passphrase can successfully extract any private key - * @throws IOException if there is a problem reading the keyring file - * @throws PGPException if there is a problem parsing/extracting the private key - * @throws NoSuchProviderException if the provider is not available - */ - public static boolean validateKeyring(String provider, String secretKeyringFile, char[] passphrase) throws IOException, PGPException, NoSuchProviderException { - try { - getDecryptedPrivateKey(provider, secretKeyringFile, passphrase); - return true; - } catch (Exception e) { - // If this point is reached, no private key could be extracted with the given passphrase - return false; - } - } - - private static PGPPrivateKey getDecryptedPrivateKey(String provider, String secretKeyringFile, char[] passphrase) throws IOException, PGPException { - // TODO: Verify that key IDs cannot be 0 - return getDecryptedPrivateKey(provider, secretKeyringFile, 0L, passphrase); - } - - private static PGPPrivateKey getDecryptedPrivateKey(String provider, String secretKeyringFile, long keyId, char[] passphrase) throws IOException, PGPException { - // TODO: Reevaluate the mechanism for executing this task as performance can suffer here and only a specific key needs to be validated - - // Read in from the secret keyring file - try (FileInputStream keyInputStream = new FileInputStream(secretKeyringFile)) { - - // Form the SecretKeyRing collection (1.53 way with fingerprint calculator) - PGPSecretKeyRingCollection pgpSecretKeyRingCollection = new PGPSecretKeyRingCollection(keyInputStream, new BcKeyFingerprintCalculator()); - - // The decryptor is identical for all keys - final PBESecretKeyDecryptor decryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(provider).build(passphrase); - - // Iterate over all secret keyrings - Iterator<PGPSecretKeyRing> keyringIterator = pgpSecretKeyRingCollection.getKeyRings(); - PGPSecretKeyRing keyRing; - PGPSecretKey secretKey; - - while (keyringIterator.hasNext()) { - keyRing = keyringIterator.next(); - - // If keyId exists, get a specific secret key; else, iterate over all - if (keyId != 0) { - secretKey = keyRing.getSecretKey(keyId); - try { - return secretKey.extractPrivateKey(decryptor); - } catch (Exception e) { - throw new PGPException("No private key available using passphrase", e); - } - } else { - Iterator<PGPSecretKey> keyIterator = keyRing.getSecretKeys(); - - while (keyIterator.hasNext()) { - secretKey = keyIterator.next(); - try { - return secretKey.extractPrivateKey(decryptor); - } catch (Exception e) { - // TODO: Log (expected) failures? - } - } - } - } - } - - // If this point is reached, no private key could be extracted with the given passphrase - throw new PGPException("No private key available using passphrase"); - } - - /* - * Get the public key for a specific user id from a keyring. - */ - @SuppressWarnings("rawtypes") - public static PGPPublicKey getPublicKey(String userId, String publicKeyringFile) throws IOException, PGPException { - // TODO: Reevaluate the mechanism for executing this task as performance can suffer here and only a specific key needs to be validated - - // Read in from the public keyring file - try (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; - } - } - } - } - } - - // If this point is reached, no public key could be extracted with the given userId - throw new PGPException("Could not find a public key with the given userId"); - } - - private static class OpenPGPDecryptCallback implements StreamCallback { - - private String provider; - private String secretKeyringFile; - private char[] passphrase; - - OpenPGPDecryptCallback(final String provider, final String secretKeyringFile, final char[] passphrase) { - this.provider = provider; - this.secretKeyringFile = secretKeyringFile; - this.passphrase = passphrase; - } - - @Override - public void process(InputStream in, OutputStream out) throws IOException { - try (InputStream pgpin = PGPUtil.getDecoderStream(in)) { - PGPObjectFactory pgpFactory = new PGPObjectFactory(pgpin, new BcKeyFingerprintCalculator()); - - Object obj = pgpFactory.nextObject(); - if (!(obj instanceof PGPEncryptedDataList)) { - obj = pgpFactory.nextObject(); - if (!(obj instanceof PGPEncryptedDataList)) { - throw new ProcessException("Invalid OpenPGP data"); - } - } - PGPEncryptedDataList encList = (PGPEncryptedDataList) obj; - - try { - PGPPrivateKey privateKey = null; - PGPPublicKeyEncryptedData encData = null; - - // Find the secret key in the encrypted data - Iterator it = encList.getEncryptedDataObjects(); - while (privateKey == null && it.hasNext()) { - obj = it.next(); - if (!(obj instanceof PGPPublicKeyEncryptedData)) { - throw new ProcessException("Invalid OpenPGP data"); - } - encData = (PGPPublicKeyEncryptedData) obj; - - // Check each encrypted data object to see if it contains the key ID for the secret key -> private key - try { - privateKey = getDecryptedPrivateKey(provider, secretKeyringFile, encData.getKeyID(), passphrase); - } catch (PGPException e) { - // TODO: Log (expected) exception? - } - } - if (privateKey == null) { - throw new ProcessException("Secret keyring does not contain the key required to decrypt"); - } - - // Read in the encrypted data stream and decrypt it - final PublicKeyDataDecryptorFactory dataDecryptor = new JcePublicKeyDataDecryptorFactoryBuilder().setProvider(provider).build(privateKey); - try (InputStream clear = encData.getDataStream(dataDecryptor)) { - // Create a plain object factory - JcaPGPObjectFactory plainFact = new JcaPGPObjectFactory(clear); - - Object message = plainFact.nextObject(); - - // Check the message type and act accordingly - - // If compressed, decompress - if (message instanceof PGPCompressedData) { - PGPCompressedData cData = (PGPCompressedData) message; - JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(cData.getDataStream()); - - message = pgpFact.nextObject(); - } - - // If the message is literal data, read it and process to the out stream - if (message instanceof PGPLiteralData) { - PGPLiteralData literalData = (PGPLiteralData) message; - - try (InputStream lis = literalData.getInputStream()) { - final byte[] buffer = new byte[BLOCK_SIZE]; - int len; - while ((len = lis.read(buffer)) >= 0) { - out.write(buffer, 0, len); - } - } - } else if (message instanceof PGPOnePassSignatureList) { - // TODO: This is legacy code but should verify signature list here - throw new PGPException("encrypted message contains a signed message - not literal data."); - } else { - throw new PGPException("message is not a simple encrypted file - type unknown."); - } - - if (encData.isIntegrityProtected()) { - if (!encData.verify()) { - throw new PGPException("Failed message integrity check"); - } - } else { - logger.warn("No message integrity check"); - } - } - } catch (Exception e) { - throw new ProcessException(e.getMessage()); - } - } - } - - } - - private static class OpenPGPEncryptCallback implements StreamCallback { - - private String algorithm; - private String provider; - private String publicKeyring; - private String userId; - private String filename; - - OpenPGPEncryptCallback(final String algorithm, final String provider, final String keyring, final String userId, final String filename) { - this.algorithm = algorithm; - this.provider = provider; - this.publicKeyring = keyring; - this.userId = userId; - this.filename = filename; - } - - @Override - public void process(InputStream in, OutputStream out) throws IOException { - PGPPublicKey publicKey; - final boolean isArmored = EncryptContent.isPGPArmoredAlgorithm(algorithm); - - try { - publicKey = getPublicKey(userId, publicKeyring); - } catch (Exception e) { - throw new ProcessException("Invalid public keyring - " + e.getMessage()); - } - - try { - OutputStream output = out; - if (isArmored) { - output = new ArmoredOutputStream(out); - } - - try { - // TODO: Refactor internal symmetric encryption algorithm to be customizable - PGPEncryptedDataGenerator encryptedDataGenerator = new PGPEncryptedDataGenerator( - new JcePGPDataEncryptorBuilder(PGPEncryptedData.AES_128).setWithIntegrityPacket(true).setSecureRandom(new SecureRandom()).setProvider(provider)); - - encryptedDataGenerator.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(publicKey).setProvider(provider)); - - // TODO: Refactor shared encryption code to utility - try (OutputStream encryptedOut = encryptedDataGenerator.open(output, new byte[BUFFER_SIZE])) { - PGPCompressedDataGenerator compressedDataGenerator = new PGPCompressedDataGenerator(PGPCompressedData.ZIP, Deflater.BEST_SPEED); - try (OutputStream compressedOut = compressedDataGenerator.open(encryptedOut, new byte[BUFFER_SIZE])) { - PGPLiteralDataGenerator literalDataGenerator = new PGPLiteralDataGenerator(); - try (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 = in.read(buffer)) >= 0) { - literalOut.write(buffer, 0, len); - } - } - } - } - } finally { - if (isArmored) { - output.close(); - } - } - } catch (Exception e) { - throw new ProcessException(e.getMessage()); - } - } - } -} http://git-wip-us.apache.org/repos/asf/nifi/blob/7d242076/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/OpenPGPPasswordBasedEncryptor.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/OpenPGPPasswordBasedEncryptor.java b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/OpenPGPPasswordBasedEncryptor.java deleted file mode 100644 index 93e565a..0000000 --- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/OpenPGPPasswordBasedEncryptor.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * 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.util.crypto; - -import org.apache.nifi.processor.exception.ProcessException; -import org.apache.nifi.processor.io.StreamCallback; -import org.apache.nifi.processors.standard.EncryptContent.Encryptor; -import org.bouncycastle.openpgp.PGPCompressedData; -import org.bouncycastle.openpgp.PGPEncryptedData; -import org.bouncycastle.openpgp.PGPEncryptedDataList; -import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPLiteralData; -import org.bouncycastle.openpgp.PGPPBEEncryptedData; -import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory; -import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory; -import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider; -import org.bouncycastle.openpgp.operator.PGPKeyEncryptionMethodGenerator; -import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; -import org.bouncycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder; -import org.bouncycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import static org.bouncycastle.openpgp.PGPUtil.getDecoderStream; - -public class OpenPGPPasswordBasedEncryptor implements Encryptor { - private static final Logger logger = LoggerFactory.getLogger(OpenPGPPasswordBasedEncryptor.class); - - private String algorithm; - private String provider; - private char[] password; - private String filename; - - public OpenPGPPasswordBasedEncryptor(final String algorithm, final String provider, final char[] passphrase, final String filename) { - this.algorithm = algorithm; - this.provider = provider; - this.password = passphrase; - this.filename = filename; - } - - @Override - public StreamCallback getEncryptionCallback() throws Exception { - return new OpenPGPEncryptCallback(algorithm, provider, password, filename); - } - - @Override - public StreamCallback getDecryptionCallback() throws Exception { - return new OpenPGPDecryptCallback(provider, password); - } - - private static class OpenPGPDecryptCallback implements StreamCallback { - - private String provider; - private char[] password; - - OpenPGPDecryptCallback(final String provider, final char[] password) { - this.provider = provider; - this.password = password; - } - - @Override - public void process(InputStream in, OutputStream out) throws IOException { - InputStream pgpin = getDecoderStream(in); - JcaPGPObjectFactory pgpFactory = new JcaPGPObjectFactory(pgpin); - - Object obj = pgpFactory.nextObject(); - if (!(obj instanceof PGPEncryptedDataList)) { - obj = pgpFactory.nextObject(); - if (!(obj instanceof PGPEncryptedDataList)) { - throw new ProcessException("Invalid OpenPGP data"); - } - } - PGPEncryptedDataList encList = (PGPEncryptedDataList) obj; - - obj = encList.get(0); - if (!(obj instanceof PGPPBEEncryptedData)) { - throw new ProcessException("Invalid OpenPGP data"); - } - PGPPBEEncryptedData encryptedData = (PGPPBEEncryptedData) obj; - - try { - final PGPDigestCalculatorProvider digestCalculatorProvider = new JcaPGPDigestCalculatorProviderBuilder().setProvider(provider).build(); - final PBEDataDecryptorFactory decryptorFactory = new JcePBEDataDecryptorFactoryBuilder(digestCalculatorProvider).setProvider(provider).build(password); - InputStream clear = encryptedData.getDataStream(decryptorFactory); - - JcaPGPObjectFactory pgpObjectFactory = new JcaPGPObjectFactory(clear); - - obj = pgpObjectFactory.nextObject(); - if (obj instanceof PGPCompressedData) { - PGPCompressedData compressedData = (PGPCompressedData) obj; - pgpObjectFactory = new JcaPGPObjectFactory(compressedData.getDataStream()); - obj = pgpObjectFactory.nextObject(); - } - - PGPLiteralData literalData = (PGPLiteralData) obj; - InputStream plainIn = literalData.getInputStream(); - final byte[] buffer = new byte[org.apache.nifi.processors.standard.util.PGPUtil.BLOCK_SIZE]; - int len; - while ((len = plainIn.read(buffer)) >= 0) { - out.write(buffer, 0, len); - } - - if (encryptedData.isIntegrityProtected()) { - if (!encryptedData.verify()) { - throw new PGPException("Integrity check failed"); - } - } else { - logger.warn("No message integrity check"); - } - } catch (Exception e) { - throw new ProcessException(e.getMessage()); - } - } - } - - private static class OpenPGPEncryptCallback implements StreamCallback { - - private String algorithm; - private String provider; - private char[] password; - private String filename; - - OpenPGPEncryptCallback(final String algorithm, final String provider, final char[] password, final String filename) { - this.algorithm = algorithm; - this.provider = provider; - this.password = password; - this.filename = filename; - } - - @Override - public void process(InputStream in, OutputStream out) throws IOException { - try { - PGPKeyEncryptionMethodGenerator encryptionMethodGenerator = new JcePBEKeyEncryptionMethodGenerator(password).setProvider(provider); - org.apache.nifi.processors.standard.util.PGPUtil.encrypt(in, out, algorithm, provider, PGPEncryptedData.AES_128, filename, encryptionMethodGenerator); - } catch (Exception e) { - throw new ProcessException(e.getMessage()); - } - } - } -} http://git-wip-us.apache.org/repos/asf/nifi/blob/7d242076/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/OpenSSLPKCS5CipherProvider.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/OpenSSLPKCS5CipherProvider.java b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/OpenSSLPKCS5CipherProvider.java deleted file mode 100644 index 4253f9b..0000000 --- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/OpenSSLPKCS5CipherProvider.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * 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.util.crypto; - -import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.processor.exception.ProcessException; -import org.apache.nifi.security.util.EncryptionMethod; -import org.apache.nifi.stream.io.StreamUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.crypto.Cipher; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.SecretKey; -import javax.crypto.SecretKeyFactory; -import javax.crypto.spec.PBEKeySpec; -import javax.crypto.spec.PBEParameterSpec; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.charset.StandardCharsets; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.SecureRandom; -import java.security.spec.InvalidKeySpecException; -import java.util.Arrays; - -public class OpenSSLPKCS5CipherProvider implements PBECipherProvider { - private static final Logger logger = LoggerFactory.getLogger(OpenSSLPKCS5CipherProvider.class); - - // Legacy magic number value - private static final int ITERATION_COUNT = 0; - private static final int DEFAULT_SALT_LENGTH = 8; - private static final byte[] EMPTY_SALT = new byte[8]; - - private static final String OPENSSL_EVP_HEADER_MARKER = "Salted__"; - private static final int OPENSSL_EVP_HEADER_SIZE = 8; - - /** - * Returns an initialized cipher for the specified algorithm. The key (and IV if necessary) are derived using the - * <a href="https://www.openssl.org/docs/manmaster/crypto/EVP_BytesToKey.html">OpenSSL EVP_BytesToKey proprietary KDF</a> [essentially {@code MD5(password || salt) }]. - * - * @param encryptionMethod the {@link EncryptionMethod} - * @param password the secret input - * @param salt the salt - * @param keyLength the desired key length in bits (ignored because OpenSSL ciphers provide key length in algorithm name) - * @param encryptMode true for encrypt, false for decrypt - * @return the initialized cipher - * @throws Exception if there is a problem initializing the cipher - */ - @Override - public Cipher getCipher(EncryptionMethod encryptionMethod, String password, byte[] salt, int keyLength, boolean encryptMode) throws Exception { - try { - return getInitializedCipher(encryptionMethod, password, salt, encryptMode); - } catch (IllegalArgumentException e) { - throw e; - } catch (Exception e) { - throw new ProcessException("Error initializing the cipher", e); - } - } - - /** - * Convenience method without key length parameter. See {@link OpenSSLPKCS5CipherProvider#getCipher(EncryptionMethod, String, int, boolean)} - * - * @param encryptionMethod the {@link EncryptionMethod} - * @param password the secret input - * @param encryptMode true for encrypt, false for decrypt - * @return the initialized cipher - * @throws Exception if there is a problem initializing the cipher - */ - public Cipher getCipher(EncryptionMethod encryptionMethod, String password, boolean encryptMode) throws Exception { - return getCipher(encryptionMethod, password, new byte[0], -1, encryptMode); - } - - /** - * Convenience method without key length parameter. See {@link OpenSSLPKCS5CipherProvider#getCipher(EncryptionMethod, String, byte[], int, boolean)} - * - * @param encryptionMethod the {@link EncryptionMethod} - * @param password the secret input - * @param salt the salt - * @param encryptMode true for encrypt, false for decrypt - * @return the initialized cipher - * @throws Exception if there is a problem initializing the cipher - */ - public Cipher getCipher(EncryptionMethod encryptionMethod, String password, byte[] salt, boolean encryptMode) throws Exception { - return getCipher(encryptionMethod, password, salt, -1, encryptMode); - } - - protected Cipher getInitializedCipher(EncryptionMethod encryptionMethod, String password, byte[] salt, boolean encryptMode) - throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, - InvalidAlgorithmParameterException { - if (encryptionMethod == null) { - throw new IllegalArgumentException("The encryption method must be specified"); - } - - if (StringUtils.isEmpty(password)) { - throw new IllegalArgumentException("Encryption with an empty password is not supported"); - } - - validateSalt(encryptionMethod, salt); - - String algorithm = encryptionMethod.getAlgorithm(); - String provider = encryptionMethod.getProvider(); - - // Initialize secret key from password - final PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray()); - final SecretKeyFactory factory = SecretKeyFactory.getInstance(algorithm, provider); - SecretKey tempKey = factory.generateSecret(pbeKeySpec); - - final PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, getIterationCount()); - Cipher cipher = Cipher.getInstance(algorithm, provider); - cipher.init(encryptMode ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, tempKey, parameterSpec); - return cipher; - } - - protected void validateSalt(EncryptionMethod encryptionMethod, byte[] salt) { - if (salt.length != DEFAULT_SALT_LENGTH && salt.length != 0) { - // This does not enforce ASCII encoding, just length - throw new IllegalArgumentException("Salt must be 8 bytes US-ASCII encoded or empty"); - } - } - - protected int getIterationCount() { - return ITERATION_COUNT; - } - - @Override - public byte[] generateSalt() { - byte[] salt = new byte[getDefaultSaltLength()]; - new SecureRandom().nextBytes(salt); - return salt; - } - - @Override - public int getDefaultSaltLength() { - return DEFAULT_SALT_LENGTH; - } - - /** - * Returns the salt provided as part of the cipher stream, or throws an exception if one cannot be detected. - * - * @param in the cipher InputStream - * @return the salt - */ - @Override - public byte[] readSalt(InputStream in) throws IOException { - if (in == null) { - throw new IllegalArgumentException("Cannot read salt from null InputStream"); - } - - // The header and salt format is "Salted__salt x8b" in ASCII - byte[] salt = new byte[DEFAULT_SALT_LENGTH]; - - // Try to read the header and salt from the input - byte[] header = new byte[OPENSSL_EVP_HEADER_SIZE]; - - // Mark the stream in case there is no salt - in.mark(OPENSSL_EVP_HEADER_SIZE + 1); - StreamUtils.fillBuffer(in, header); - - final byte[] headerMarkerBytes = OPENSSL_EVP_HEADER_MARKER.getBytes(StandardCharsets.US_ASCII); - - if (!Arrays.equals(headerMarkerBytes, header)) { - // No salt present - salt = new byte[0]; - // Reset the stream because we skipped 8 bytes of cipher text - in.reset(); - } - - StreamUtils.fillBuffer(in, salt); - return salt; - } - - @Override - public void writeSalt(byte[] salt, OutputStream out) throws IOException { - if (out == null) { - throw new IllegalArgumentException("Cannot write salt to null OutputStream"); - } - - out.write(OPENSSL_EVP_HEADER_MARKER.getBytes(StandardCharsets.US_ASCII)); - out.write(salt); - } -} http://git-wip-us.apache.org/repos/asf/nifi/blob/7d242076/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/PBECipherProvider.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/PBECipherProvider.java b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/PBECipherProvider.java deleted file mode 100644 index 4d9fcfc..0000000 --- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/crypto/PBECipherProvider.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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.util.crypto; - -import org.apache.nifi.security.util.EncryptionMethod; - -import javax.crypto.Cipher; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -public interface PBECipherProvider extends CipherProvider { - - /** - * Returns an initialized cipher for the specified algorithm. The key (and IV if necessary) are derived by the KDF of the implementation. - * <p/> - * The IV can be retrieved by the calling method using {@link Cipher#getIV()}. - * - * @param encryptionMethod the {@link EncryptionMethod} - * @param password the secret input - * @param salt the salt - * @param keyLength the desired key length in bits - * @param encryptMode true for encrypt, false for decrypt - * @return the initialized cipher - * @throws Exception if there is a problem initializing the cipher - */ - Cipher getCipher(EncryptionMethod encryptionMethod, String password, byte[] salt, int keyLength, boolean encryptMode) throws Exception; - - /** - * Returns a random salt suitable for this cipher provider. - * - * @return a random salt - * @see PBECipherProvider#getDefaultSaltLength() - */ - byte[] generateSalt(); - - /** - * Returns the default salt length for this implementation. - * - * @return the default salt length in bytes - */ - int getDefaultSaltLength(); - - /** - * Returns the salt provided as part of the cipher stream, or throws an exception if one cannot be detected. - * - * @param in the cipher InputStream - * @return the salt - */ - byte[] readSalt(InputStream in) throws IOException; - - /** - * Writes the salt provided as part of the cipher stream, or throws an exception if it cannot be written. - * - * @param salt the salt - * @param out the cipher OutputStream - */ - void writeSalt(byte[] salt, OutputStream out) throws IOException; -}
