Java: Patching client side security policy from ap

2019-01-24 17:06发布

问题:

I require AES256 encryption/decryption in a commercial web application. Currently everything is good with a key size of 128. This is not satisfactory cryptographically so my problem is how best to get round this issue without requiring the user to install anything manually.

I have the unlimited jurisdiction jar files from Oracle but I have no idea if replacing these in the user's JRE/lib/security directory will be compatible with older versions. Obviously I don't want to corrupt the user's JRE. Also I have write permission to my JRE security directory but I assume some user's will not have these privileges.

Is there a simple way around this issue, or am I stuck with either weak encryption or a potentially problematic step for users?


Update for "unrestricting" javax.crypto.JceSecurity

@ntoskml You are correct. getMaxAllowedKeyLength still returns the limited key size but the encryption succeeds with key size == 256 :). I will update my test method and set the key size if strong encryption is available. Thanks

>>> from javax.crypto import Cipher
>>> Cipher.getMaxAllowedKeyLength("AES")
128
>>> from java.lang import Class
>>> c = Class.forName("javax.crypto.JceSecurity")
>>> isRestricted = c.getDeclaredField("isRestricted")
>>> isRestricted.setAccessible(True)
>>> isRestricted.set(None, False)
>>> isRestricted.get(None)
False
>>> Cipher.getMaxAllowedKeyLength("AES")
128
>>> from javax.crypto import KeyGenerator
>>> kge = KeyGenerator.getInstance("AES")
>>> kge.init(256)
>>> aesKey = kgen.generateKey()
>>> c2 = Cipher.getInstance("AES")
>>> c2.init(Cipher.ENCRYPT_MODE, aesKey)
>>> c2.doFinal("test")
array('b', [-81, 99, -61, -51, 93, -42, -68, -28, 107, 59, -109, -98, -25, 127, 37, 23])

And the test case after restarting Jython console

>>> # Reflection as above
>>> isRestricted.get(None)
True
>>> kge.init(256)
>>> aesKey = kge.generateKey()
>>> c2.init(Cipher.ENCRYPT_MODE, aesKey)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
        at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1011)
        at javax.crypto.Cipher.implInit(Cipher.java:786)
        at javax.crypto.Cipher.chooseProvider(Cipher.java:849)
        at javax.crypto.Cipher.init(Cipher.java:1213)
        at javax.crypto.Cipher.init(Cipher.java:1153)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)

java.security.InvalidKeyException: java.security.InvalidKeyException: Illegal key size or default parameters

Bingo :) Thanks for sharing @ntoskml

回答1:

EDIT: Here's an updated answer to this question: How to avoid installing "Unlimited Strength" JCE policy files when deploying an application?


It is possible to disable the key size restrictions simply by using a few lines of reflection. We use this method in our program which needs access to 256-bit cryptography for interoperability purposes.

private static void removeCryptographyRestrictions() {
    if (!isRestrictedCryptography()) {
        return;
    }
    try {
        java.lang.reflect.Field isRestricted;
        try {
            final Class<?> c = Class.forName("javax.crypto.JceSecurity");
            isRestricted = c.getDeclaredField("isRestricted");
        } catch (final ClassNotFoundException e) {
            try {
                // Java 6 has obfuscated JCE classes
                final Class<?> c = Class.forName("javax.crypto.SunJCE_b");
                isRestricted = c.getDeclaredField("g");
            } catch (final ClassNotFoundException e2) {
                throw e;
            }
        }
        isRestricted.setAccessible(true);
        isRestricted.set(null, false);
    } catch (final Throwable e) {
        logger.log(Level.WARNING,
                "Failed to remove cryptography restrictions", e);
    }
}

private static boolean isRestrictedCryptography() {
    return "Java(TM) SE Runtime Environment"
            .equals(System.getProperty("java.runtime.name"));
}

However, our program is not an applet, and I am not sure whether applets have access to the reflection API.

The question about legality also remains. There is a reason for that limit. Consult a lawyer if you are concerned.

If possible, try to keep it to 128-bit keys. Even when taking Moore's law into consideration, breaking 128-bit AES would take billions upon billions of years. Longer keys offer no benefit in the real world – particularly when the keys are derived from passwords, which don't have anywhere near 256 bits of entropy anyway.



回答2:

You are either stuck with the weak encryption or a potentially problematic step for users if you stick to the SunJCE.

There is obviously no problem importing an AES library, there is just a problem using it using an instance of Cipher. If you have a specific piece of software that does not depend on JCA, you can for instance rewrite it to use the lightweight crypto API of Bouncy Castle.

Note that many other parts of the Bouncy API themselves depend on the JCE. The lightweight API is also trickier to use and less documented/tested than the SunJCE.

The Bouncy Castle lightweight API is pretty large as well. It contains a lot of functionality that you won't need. So it is probably too large for your applet. If it is I would advice you to create a new library that only contains the specific classes that you need from Bouncy Castle. The Bouncy Castle is fortunately very liberally licensed. As long as you keep the copyright statements etc. in place, you can easily split it off.