Project

General

Profile

Actions

Task #2214

closed

Allow stronger ciphers - enhance Java security policy file

Added by Petr Fišer about 4 years ago. Updated almost 4 years ago.

Status:
Closed
Priority:
Normal
Assignee:
Petr Fišer
Target version:
-
Start date:
04/17/2020
Due date:
% Done:

0%

Estimated time:
Owner:

Description

This is a limitation made by Oracle due to government-imposed limit of export of strong crypto algorithms from the USA.
We should be able to integrate this... so IdM can support stronger ciphers.

Related issues

Related to IdStory Identity Manager - Task #2355: Confidential storage cipher uses hardcoded initialization vectorClosedOndřej Kopr07/01/202009/16/2020

Actions
Actions #1

Updated by Petr Fišer about 4 years ago

  • Target version deleted (152)
Actions #2

Updated by Petr Fišer almost 4 years ago

  • Assignee set to Petr Fišer
Actions #3

Updated by Petr Fišer almost 4 years ago

I think this will need a bit of testing. Currently, our images are based on CentOS7. Also, my primary goal was to enable AES-256 and not to deal with (EC)DHE but when I think about it now... it does not seem like a bad idea. We could kill two birds with one stone.

ECDHE was removed by RH from early openjdk8 but returned around ~openjdk8u90. The AES probably didn't budge and stayed on 128b version until today.
So the first thing is to assess which ciphers we currently have on our basis (RHEL/CentOS 7 latest; openjdk8 latest), both symmetrical and asymmetrical and also EC/non-EC. Just to be sure.
Next thing to do is to make it work and assess which ciphers we got and if it is what we really want.

Options:
  • Java Unlimited policy file for JDK8 https://www.oracle.com/java/technologies/javase-jce8-downloads.html . The more i dig through those Oracle's licensing docs, the crazier this idea seems.
  • Use Oracle's native EC library libsunec.so. Same agrument as above. Also, this does not solve our AES-256 wish.
  • Go to openjdk9 (at least), there should be strong ciphers packed within, just add a crypto.policy=unlimited property and we should be good to go. This would be ideal. Not sure how we stand with Java9 support in the product.
  • Go the BouncyCastle way and register it as a lower-priority security provider. This is really appealing choice. :)
    # quote from one of SO links below
    Add the bcprov-<verion>.jar to /usr/lib/jvm/jre/lib/ext
    Edit /usr/lib/jvm/jre/lib/security/java.security adding the following line to the list of providers:
    security.provider.6=org.bouncycastle.jce.provider.BouncyCastleProvider
    

