How to avoid installing "Unlimited Strength" JCE policy files when deploying an application?
Asked Answered
S

11

174

I have an app that uses 256-bit AES encryption which is not supported by Java out of the box. I know to get this to function correctly I install the JCE unlimited strength jars in the security folder. This is fine for me being the developer, I can install them.

My question is since this app will be distributed, end users most likely will not have these policy files installed. Having the end user download these just to make the app function is not an attractive solution.

Is there a way to make my app run without overwriting files on the end user machine? A third party software that can handle it without the policy files installed? Or a way to just reference these policy files from within a JAR?

Salesin answered 24/7, 2009 at 19:27 Comment(3)
Take a look here: docs.oracle.com/javase/1.5.0/docs/guide/security/jce/…Grass
Refer to How to install Java Cryptography Extension unlimited strength jurisdiction policy filesSeem
I suspect Sun/Oracle intention was that the client would use a less-safe cipher so that the NSA can snoop on the connection . I'm not joking or being paranoid, but cryptography is treated as a weapon and there are export bans on sharing encryption.Bonnet
P
176

There are a couple of commonly quoted solutions to this problem. Unfortunately neither of these are entirely satisfactory:

  • Install the unlimited strength policy files. While this is probably the right solution for your development workstation, it quickly becomes a major hassle (if not a roadblock) to have non-technical users install the files on every computer. There is no way to distribute the files with your program; they must be installed in the JRE directory (which may even be read-only due to permissions).
  • Skip the JCE API and use another cryptography library such as Bouncy Castle. This approach requires an extra 1MB library, which may be a significant burden depending on the application. It also feels silly to duplicate functionality included in the standard libraries. Obviously, the API is also completely different from the usual JCE interface. (BC does implement a JCE provider, but that doesn't help because the key strength restrictions are applied before handing over to the implementation.) This solution also won't let you use 256-bit TLS (SSL) cipher suites, because the standard TLS libraries call the JCE internally to determine any restrictions.

But then there's reflection. Is there anything you can't do using reflection?

private static void removeCryptographyRestrictions() {
    if (!isRestrictedCryptography()) {
        logger.fine("Cryptography restrictions removal not needed");
        return;
    }
    try {
        /*
         * Do the following, but with reflection to bypass access checks:
         *
         * JceSecurity.isRestricted = false;
         * JceSecurity.defaultPolicy.perms.clear();
         * JceSecurity.defaultPolicy.add(CryptoAllPermission.INSTANCE);
         */
        final Class<?> jceSecurity = Class.forName("javax.crypto.JceSecurity");
        final Class<?> cryptoPermissions = Class.forName("javax.crypto.CryptoPermissions");
        final Class<?> cryptoAllPermission = Class.forName("javax.crypto.CryptoAllPermission");

        final Field isRestrictedField = jceSecurity.getDeclaredField("isRestricted");
        isRestrictedField.setAccessible(true);
        final Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(isRestrictedField, isRestrictedField.getModifiers() & ~Modifier.FINAL);
        isRestrictedField.set(null, false);

        final Field defaultPolicyField = jceSecurity.getDeclaredField("defaultPolicy");
        defaultPolicyField.setAccessible(true);
        final PermissionCollection defaultPolicy = (PermissionCollection) defaultPolicyField.get(null);

        final Field perms = cryptoPermissions.getDeclaredField("perms");
        perms.setAccessible(true);
        ((Map<?, ?>) perms.get(defaultPolicy)).clear();

        final Field instance = cryptoAllPermission.getDeclaredField("INSTANCE");
        instance.setAccessible(true);
        defaultPolicy.add((Permission) instance.get(null));

        logger.fine("Successfully removed cryptography restrictions");
    } catch (final Exception e) {
        logger.log(Level.WARNING, "Failed to remove cryptography restrictions", e);
    }
}

private static boolean isRestrictedCryptography() {
    // This matches Oracle Java 7 and 8, but not Java 9 or OpenJDK.
    final String name = System.getProperty("java.runtime.name");
    final String ver = System.getProperty("java.version");
    return name != null && name.equals("Java(TM) SE Runtime Environment")
            && ver != null && (ver.startsWith("1.7") || ver.startsWith("1.8"));
}

Simply call removeCryptographyRestrictions() from a static initializer or such before performing any cryptographic operations.

The JceSecurity.isRestricted = false part is all that is needed to use 256-bit ciphers directly; however, without the two other operations, Cipher.getMaxAllowedKeyLength() will still keep reporting 128, and 256-bit TLS cipher suites won't work.

This code works on Oracle Java 7 and 8, and automatically skips the process on Java 9 and OpenJDK where it's not needed. Being an ugly hack after all, it likely doesn't work on other vendors' VMs.

It also doesn't work on Oracle Java 6, because the private JCE classes are obfuscated there. The obfuscation does not change from version to version though, so it is still technically possible to support Java 6.

Psycholinguistics answered 18/3, 2014 at 22:42 Comment(9)
The reflection solution may violate the Java License Agreement: "F. JAVA TECHNOLOGY RESTRICTIONS. You may not ... change the behavior of ... classes, interfaces, or subpackages that are in any way identified as 'java', 'javax', 'sun', 'oracle' or similar convention ..."Bula
@M.Dudley Could be. Check with a lawyer before shipping a product that contains this piece of code if it concerns you.Psycholinguistics
"There is no way to distribute the files with your program; they must be installed in the JRE directory" -- I'm not an expert, but it's my understanding it is legal to distribute/bundle a jre with your program, including the JCE policy files provided you adhere to the licensing terms.Trypanosomiasis
@Trypanosomiasis Including a 100MB JRE with your program is certainly an option in some cases. But if not, the users will still have to install the policy files manually, even if you include them with your program (because of various reasons like file permissions). In my experience, many users are not capable of that.Psycholinguistics
I might be running into same scenario --> #30792656 just to test removing that restriction still didn't attempt to connect over anything less than TLSv1.2Eonian
I get this error after running this: java.security.InvalidKeyException: Wrong algorithm: AES or Rijndael requiredRevolt
It seems like the reflection solution just stopped working in 1.8.0_112. It works in 1.8.0_111, but not 112.Curve
@JohnL I use this in an application. After running into trouble with the final field in 8u111, I modified it so it can change the final field, following this answer. The result is about the same as ntoskrnl's new version, except that I didn't declare modifiersField as final. One of my users reports that it works in 8u112 as well.Vernettaverneuil
This is all related to an arcane WWII law citing export (which includes posting on the Internet) of strong cryptography implementations - which the US government likened to exporting munitions (arms/weapons). Phil Zimmerman of PGP was prosecuted along these lines - hence the reason Oracle has not until very recently offered strong crypto libraries as part of the standard Java download. en.wikipedia.org/wiki/…Peru
I
90

This is now no longer needed for Java 9, nor for any recent release of Java 6, 7, or 8. Finally! :)

