检查是否可用无限制的加密技术

49

我怎样在Java代码中检查当前JVM是否具备无限制加密强度?


1
一个不错的方法:https://dev59.com/v2gu5IYBdhLWcg3wJT5I - Chris
请注意,自Java 8u152起,Ultimate将成为干净安装的默认选项。请参阅https://www.oracle.com/technetwork/java/javase/8u152-relnotes-3850503.html。 - Haim Raman
9个回答

48
与 Dan Cruz 的答案相同,但只需一行代码且无需经过异常处理:
boolean limit = Cipher.getMaxAllowedKeyLength("RC5")<256;

因此,一个完整的程序可能是:

import javax.crypto.Cipher;

public class TestUCE {
  public static void main(String args[]) throws Exception {
    boolean unlimited =
      Cipher.getMaxAllowedKeyLength("RC5") >= 256;
    System.out.println("Unlimited cryptography enabled: " + unlimited);
  }
}

测试过了,这个可以工作,分别返回“false”和“true”。谢谢。 - eis
3
在我看来,这个答案比被采纳的答案更好。 - Y123
2
这个答案使用了“RC5”变换,这是一种相当晦涩的流密码,不在所需算法列表中,它检查了256(可能在未来受限制的JRE中可用),并且也不能很好地处理异常。请看一下我的答案,解决了这个问题。 - Maarten Bodewes

45

如果您正在使用Linux,并且已安装了JDK(但Beanshell不可用),则可以使用JDK提供的runscript命令进行检查。

jrunscript -e 'exit (javax.crypto.Cipher.getMaxAllowedKeyLength("RC5") >= 256 ? 0 : 1);'; echo $?

如果无限密码可用,则返回0状态代码;如果不可用,则返回1。对于shell函数来说,零是正确的“成功”返回值,非零表示失败。


只是提醒一下,jrunscript已经包含在Oracle JDK中了,所以你可能需要去寻找它。在RedHat/CentOS/Fedora系统上,你可以执行“rpm -ql jdk | grep jrunscript”来查找它。 - slm
1
我正在尝试在Windows上运行这个程序。我需要使用特殊的转义字符吗? - user1207381
这在 macOS 上运行得非常好。在 PowerShell 下也应该能很好地工作。 - Ryan J. McDonough
2
Windows版本:jrunscript -e“exit(println(javax.crypto.Cipher.getMaxAllowedKeyLength(\”RC5\“)> = 256);)” - chance

25

我认为你可以使用Cipher.getMaxAllowedKeyLength(),同时将你正在使用的密码与已知的“好”的安全密码列表进行比较,例如AES。

这篇文章列出了截至Java 1.4时当前的最大密钥大小管辖限制(除非法律也发生了变化,否则这些可能没有改变-请参见下文)。

如果您所在的国家存在加密出口/进口限制,则必须查阅贵国的法律,但在这种情况下,您可能无法在JVM中默认使用无限强度加密。换句话说,假设您正在使用Oracle公司的官方JVM,并且您恰好居住在美国对其实施了加密出口限制的国家(由于Oracle是一家美国公司,因此会受到这些限制),那么在这种情况下,您还可以假设您没有无限强度可用。

当然,这并不妨碍您自己构建,从而授予自己无限的强度,但根据您所在地区的法律,这可能是非法的。

这篇文章概述了美国对其他国家加密出口的限制。


1
我觉得从Java中检查我是否在受美国出口法规限制的机器上运行相当困难... - Chi-Lan
是的,我想这方面可能没有API可用。我认为丹·克鲁兹的答案可能是你能得到的最接近的答案了。 - jefflunt
不,实际上“getMaxAllowedKeyLength”听起来更好。 - Chi-Lan

19

如何检查限制是否适用已在方法Cipher.getMaxAllowedKeyLength中记录:

如果安装了JCE无限制强度管辖策略文件,则将返回Integer.MAX_VALUE

这意味着,如果返回任何小于(或低于)Integer.MAX_VALUE的值,则适用限制。

更多信息请参见下面方法的JavaDoc:

/**
 * 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
 */
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);
    }
}
请注意,自Java 9以来,默认安装无限制的加密策略(那些受进出口管制影响的人需要安装有限的加密策略)。因此,这段代码主要是为了向后兼容和/或其他运行时所需。

