`java.security.InvalidKeyException`仅在旧版本的Java中在AES解密过程中抛出。

huangapple go评论78阅读模式
英文:

java.security.InvalidKeyException thrown only for older java versions during AES decryption

问题

以下是您要翻译的内容:

我的目标是加密缓存资产(在本例中为使用自定义格式的3D模型),这些资产由分发给用户的客户端进行解密。为此,我使用了以下实现:

public class AESTestStackoverflow {
    
    private final static byte[] secretKey = new byte[]{-74, 80, 22, 62, -70, -117, 22, 110, 57, -51, 2, 70, 68, -29, 14, -100, -24, 121, -122, 81, 5, 23, -90, 78, -99, -116, 29, -38, 118, 121, 126, 51};
    SecretKeySpec secretKeySpec;

    @Before
    public void setUp() {
        secretKeySpec = new SecretKeySpec(secretKey, "AES");
    }

    @Test
    public void testDecryption() {
        final byte[] raw = new byte[]{1,2,3,4,5,6,7,8,9,10};
        final EncryptionResult result = AES.encrypt(raw, secretKeySpec);

        Assert.assertNotNull(result);

        final byte[] encrypted = result.getData();
        final byte[] iv = result.getIv();
        final byte[] decrypted = AES.decrypt(encrypted, iv, secretKeySpec);

        Assert.assertArrayEquals(raw, decrypted);
    }
}

这个实现在最新的Java版本中运行得很好,但是在Java版本等于或低于 1.8.131 时失败(不确定在哪个确切的版本上出现问题,但是在 1.8.2xx 范围内的最新Java版本似乎正常工作)。运行这些Java版本的用户会得到以下异常:

java.security.InvalidKeyException: No installed provider supports this key: javax.crypto.spec.SecretKeySpec
    at javax.crypto.Cipher.chooseProvider(Cipher.java:893)
    at javax.crypto.Cipher.init(Cipher.java:1396)
    at javax.crypto.Cipher.init(Cipher.java:1327)

我确实对这个错误进行了一些研究,我找到了几篇帖子指出这可能与JCE库的某些部分未随JRE一起提供有关,提供的解决方案是手动将它们安装到您的JRE中。

我的经理不想强迫用户更新他们的Java版本,显然我们也不能要求用户手动修改他们的JRE,所以我有点不知所措该如何继续下去。我尝试过做更多的研究,但我对安全库或任何相关的JVM设置并不熟悉。如果有人能够阐明为什么会出现这种情况,以及是否可以使其与旧的Java版本(从1.8.xxx版本开始)兼容,我将不胜感激。

英文:

My goal is to encrypt cache assets (in this case 3D models using a custom format) that are being decrypted by a client distributed to users. For this I used the following implementation:

public class AESTestStackoverflow {
    
    private final static byte[] secretKey = new byte[]{-74, 80, 22, 62, -70, -117, 22, 110, 57, -51, 2, 70, 68, -29, 14, -100, -24, 121, -122, 81, 5, 23, -90, 78, -99, -116, 29, -38, 118, 121, 126, 51};
    SecretKeySpec secretKeySpec;

    @Before
    public void setUp() {
        secretKeySpec = new SecretKeySpec(secretKey, "AES");
    }

    @Test
    public void testDecryption() {
        final byte[] raw = new byte[]{1,2,3,4,5,6,7,8,9,10};
        final EncryptionResult result = AES.encrypt(raw, secretKeySpec);

        Assert.assertNotNull(result);

        final byte[] encrypted = result.getData();
        final byte[] iv = result.getIv();
        final byte[] decrypted = AES.decrypt(encrypted, iv, secretKeySpec);

        Assert.assertArrayEquals(raw, decrypted);
    }
}

This implementation works perfectly fine for the latest java versions but fails for java versions equal to or below 1.8.131 (not sure on the exact version that it breaks, but the latest java versions in the 1.8.2xx range appear to work fine). Users running these java versions get the following exception:

java.security.InvalidKeyException: No installed provider supports this key: javax.crypto.spec.SecretKeySpec
	at javax.crypto.Cipher.chooseProvider(Cipher.java:893)
	at javax.crypto.Cipher.init(Cipher.java:1396)
	at javax.crypto.Cipher.init(Cipher.java:1327)

I did do some research on this error and I found several posts stating this might have to do with some part of the JCE library not being shipped with the JRE, and the provided solution was to manually install these into your JRE.

My manager does not want to force users to update their java version, and obviously we cannot ask users to modify their JRE manually, so I am at a bit of a loss on how to continue. I have tried doing some more research but I am not well versed in the security libraries or any related JVM settings. I would appreciate it if someone can shed some light on why this happens, and if it can be made compatible for older java version (from 1.8.xxx onwards).

答案1

得分: 2

这是您提供的翻译内容:

我没有测试过你的代码,但是我的简单程序将测试所谓的“无限加密策略”。由于美国的出口限制,美国公司不被允许使用无限密钥格式来运送他们的程序。您正在使用32字节长的AES密钥,因此被称为AES-256(32字节 * 8位 = 256位),但政府只允许使用16字节长的密钥(“AES 128”)。

我的程序根据使用的Java版本运行测试。为了进行更简单的测试,我设置了一个JFiddle链接,您可以直接在其中测试该程序,并且可以更改Java版本以方便测试。JFiddle可以通过此链接访问:https://www.jdoodle.com/a/2nZe

在运行测试时,Java版本是JDK 11.0.4,但您可以更改(请向下滚动)为JDK 1.8.0_66。

Java 11的输出:

检查无限加密策略
受限密码学:false 注意:“false”表示无限策略
安全属性:无限
最大AES密钥长度 = 2147483647

Java 1.8.0_66的输出:

受限密码学:true 注意:“false”表示无限策略
安全属性:null
最大AES密钥长度 = 128

为了在像1.8.0_66这样的较旧的Java版本中启用无限策略,您的客户必须从Oracle的页面加载两个小文件,并将它们放在他们的Java虚拟机中。

正如您所述,您的经理不允许这样做,只剩下一种方法 - 您必须使用(仅限)16字节长的AES密钥。

安全警告:由于即将出现的量子计算机,实际建议使用32字节长的AES密钥

代码:

import javax.crypto.Cipher;
import java.security.NoSuchAlgorithmException;
import java.security.Security;

public class UnlimitedCryptoPoliciesCheck {
    public static void main(String[] args) throws NoSuchAlgorithmException {
        System.out.println("检查无限加密策略");
        //Security.setProperty("crypto.policy", "limited"); // 必须在最开始设置!
        System.out.println("受限密码学:" + restrictedCryptography() + " 注意:“false”表示无限策略"); // false表示无限加密
        System.out.println("安全属性:" + Security.getProperty("crypto.policy"));
        int maxKeyLen = Cipher.getMaxAllowedKeyLength("AES");
        System.out.println("最大AES密钥长度 = " + maxKeyLen);
  }

    /**
     * 确定是否应用了密码学限制。
     * 如果{@link Cipher#getMaxAllowedKeyLength(String)}的值小于{@link Integer#MAX_VALUE},则应用限制,如果该方法的JavaDoc中有任何限制。
     * 此方法与transform <code>"AES/CBC/PKCS5Padding"</code>一起使用,因为这是一个经常使用的算法,它是Java SE的实现要求之一。
     *
     * @return <code>true</code>表示应用了限制,否则为<code>false</code>
     * https://stackoverflow.com/posts/33849265/edit,作者Maarten Bodewes
     */
    public static boolean restrictedCryptography() {
        try {
            return Cipher.getMaxAllowedKeyLength("AES/CBC/PKCS5Padding") < Integer.MAX_VALUE;
        } catch (final NoSuchAlgorithmException e) {
            throw new IllegalStateException("不可用的转换\"AES/CBC/PKCS5Padding\"(此算法的可用性对于Java SE实现是强制性的)", e);
        }
    }
}
英文:

I didn't test you your code but my simple program will test for something that is called "unlimited crypto policies". Due to US export restrictions
the US companies were not allowed to ship their programs with an unlimited key format. You are using AES with a 32 bytes long key so it is called
AES-256 (32 bytes * 8 bit = 256) but the government allowed only 16 bytes long keys ("AES 128").

My program runs the test depending on the Java version that is in use. For an easier test I setup a JFiddle-link where you can test the program directly
and for convinience change the Java version. The JFiddle is available under this link: https://www.jdoodle.com/a/2nZe

When running the test the Java version is JDK 11.0.4 but you can change (please scroll down) to JDK 1.8.0_66.

output for Java 11:

Check for unlimited crypto policies
restricted cryptography: false Notice: &#39;false&#39; means unlimited policies
Security properties: unlimited
Max AES key length = 2147483647

output for Java 1.8.0_66:

restricted cryptography: true Notice: &#39;false&#39; means unlimited policies
Security properties: null
Max AES key length = 128

To enable the unlimited policy for older Java's like 1.8.0_66 your clients have to load two small files from Oracle's pages and place them in their Java VM.

As you stated your manager does not allow this there is only one way left - you have to use (only) 16 byte long AES keys.

Security warning: due to upcoming Quantum computers the actual recommendation for AES keys is 32 byte long keys.

code:

import javax.crypto.Cipher;
import java.security.NoSuchAlgorithmException;
import java.security.Security;

public class UnlimitedCryptoPoliciesCheck {
    public static void main(String[] args) throws NoSuchAlgorithmException {
        System.out.println(&quot;Check for unlimited crypto policies&quot;);
        //Security.setProperty(&quot;crypto.policy&quot;, &quot;limited&quot;); // muss ganz am anfang gesetzt werden !
        System.out.println(&quot;restricted cryptography: &quot; + restrictedCryptography() + &quot; Notice: &#39;false&#39; means unlimited policies&quot;); // false mean unlimited crypto
        System.out.println(&quot;Security properties: &quot; + Security.getProperty(&quot;crypto.policy&quot;));
        int maxKeyLen = Cipher.getMaxAllowedKeyLength(&quot;AES&quot;);
        System.out.println(&quot;Max AES key length = &quot; + maxKeyLen);
  }

    /**
     * Determines if cryptography restrictions apply.
     * Restrictions apply if the value of {@link Cipher#getMaxAllowedKeyLength(String)} returns a value smaller than {@link Integer#MAX_VALUE} if there are any restrictions according to the JavaDoc of the method.
     * This method is used with the transform &lt;code&gt;&quot;AES/CBC/PKCS5Padding&quot;&lt;/code&gt; as this is an often used algorithm that is &lt;a href=&quot;https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#impl&quot;&gt;an implementation requirement for Java SE&lt;/a&gt;.
     *
     * @return &lt;code&gt;true&lt;/code&gt; if restrictions apply, &lt;code&gt;false&lt;/code&gt; otherwise
     * https://stackoverflow.com/posts/33849265/edit, author Maarten Bodewes
     */
    public static boolean restrictedCryptography() {
        try {
            return Cipher.getMaxAllowedKeyLength(&quot;AES/CBC/PKCS5Padding&quot;) &lt; Integer.MAX_VALUE;
        } catch (final NoSuchAlgorithmException e) {
            throw new IllegalStateException(&quot;The transform \&quot;AES/CBC/PKCS5Padding\&quot; is not available (the availability of this algorithm is mandatory for Java SE implementations)&quot;, e);
        }
    }
}

huangapple
  • 本文由 发表于 2020年10月1日 18:23:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/64153440.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定