Per JDK-8170157, the unlimited cryptographic policy is now enabled by default.

Specific versions from the JIRA issue:

  • Java 9 (10, 11, etc..): Any official release!
  • Java 8u161 or later (Available now)
  • Java 7u171 or later (Only available through 'My Oracle Support')
  • Java 6u181 or later (Only available through 'My Oracle Support')

Note that if for some odd reason the old behavior is needed in Java 9, it can be set using:

Security.setProperty("crypto.policy", "limited");
Inhabitant answered 6/10, 2016 at 7:13 Comment(5)
In fact, this policy is the default, so no actions necessary in Java 9!Psycholinguistics
As of 2018/01/14 (latest Oracle JDK is 8u151/152) this is still not enabled by default on Java 8, well over a year after this answer was originally written... However according to java.com/en/jre-jdk-cryptoroadmap.html this is intended to GA on 2018/01/16Baroque
In my case, and for me to get a Mark of A in this site: ssllabs.com/ssltest... I have to set it this way: Security.setProperty("crypto.policy", "unlimited"); then... set server.ssl.ciphers in my applications.properties with 256-based algorithms indicated in this article --> weakdh.org/sysadmin.htmlDermatoglyphics
Also relevant for OpenJDK 8-Installations. See: stackoverlow-Article: Is JCE policy bundled with openjdk 8?Bertsche
Hope it is not needed in Java 11 too ?Aldridge
V
22

Here is solution: http://middlesphere-1.blogspot.ru/2014/06/this-code-allows-to-break-limit-if.html

