仅在AES解密期间为旧java版本引发java.security.InvalidKeyException

仅在AES解密期间为旧java版本引发java.security.InvalidKeyException,java,encryption,client,version,Java,Encryption,Client,Version,我的目标是加密由分发给用户的客户端解密的缓存资产(在本例中是使用自定义格式的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

我的目标是加密由分发给用户的客户端解密的缓存资产(在本例中是使用自定义格式的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版本,此实现可以很好地工作,但对于等于或低于1.8.131的java版本,此实现失败(不确定它是否会在确切的版本上中断,但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)
我对这个错误做了一些研究,发现有几篇文章指出这可能与JRE没有附带JCE库的某些部分有关,提供的解决方案是手动将这些安装到JRE中


我的经理不想强迫用户更新他们的java版本,显然我们不能要求用户手动修改他们的JRE,所以我对如何继续感到有点不知所措。我试着做了更多的研究,但我不太熟悉安全库或任何相关的JVM设置。如果有人能解释一下为什么会发生这种情况,以及它是否能与旧的java版本(从1.8.xxx开始)兼容,我将不胜感激。

我没有测试你的代码,但我的简单程序将测试一种叫做“无限加密策略”的东西。由于美国的出口限制 美国公司不允许以不受限制的密钥格式发布其程序。您使用的AES具有32字节长的密钥,因此称为 AES-256(32字节*8位=256),但政府只允许16字节长的密钥(“AES 128”)

我的程序根据正在使用的Java版本运行测试。为了便于测试,我设置了一个JFiddle链接,您可以直接测试程序 为方便起见,请更改Java版本。JFIDLE可通过以下链接获得:

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

Java 11的输出:

Check for unlimited crypto policies
restricted cryptography: false Notice: 'false' means unlimited policies
Security properties: unlimited
Max AES key length = 2147483647
Java 1.8.0_66的输出:

restricted cryptography: true Notice: 'false' means unlimited policies
Security properties: null
Max AES key length = 128
要为旧版Java(如1.8.066)启用无限制策略,您的客户机必须从Oracle的页面加载两个小文件,并将它们放在Java虚拟机中

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

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

代码:


@谢谢你的推荐,虽然我的问题已经得到了回答,但我想知道你是否可以对我的补充问题提供更多的见解;我把它贴在了接受答案的评论中。再次感谢:)出口管制问题是复杂的,有争议的,而且与stackoverflow的主题非常脱节。很抱歉。非常感谢,这拼凑了我找到的很多信息,但未能完全理解。出于兴趣,美国为什么要对此设置限制,只是为了在需要时破解密码?他们是否也停止在更高版本的java中使用此策略,如果没有,为什么它在更高版本的java中工作?谢谢,我没有看到任何官方信息,为什么美国政府取消了限制,但从Java8Build154(可能稍早或稍晚)开始,Oracle部署了具有无限策略的Java。也许我们可以从这里的一些加密专家那里得到一些更深入的信息,所以:-)
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("Check for unlimited crypto policies");
        //Security.setProperty("crypto.policy", "limited"); // muss ganz am anfang gesetzt werden !
        System.out.println("restricted cryptography: " + restrictedCryptography() + " Notice: 'false' means unlimited policies"); // false mean unlimited crypto
        System.out.println("Security properties: " + Security.getProperty("crypto.policy"));
        int maxKeyLen = Cipher.getMaxAllowedKeyLength("AES");
        System.out.println("Max AES key length = " + 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 <code>"AES/CBC/PKCS5Padding"</code> as this is an often used algorithm that is <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#impl">an implementation requirement for Java SE</a>.
     *
     * @return <code>true</code> if restrictions apply, <code>false</code> otherwise
     * https://stackoverflow.com/posts/33849265/edit, author Maarten Bodewes
     */
    public static boolean restrictedCryptography() {
        try {
            return Cipher.getMaxAllowedKeyLength("AES/CBC/PKCS5Padding") < Integer.MAX_VALUE;
        } catch (final NoSuchAlgorithmException e) {
            throw new IllegalStateException("The transform \"AES/CBC/PKCS5Padding\" is not available (the availability of this algorithm is mandatory for Java SE implementations)", e);
        }
    }
}