[
https://issues.apache.org/jira/browse/NIFI-1242?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15042118#comment-15042118
]
Andy LoPresto commented on NIFI-1242:
-------------------------------------
[~joewitt] is correct that the `Cipher.getMaxAllowedKeyLength(String
algorithm)` method will return either the limit on key size for a vanilla JRE
installation or `Integer.MAX_VALUE` for an unlimited strength cryptography
installation. However, as this value is dependent on the algorithm (see [JCA
documentation](http://docs.oracle.com/javase/7/docs/technotes/guides/security/SunProviders.html#importlimits)
for limits per algorithm), we need to combine this check with the check of the
length of the **raw password**, not the length of the derived key. I have
written the following in the PR and merged [~aldrin]'s changes as well as fixed
a bug in the associated test and improved the key length determination method.
I will raise a ticket to reevaluate this for 0.5.0 as we should decide
definitively if we allow "any cipher that works" or "ciphers that are legally
permissible based on installed JCE policy" to run.
---
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
try {
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
String key =
Hex.encodeHexString(sha1.digest("thisIsABadPassword".getBytes())).substring(0,
32);
String iv =
Hex.encodeHexString(sha1.digest("thisIsABadIv".getBytes())).substring(0, 32);
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
try {
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.
> Password-based encryption is not compatible with OpenSSL
> --------------------------------------------------------
>
> Key: NIFI-1242
> URL: https://issues.apache.org/jira/browse/NIFI-1242
> Project: Apache NiFi
> Issue Type: Bug
> Components: Extensions
> Affects Versions: 0.4.0
> Reporter: Andy LoPresto
> Assignee: Andy LoPresto
> Priority: Critical
> Labels: security
> Fix For: 0.4.0
>
> Attachments: NIFI-1242.0001.patch
>
> Original Estimate: 24h
> Remaining Estimate: 24h
>
> Despite the algorithm names indicating compatibility with OpenSSL, the
> current password-based encryption processors cannot decrypt data that was
> encrypted with OpenSSL external to NiFi.
> I will create a new OpenSSLPBEEncryptor implementation, a new
> EncryptionMethod, and wire the logic in EncryptContent to select the correct
> encryptor.
> I have a more in-depth explanation of the issue at
> https://github.com/alopresto/opensslpbeencryptor/blob/master/blog.md, but the
> fix is done in a sandbox and will be moved into NiFi by morning 12/03/15.
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)