//this code allows to break limit if client jdk/jre has no unlimited policy files for JCE.
//it should be run once. So this static section is always execute during the class loading process.
//this code is useful when working with Bouncycastle library.
static {
    try {
        Field field = Class.forName("javax.crypto.JceSecurity").getDeclaredField("isRestricted");
        field.setAccessible(true);
        field.set(null, java.lang.Boolean.FALSE);
    } catch (Exception ex) {
    }
}
Villiers answered 25/1, 2015 at 11:57 Comment(4)
This is the same solution as mine, except without the "defaultPolicy" part. The blog post is dated after my answer.Psycholinguistics
But is this right thing to do? In real time can this code challenge the application security? I am not sure please help me understand it's impact.Curch
I get this error after running this: java.security.InvalidKeyException: Wrong algorithm: AES or Rijndael requiredRevolt
As of Java 8 build 111, this solution will be insufficient, as the isRestricted field has become final (bugs.openjdk.java.net/browse/JDK-8149417). @ntoskrnl's answer takes care of any possible inclusion of a "final" modifier. @M.Dudley's comment on the Java Licence Agreement still applies too.Ellynellynn
H
14

As of JDK 8u102, the posted solutions relying on reflection will no longer work: the field that these solutions set is now final (https://bugs.openjdk.java.net/browse/JDK-8149417).

Looks like it's back to either (a) using Bouncy Castle, or (b) installing the JCE policy files.

Hepplewhite answered 28/7, 2016 at 1:41 Comment(3)
You could always use more reflection #3302135Warehouse
Yes, @M.Dudley's solution will still work for the isRestricted field, because it takes care of a possible addition of a "final" modifier.Ellynellynn
New release JDK 8u151 has "New Security property to control crypto policy". Bottom line: remove the "#" from the line "#crypto.policy=unlimited" in "lib\security\java.security": oracle.com/technetwork/java/javase/8u151-relnotes-3850493.htmlExcess
D
13

Bouncy Castle still requires jars installed as far as I can tell.

I did a little test and it seemed to confirm this:

http://www.bouncycastle.org/wiki/display/JA1/Frequently+Asked+Questions

Dela answered 8/5, 2011 at 17:56 Comment(0)
P
10

For an alternative cryptography library, have a look at Bouncy Castle. It has AES and a lot of added functionality. It's a liberal open source library. You will have to use the lightweight, proprietary Bouncy Castle API for this to work though.

Pukka answered 5/8, 2009 at 12:42 Comment(2)
They are a great crypto provider, but still require the unlimited strength JCE file in order to work with large keys.Pryer
If you use the Bouncy Castle API directly you don't need the unlimited strength files.Fremont
S
5

You could use method

javax.crypto.Cipher.getMaxAllowedKeyLength(String transformation)

to test the available key length, use that and inform the user about what is going on. Something stating that your application is falling back to 128 bit keys due to the policy files not being installed, for example. Security conscious users will install the policy files, others will continue using weaker keys.

Shelly answered 26/7, 2015 at 10:11 Comment(0)
W
3

For our application, we had a client server architecture and we only allowed decrypting/encrypting data in the server level. Hence the JCE files are only needed there.

We had another problem where we needed to update a security jar on the client machines, through JNLP, it overwrites the libraries in${java.home}/lib/security/ and the JVM on first run.

That made it work.

Wire answered 24/7, 2009 at 22:29 Comment(0)
T
3

Here's a updated version of ntoskrnl answer. It additionally contains a function to remove the final modifier like Arjan mentioned in the comments.

This version works with JRE 8u111 or newer.

private static void removeCryptographyRestrictions() {
    if (!isRestrictedCryptography()) {
        return;
    }
    try {
        /*
         * Do the following, but with reflection to bypass access checks:
         * 
         * JceSecurity.isRestricted = false; JceSecurity.defaultPolicy.perms.clear();
         * JceSecurity.defaultPolicy.add(CryptoAllPermission.INSTANCE);
         */
        final Class<?> jceSecurity = Class.forName("javax.crypto.JceSecurity");
        final Class<?> cryptoPermissions = Class.forName("javax.crypto.CryptoPermissions");
        final Class<?> cryptoAllPermission = Class.forName("javax.crypto.CryptoAllPermission");

        Field isRestrictedField = jceSecurity.getDeclaredField("isRestricted");
        isRestrictedField.setAccessible(true);
        setFinalStatic(isRestrictedField, true);
        isRestrictedField.set(null, false);

        final Field defaultPolicyField = jceSecurity.getDeclaredField("defaultPolicy");
        defaultPolicyField.setAccessible(true);
        final PermissionCollection defaultPolicy = (PermissionCollection) defaultPolicyField.get(null);

        final Field perms = cryptoPermissions.getDeclaredField("perms");
        perms.setAccessible(true);
        ((Map<?, ?>) perms.get(defaultPolicy)).clear();

        final Field instance = cryptoAllPermission.getDeclaredField("INSTANCE");
        instance.setAccessible(true);
        defaultPolicy.add((Permission) instance.get(null));
    }
    catch (final Exception e) {
        e.printStackTrace();
    }
}

static void setFinalStatic(Field field, Object newValue) throws Exception {
      field.setAccessible(true);

      Field modifiersField = Field.class.getDeclaredField("modifiers");
      modifiersField.setAccessible(true);
      modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);

      field.set(null, newValue);
   }