Reading (I know those posts are like 3yo - that's why I suggest to assess ciphers first):
https://stackoverflow.com/questions/31971499/ecdhe-cipher-suites-not-supported-on-openjdk-8-installed-on-ec2-linux-machine
https://security.stackexchange.com/questions/117975/how-to-enable-ecdhe-in-openjdk-1-8-0-in-centos-6-7
http://mail.openjdk.java.net/pipermail/security-dev/2016-October/014943.html

Actions #4

Updated by Petr Fišer almost 4 years ago

I created a CentOS7-based container and made Java8 barf its supported ciphers (default setting after installation). This actually looks like we already have what we want.
SSL ciphers there are cool. Also the AES-256 seems to be present, at least with NoPadding. I think we use PKCS5Padding in the product. Need to check.

[root@javaciphers /]# java ListCiphers 
SSL ciphers list:
Default    Cipher
*    TLS_DHE_DSS_WITH_AES_128_CBC_SHA
*    TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
*    TLS_DHE_DSS_WITH_AES_128_GCM_SHA256
*    TLS_DHE_DSS_WITH_AES_256_CBC_SHA
*    TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
*    TLS_DHE_DSS_WITH_AES_256_GCM_SHA384
*    TLS_DHE_RSA_WITH_AES_128_CBC_SHA
*    TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
*    TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
*    TLS_DHE_RSA_WITH_AES_256_CBC_SHA
*    TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
*    TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
*    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
*    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
*    TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
*    TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
*    TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
*    TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
*    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
*    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
*    TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
*    TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
*    TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
*    TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
*    TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
*    TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
*    TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
*    TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
*    TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
*    TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
*    TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
*    TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
*    TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
*    TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
*    TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
*    TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
*    TLS_EMPTY_RENEGOTIATION_INFO_SCSV
*    TLS_RSA_WITH_AES_128_CBC_SHA
*    TLS_RSA_WITH_AES_128_CBC_SHA256
*    TLS_RSA_WITH_AES_128_GCM_SHA256
*    TLS_RSA_WITH_AES_256_CBC_SHA
*    TLS_RSA_WITH_AES_256_CBC_SHA256
*    TLS_RSA_WITH_AES_256_GCM_SHA384

Encryption algorithms:
AES
AESWrap
AESWrap_128
AESWrap_192
AESWrap_256
AES_128/CBC/NoPadding
AES_128/CFB/NoPadding
AES_128/ECB/NoPadding
AES_128/GCM/NoPadding
AES_128/OFB/NoPadding
AES_192/CBC/NoPadding
AES_192/CFB/NoPadding
AES_192/ECB/NoPadding
AES_192/GCM/NoPadding
AES_192/OFB/NoPadding
AES_256/CBC/NoPadding
AES_256/CFB/NoPadding
AES_256/ECB/NoPadding
AES_256/GCM/NoPadding
AES_256/OFB/NoPadding
ARCFOUR
Blowfish
DES
DESede
DESedeWrap
PBEWithHmacSHA1AndAES_128
PBEWithHmacSHA1AndAES_256
PBEWithHmacSHA224AndAES_128
PBEWithHmacSHA224AndAES_256
PBEWithHmacSHA256AndAES_128
PBEWithHmacSHA256AndAES_256
PBEWithHmacSHA384AndAES_128
PBEWithHmacSHA384AndAES_256
PBEWithHmacSHA512AndAES_128
PBEWithHmacSHA512AndAES_256
PBEWithMD5AndDES
PBEWithMD5AndTripleDES
PBEWithSHA1AndDESede
PBEWithSHA1AndRC2_128
PBEWithSHA1AndRC2_40
PBEWithSHA1AndRC4_128
PBEWithSHA1AndRC4_40
RC2
RSA
Actions #5

Updated by Petr Fišer almost 4 years ago

Did a little testing with this piece of code (taken from IdM and bent for what I needed to test):

import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

public class TestCipher {
    public static void main(String[] args) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
        String alg = "AES/CBC/PKCS5Padding";
        //abcdefghijklmnop = 6162636465666768696a6b6c6d6e6f70 as hex
        byte[] iv1 = "abcdefghijklmnop".getBytes();
        byte[] sk1 = "abcdefghijklmnop".getBytes();
        // this is right. AES has 128b blocks and CBC mode needs IV to be the size of a block
        byte[] iv2 = "abcdefghijklmnop".getBytes();
        byte[] sk2 = "abcdefghijklmnopabcdefghijklmnop".getBytes();

        SecretKey sk128 = new SecretKeySpec(sk1, "AES");
        testCipherInit(Cipher.ENCRYPT_MODE, sk128, alg, iv1);

        SecretKey sk256 = new SecretKeySpec(sk2, "AES");
        testCipherInit(Cipher.ENCRYPT_MODE, sk256, alg, iv2);
    }

    public static Cipher testCipherInit(int encryptMode, SecretKey key, String algo, byte[] iv) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
        Cipher cipher = null;
        byte[] plaintext = "test".getBytes();
        try {
            cipher = Cipher.getInstance(algo);
            cipher.init(encryptMode, key, new IvParameterSpec(iv));
            System.out.println("Cipher initialized. " + algo);
            byte[] res = cipher.doFinal(plaintext);
            System.out.println("enc('test'): " + DatatypeConverter.printBase64Binary(res));
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException e) {
            System.out.println("Cipher can't be initialized! " + algo);
            System.out.println("Exception: " + e.getMessage());
        }
        return cipher;
    }
}

Output of java code:

Cipher initialized. AES/CBC/PKCS5Padding
enc('test'): G7pg1uF/q8i9saz2yW4ecA==
Cipher initialized. AES/CBC/PKCS5Padding
enc('test'): UBWxzyp6dzQeumNpGZudnA==

Verification with openssl:

echo -n "test" | openssl enc -aes-128-cbc -base64 -K "6162636465666768696a6b6c6d6e6f70" -iv "6162636465666768696a6b6c6d6e6f70" 
G7pg1uF/q8i9saz2yW4ecA==

echo -n "test" | openssl enc -aes-256-cbc -base64 -K "6162636465666768696a6b6c6d6e6f706162636465666768696a6b6c6d6e6f70" -iv "6162636465666768696a6b6c6d6e6f70" 
UBWxzyp6dzQeumNpGZudnA==

Then tested it on CentOS7 and Windows+Java8 IdM testing setup. Works fine everywhere.
Because the version of the cipher (128b vs. 256b) is determined by the size of secret key that is used, we should be able to use this in CzechIdM out of the box - just specify the 32B (=256b) key.

Need to test this with CzechIdM product.

Actions #6

Updated by Petr Fišer almost 4 years ago

Tested on Docker image (i.e. Linux installation) and 32B=256b key works fine.
Need to test on Windows but I don't expect problems.

Also noticed:
  • ChangeConfidentialStorageKeyTaskExecutor LRT mandates old key to be 16B (128b) long. We will need to change that validation to accept both 16B and 32B long keys.
  • The dialogue for ChangeConfidentialStorageKeyTaskExecutor configuration hides old key behind dots.
    • This is extremely uncomfortable because I cannot be sure I filled out the field correctly and there is no other way to confirm (like temporary unhide button or "write again").
    • It makes no sense because we are changing the key - that key was probably compromised and therefore is is already known and there is no need to hide it anymore.
    • If I configure the LRT and go to its detail, I can see the key written there in plaintext anyway.
Actions #7

Updated by Petr Fišer almost 4 years ago

Tested on Windows, it works too. However, on JDKs before 1.8u161 we need to add Unlimited Strength JCE Policy Files https://www.oracle.com/java/technologies/javase-jce8-downloads.html . Since those kinds of installation do not have policy files packed with IdM nor it is a Docker image, we are not effectively distributing them... so licensing is the same as for JDK for us and for the customer.

JDKs newer than 1.8u161 (inclusive) have strong ciphers enabled by default so we are good.
On older projects we can do any one of following three depending on the goal:
  • Upgrade JDK to 1.8u161 and later.
  • Leave the older version and Apply JCE Policy files. We should be upgrading regularly so this is not really an option.
  • Do not use AES with 256b key and stay on the 128b key. This is OK, depending on presumed threats. AES-128 is still considered secure.

Thinking about the JDK... I tested in in the container with latest JDK8. So this may affect linux installations as well.

Snippet from testing on Windows:

For JDK older than 1.8u161, we need unlimited strength policy files.
- Download https://www.oracle.com/java/technologies/javase-jce8-downloads.html
- install according to readme

Administrator@BAK MINGW64 /c/CzechIdM/etc xxd secret.key
00000000: 536f 6d65 5365 6372 6574 584f 584f 584f  SomeSecretXOXOXO
00000010: 536f 6d65 5365 6372 6574 584f 584f 584f  SomeSecretXOXOXO
00000020: 0a                                       .

2020-07-15 13:38:48.139  INFO 115094 --- [localhost-startStop-1] e.b.i.c.s.s.impl.DefaultCryptService.getKey : For crypt confidetial storage will be use key in file: [cipher.crypt.secret.keyPath].
2020-07-15 13:38:48.827  INFO 115782 --- [localhost-startStop-1] e.b.i.c.s.s.impl.DefaultCryptService.init : Initializing Cipher succeeded - Confidetial storage will be crypted.
2020-07-15 13:38:48.827  INFO 115782 --- [localhost-startStop-1] o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization : Bean 'cryptService' of type [class eu.bcvsolutions.idm.core.security.service.impl.DefaultCryptService] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

Actions #8

Updated by Petr Fišer almost 4 years ago

  • Status changed from New to Resolved

We will oficially have this in 10.6.0 including the LRT for changing confidential storage key.
Closing this ticket because it continues as #2391.

Actions #9

Updated by Petr Fišer almost 4 years ago

  • Status changed from Resolved to Closed
Actions #10

Updated by Ondřej Kopr over 3 years ago

  • Related to Task #2355: Confidential storage cipher uses hardcoded initialization vector added
Actions

Also available in: Atom PDF