Github user alopresto commented on the pull request:
https://github.com/apache/nifi/pull/140#issuecomment-162039927
After debugging, this is related to the JCE Unlimited Strength
Cryptographic Policies that must be installed to allow the JRE to use key
lengths above export limits. AES is restricted to 128 bit (16 bytes) by default
without these policy files.
However, in examining the code, we believe we have found a larger issue in
Java's handling of the key when using password-based encryption. In a normal
scenario, the key is generated or derived and then used to initialize the
cipher as follows:
```java
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
String key =
Hex.encodeHexString(sha1.digest("thisIsABadPassword".getBytes())).substring(0,
31);
String iv =
Hex.encodeHexString(sha1.digest("thisIsABadIv".getBytes())).substring(0, 31);
SecretKey secretKey = new
SecretKeySpec(Hex.decodeHex(key.toCharArray()), "AES");
IvParameterSpec ivParameterSpec = new
IvParameterSpec(Hex.decodeHex(iv.toCharArray()));
cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey,
ivParameterSpec);
String message = "This is a plaintext message.";
byte[] cipherBytes = cipher.doFinal(message.getBytes());
cipher.init(Cipher.DECRYPT_MODE, secretKey,
ivParameterSpec);
byte[] recoveredBytes = cipher.doFinal(cipherBytes);
System.out.println("Recovered message: " + new
String(recoveredBytes));
} catch (Exception e) {
fail(e.getMessage());
}
```
During `cipher.init()`, the system checks
`Cipher.getMaxAllowedKeyLength("AES")` for the key limit in bits. As 32 hex ==
16 bytes == 128 bits, this will run on a vanilla JRE installation. However,
using the password-based encryption `PBEParameterSpec` is slightly different.
```java
String password = "thisIsABadPassword";
String salt = "SALTSALT";
PBEKeySpec keySpec = new PBEKeySpec(password);
SecretKeyFactory keyFactory =
SecretKeyFactory.getInstance("PBEWITHMD5AND256BITAES-CBC-OPENSSL", "BC");
SecretKey key = keyFactory.generateSecret(keySpec);
PBEParameterSpec saltSpec = new
PBEParameterSpec(salt.getBytes("US-ASCII"), 0);
Cipher cipher =
Cipher.getInstance("PBEWITHMD5AND256BITAES-CBC-OPENSSL", "BC");
cipher.init(Cipher.ENCRYPT_MODE, key, saltSpec);
String message = "This is a plaintext message.";
byte[] cipherBytes = cipher.doFinal(message.getBytes());
cipher.init(Cipher.DECRYPT_MODE, key, saltSpec);
byte[] recoveredBytes = cipher.doFinal(cipherBytes);
System.out.println("Recovered message: " + new
String(recoveredBytes));
} catch (Exception e) {
fail(e.getMessage());
}
```
The actual key is not derived until **during** `cipher.init()`, so at the
time the key length check is done, it is actually checking the length of the
raw password. This means that a vanilla JRE installation can use 256-bit AES to
encrypt or decrypt a file provided the password is <= 16 bytes. The tests above
fail because the provided password is 18 bytes. However, the algorithm used is
still AES-256-CBC, which means the derived key is 32 bytes. This has been
verified on a vanilla JRE installation decrypting OpenSSL AES-256-CBC encrypted
files.
I will look at @apiri 's patch on NIFI-1242 but this is a serious issue and
should be well-documented. The usual test of "is available on a vanilla JRE
because the key length is <= 128 bits (for AES)" is no longer sufficient
because the supplied password length now affects the determination.
---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at [email protected] or file a JIRA ticket
with INFRA.
---