private static boolean isRestrictedCryptography() {
    // This simply matches the Oracle JRE, but not OpenJDK.
    return "Java(TM) SE Runtime Environment".equals(System.getProperty("java.runtime.name"));
}
Tacnaarica answered 18/5, 2017 at 19:18 Comment(1)
It works nicely, but the line ((Map<?, ?>) perms.get(defaultPolicy)).clear(); yields a compiler error. Commenting out does not seem to impact its functionality. Is this line necessary?Pulley
W
2

Here is a modified version of @ntoskrnl's code featuring isRestrictedCryptography check by actual Cipher.getMaxAllowedKeyLength, slf4j logging and support of singleton initialization from application bootstrap like this:

static {
    UnlimitedKeyStrengthJurisdictionPolicy.ensure();
}

This code would correctly stop mangling with reflection when unlimited policy becomes available by default in Java 8u162 as @cranphin's answer predicts.


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.security.NoSuchAlgorithmException;
import java.security.Permission;
import java.security.PermissionCollection;
import java.util.Map;

// https://mcmap.net/q/24705/-how-to-avoid-installing-quot-unlimited-strength-quot-jce-policy-files-when-deploying-an-application
public class UnlimitedKeyStrengthJurisdictionPolicy {

    private static final Logger log = LoggerFactory.getLogger(UnlimitedKeyStrengthJurisdictionPolicy.class);

    private static boolean isRestrictedCryptography() throws NoSuchAlgorithmException {
        return Cipher.getMaxAllowedKeyLength("AES/ECB/NoPadding") <= 128;
    }

    private static void removeCryptographyRestrictions() {
        try {
            if (!isRestrictedCryptography()) {
                log.debug("Cryptography restrictions removal not needed");
                return;
            }
            /*
             * Do the following, but with reflection to bypass access checks:
             *
             * JceSecurity.isRestricted = false;
             * JceSecurity.defaultPolicy.perms.clear();
             * JceSecurity.defaultPolicy.add(CryptoAllPermission.INSTANCE);
             */
            Class<?> jceSecurity = Class.forName("javax.crypto.JceSecurity");
            Class<?> cryptoPermissions = Class.forName("javax.crypto.CryptoPermissions");
            Class<?> cryptoAllPermission = Class.forName("javax.crypto.CryptoAllPermission");

            Field isRestrictedField = jceSecurity.getDeclaredField("isRestricted");
            isRestrictedField.setAccessible(true);
            Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);
            modifiersField.setInt(isRestrictedField, isRestrictedField.getModifiers() & ~Modifier.FINAL);
            isRestrictedField.set(null, false);

            Field defaultPolicyField = jceSecurity.getDeclaredField("defaultPolicy");
            defaultPolicyField.setAccessible(true);
            PermissionCollection defaultPolicy = (PermissionCollection) defaultPolicyField.get(null);

            Field perms = cryptoPermissions.getDeclaredField("perms");
            perms.setAccessible(true);
            ((Map<?, ?>) perms.get(defaultPolicy)).clear();

            Field instance = cryptoAllPermission.getDeclaredField("INSTANCE");
            instance.setAccessible(true);
            defaultPolicy.add((Permission) instance.get(null));

            log.info("Successfully removed cryptography restrictions");
        } catch (Exception e) {
            log.warn("Failed to remove cryptography restrictions", e);
        }
    }

    static {
        removeCryptographyRestrictions();
    }

    public static void ensure() {
        // just force loading of this class
    }
}
Wirewove answered 16/10, 2017 at 17:21 Comment(0)
B
-1

During installation of your program, just prompt the user and have a DOS Batch script or a Bash shell script download and copy the JCE into the proper system location.

I used to have to do this for a server webservice and instead of a formal installer, I just provided scripts to setup the app before the user could run it. You can make the app un-runnable until they run the setup script. You could also make the app complain that the JCE is missing and then ask to download and restart the app?

Boyceboycey answered 24/7, 2009 at 20:9 Comment(2)
"make my app run without overwriting files on the end user machine"Nursemaid
I did a complete edit of my answer since my initial answer was a wrong one.Boyceboycey

© 2022 - 2024 — McMap. All rights reserved.