public static boolean isKeyedCipher(String algorithm) { EncryptionMethod em = EncryptionMethod.forAlgorithm(algorithm); return em != null && em.isKeyedCipher(); }
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; } }
if (!encryptionMethod.isKeyedCipher()) { throw new IllegalArgumentException(encryptionMethod.name() + " requires a PBECipherProvider"); String algorithm = encryptionMethod.getAlgorithm(); String provider = encryptionMethod.getProvider();
public static boolean isPBECipher(String algorithm) { EncryptionMethod em = EncryptionMethod.forAlgorithm(algorithm); return em != null && em.isPBECipher(); }
private static AllowableValue[] buildEncryptionMethodAllowableValues() { final EncryptionMethod[] encryptionMethods = EncryptionMethod.values(); List<AllowableValue> allowableValues = new ArrayList<>(encryptionMethods.length); for (EncryptionMethod em : encryptionMethods) { allowableValues.add(new AllowableValue(em.name(), em.name(), em.toString())); } return allowableValues.toArray(new AllowableValue[0]); }
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 PBKDF2"); } String algorithm = encryptionMethod.getAlgorithm(); 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); } if (StringUtils.isEmpty(password)) { throw new IllegalArgumentException("Encryption with an empty password is not supported"); } if (salt == null || salt.length < DEFAULT_SALT_LENGTH) { throw new IllegalArgumentException("The salt must be at least " + DEFAULT_SALT_LENGTH + " bytes. To generate a salt, use PBKDF2CipherProvider#generateSalt()"); } PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(this.prf); gen.init(password.getBytes(StandardCharsets.UTF_8), salt, getIterationCount()); byte[] dk = ((KeyParameter) gen.generateDerivedParameters(keyLength)).getKey(); SecretKey tempKey = new SecretKeySpec(dk, algorithm); KeyedCipherProvider keyedCipherProvider = new AESKeyedCipherProvider(); return keyedCipherProvider.getCipher(encryptionMethod, tempKey, iv, encryptMode); }
if (encryptionMethod.isUnlimitedStrength()) { validationResults.add(new ValidationResult.Builder().subject(ENCRYPTION_ALGORITHM.getName()) .explanation(encryptionMethod.name() + " (" + encryptionMethod.getAlgorithm() + ") is not supported by this JVM due to lacking JCE Unlimited " + "Strength Jurisdiction Policy files. See Admin Guide.").build()); .explanation(RAW_KEY_HEX.getDisplayName() + " is required when using algorithm " + encryptionMethod.getAlgorithm() + ". See Admin Guide.").build()); } else { byte[] keyBytes = new byte[0]; " due to lacking JCE Unlimited Strength Jurisdiction Policy files. See Admin Guide.").build()); if (!CipherUtility.isValidKeyLengthForAlgorithm(keyBytes.length * 8, encryptionMethod.getAlgorithm())) { List<Integer> validKeyLengths = CipherUtility.getValidKeyLengthsForAlgorithm(encryptionMethod.getAlgorithm()); validationResults.add(new ValidationResult.Builder().subject(RAW_KEY_HEX.getName()) .explanation("Key must be valid length [" + StringUtils.join(validKeyLengths, ", ") + "]. See Admin Guide.").build()); validationResults.add(new ValidationResult.Builder().subject(KEY_DERIVATION_FUNCTION.getName()) .explanation(KEY_DERIVATION_FUNCTION.getDisplayName() + " is required to be " + StringUtils.join(kdfsForKeyedCipher, ", ") + " when using algorithm " + encryptionMethod.getAlgorithm()).build());
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); }
for (final EncryptionMethod encryptionMethod : EncryptionMethod.values()) { if (encryptionMethod.isUnlimitedStrength()) { continue; // cannot test unlimited strength in unit tests because it's not enabled by the JVM by default. if (encryptionMethod.isKeyedCipher()) { continue; logger.info("Attempting {}", encryptionMethod.name()); testRunner.setProperty(EncryptContent.ENCRYPTION_ALGORITHM, encryptionMethod.name()); testRunner.setProperty(EncryptContent.MODE, EncryptContent.ENCRYPT_MODE); testRunner.assertAllFlowFilesTransferred(EncryptContent.REL_SUCCESS, 1); logger.info("Successfully decrypted {}", encryptionMethod.name());
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); } }
@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 (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).evaluateAttributeExpressions().getValue(); validationResults.addAll(validatePGP(encryptionMethod, password, encrypt, publicKeyring, publicUserId, privateKeyring, privateKeyringPassphrase)); } else { // Not PGP if (encryptionMethod.isKeyedCipher()) { // Raw key validationResults.addAll(validateKeyed(encryptionMethod, kdf, keyHex)); } else { // PBE boolean allowWeakCrypto = context.getProperty(ALLOW_WEAK_CRYPTO).getValue().equalsIgnoreCase(WEAK_CRYPTO_ALLOWED_NAME); validationResults.addAll(validatePBE(encryptionMethod, kdf, password, allowWeakCrypto)); } } return validationResults; }
final EncryptionMethod encryptionMethod = EncryptionMethod.valueOf(method); final String providerName = encryptionMethod.getProvider(); final String algorithm = encryptionMethod.getAlgorithm(); final String password = context.getProperty(PASSWORD).getValue(); final KeyDerivationFunction kdf = KeyDerivationFunction.valueOf(context.getProperty(KEY_DERIVATION_FUNCTION).getValue());
@Test public void testPGPDecrypt() throws IOException { final TestRunner runner = TestRunners.newTestRunner(EncryptContent.class); runner.setProperty(EncryptContent.MODE, EncryptContent.DECRYPT_MODE); runner.setProperty(EncryptContent.ENCRYPTION_ALGORITHM, EncryptionMethod.PGP_ASCII_ARMOR.name()); runner.setProperty(EncryptContent.PASSWORD, "Hello, World!"); runner.enqueue(Paths.get("src/test/resources/TestEncryptContent/text.txt.asc")); runner.run(); runner.assertAllFlowFilesTransferred(EncryptContent.REL_SUCCESS, 1); final MockFlowFile flowFile = runner.getFlowFilesForRelationship(EncryptContent.REL_SUCCESS).get(0); flowFile.assertContentEquals(Paths.get("src/test/resources/TestEncryptContent/text.txt")); }
public static int getMaximumPasswordLengthForAlgorithmOnLimitedStrengthCrypto(EncryptionMethod encryptionMethod) { if (encryptionMethod == null) { throw new IllegalArgumentException("Cannot evaluate an empty encryption method algorithm"); } return MAX_PASSWORD_LENGTH_BY_ALGORITHM.getOrDefault(encryptionMethod.getAlgorithm(), -1); }
private byte[] decryptKeyed(byte[] cipherBytes) { KeyedCipherProvider keyedcp = (KeyedCipherProvider) cipherProvider; // Generate cipher try { int ivLength = 16; byte[] iv = new byte[ivLength]; System.arraycopy(cipherBytes, 0, iv, 0, ivLength); byte[] actualCipherBytes = Arrays.copyOfRange(cipherBytes, ivLength, cipherBytes.length); Cipher cipher = keyedcp.getCipher(EncryptionMethod.forAlgorithm(algorithm), key, iv, false); // Encrypt the plaintext return cipher.doFinal(actualCipherBytes); } catch (Exception e) { throw new EncryptionException("Could not decrypt sensitive value", e); } }
public static EncryptionMethod forAlgorithm(String algorithm) { if (StringUtils.isNotBlank(algorithm)) { for (EncryptionMethod em : EncryptionMethod.values()) { if (em.algorithm.equalsIgnoreCase(algorithm)) { return em; } } } return null; } }
@Override public String toString() { final ToStringBuilder builder = new ToStringBuilder(this); ToStringBuilder.setDefaultStyle(ToStringStyle.SHORT_PREFIX_STYLE); builder.append("Algorithm name", algorithm); builder.append("Requires unlimited strength JCE policy", unlimitedStrength); builder.append("Algorithm Provider", provider); builder.append("Compatible with strong KDFs", compatibleWithStrongKDFs); builder.append("Keyed cipher", isKeyedCipher()); return builder.toString(); }
private List<String> getKDFsForPBECipher(EncryptionMethod encryptionMethod) { List<String> kdfsForPBECipher = new ArrayList<>(); for (KeyDerivationFunction k : KeyDerivationFunction.values()) { // Add all weak (legacy) KDFs except NONE if (!k.isStrongKDF() && !k.equals(KeyDerivationFunction.NONE)) { kdfsForPBECipher.add(k.name()); // If this algorithm supports strong KDFs, add them as well } else if ((encryptionMethod.isCompatibleWithStrongKDFs() && k.isStrongKDF())) { kdfsForPBECipher.add(k.name()); } } return kdfsForPBECipher; }
throw new IllegalArgumentException("The encryption method must be specified"); if (!encryptionMethod.isCompatibleWithStrongKDFs()) { throw new IllegalArgumentException(encryptionMethod.name() + " is not compatible with Scrypt"); String algorithm = encryptionMethod.getAlgorithm();
.explanation(PASSWORD.getDisplayName() + " is required when using algorithm " + encryptionMethod.getAlgorithm()).build()); return validationResults; if (encryptionMethod.isUnlimitedStrength()) { validationResults.add(new ValidationResult.Builder().subject(ENCRYPTION_ALGORITHM.getName()) .explanation(encryptionMethod.name() + " (" + encryptionMethod.getAlgorithm() + ") is not supported by this JVM due to lacking JCE Unlimited " + "Strength Jurisdiction Policy files. See Admin Guide.").build()); validationResults.add(new ValidationResult.Builder().subject(displayName) .explanation(displayName + " is required to be " + StringUtils.join(kdfsForPBECipher, ", ") + " when using algorithm " + encryptionMethod.getAlgorithm() + ". See Admin Guide.").build());