[ 
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)

Reply via email to