添加了这个额外的答案,因为被接受的答案不是非常明确,而且目前得到最多赞的答案使用了RC5,这是一个相当晦涩的流密码,不在所需算法列表中,它检查了256(可能在将来的受限制的 JRE 中可用),而且也不能很好地处理异常。 - Maarten Bodewes
1
请注意,您可以使用 getMaxAllowedKeyLength("foo") 并仍然得到正确的答案。我发现只要参数不是 null"",您就会得到您要寻找的答案。 - Christopher Schultz
1
没错。但我宁愿确保我的代码不会受到实现变化的影响... - Maarten Bodewes

7

这是完整的复制粘贴版本,以便进行测试。

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

class Test {
    public static void main(String[] args) {
        int allowedKeyLength = 0;

        try {
            allowedKeyLength = Cipher.getMaxAllowedKeyLength("AES");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

        System.out.println("The allowed key length for AES is: " + allowedKeyLength);
    }
}

运行以下命令:

javac Test.java

java Test

如果 JCE 未能正常工作,输出结果为:128。如果 JCE 已经正常工作,输出结果会类似于:2147483647


函数 getMaxAllowedKeyLength 可能不会返回有效的 AES 密钥大小,因此此答案是不正确的。个人而言,我讨厌 printStackTrace,因为代码会继续运行。如果您抛出 RuntimeException,则无需将错误值 0 分配给 allowedKeyLength - Maarten Bodewes

3

如果您使用的是Linux系统,可以通过以下命令轻松检查:

java -version ; \
echo 'System.err.println(javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding").getMaxAllowedKeyLength("AES"));'  \
| java -cp /usr/share/java/bsh-*.jar bsh.Interpreter >/dev/null

如果输出类似于这样,那么无限制的加密技术是不可用的。
java version "1.7.0_76"
Java(TM) SE Runtime Environment (build 1.7.0_76-b13)
Java HotSpot(TM) 64-Bit Server VM (build 24.76-b04, mixed mode)
128

3

你可以使用Groovy在命令行中一步检查它:

groovysh -e 'javax.crypto.Cipher.getMaxAllowedKeyLength("AES")'

如果结果是2147483647,则您拥有无限的加密功能。

在旧版本的Groovy中,您需要删除-e

groovysh 'javax.crypto.Cipher.getMaxAllowedKeyLength("AES")'

0

注意:请使用jefflunt的答案KonstantinSpirov的答案。这个答案不是一个有效的答案,因为它总是返回true。我只是把这个答案留在这里,因为它在其他答案和评论中被引用,并且仅作为参考。


您可以使用以下代码来初始化一个static final boolean,然后在需要测试无限制加密支持的时候使用它(因为只有安装了无限制策略才支持AES 256位)。
boolean isUnlimitedSupported = false;
try {
    KeyGenerator kgen = KeyGenerator.getInstance("AES", "SunJCE");
    kgen.init(256);
    isUnlimitedSupported = true;
} catch (NoSuchAlgorithmException e) {
    isUnlimitedSupported = false;
} catch (NoSuchProviderException e) {
    isUnlimitedSupported = false;
}
System.out.println("isUnlimitedSupported=" + isUnlimitedSupported);
// set static final variable = isUnlimitedSupported;

2
确实。这将始终返回true。答案应该被移除。 - eis
@eis,我同意。这将始终返回true。更新以记录更好的答案。 - Go Dan
3
这样不可行,因为Cipher检查允许使用的密钥大小,而KeyGenerator则没有这样做。这些限制并非特定于提供程序。此外,实例化KeyGenerator不是检查这些限制的高效或逻辑方法。请删除不正确的答案。 - Maarten Bodewes

-1

最近我不得不添加一个JCE检查,我的解决方案演变成了以下片段。这是一个Groovy脚本,但应该很容易转换为标准的Java方法,并带有try catch。这已经在Java 7和Java 8中进行了测试。

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.SecretKey;

// Make a blank 256 Bit AES Key
final SecretKey secretKey = new SecretKeySpec(new byte[32], "AES");
final Cipher encryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// This line will throw a invalid key length exception if you don't have
// JCE Unlimited strength installed
encryptCipher.init(Cipher.ENCRYPT_MODE, secretKey);
// If it makes it here, you have JCE installed

1
你的方案存在一些问题。首先,它依赖于代码运行期间的异常来执行测试。这使得调试更加困难。可能会有未来的限制,例如RSA不影响AES密钥大小,那么测试将失败。第三,它需要创建不真正需要执行测试的虚假对象。尽管如此,该测试当然适用于Java 7和8,并且您选择的转换至少对于Java SE实现是强制性的。 - Maarten Bodewes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接