如何在Java中获取RSA密钥的大小

11

给定一个 java.security.interfaces.RSAKey,如何获取它的大小?


1
不要重复发布:https://dev59.com/X3E85IYBdhLWcg3wJQCt - leonbloy
4个回答

27

你可以尝试这样做:

key.getModulus().bitLength();

请注意,如果您的密钥具有八个前导零,则此策略可能会失败。不幸的是,在某些互操作情况下,选择二的幂是必要的。如果您尝试将Java生成的公钥导入到.Net中并使用它来加密数据,则除非密钥长度符合预期,否则它将无法在Java中解密。如果您生成了一个2048位的密钥,则不能给.Net一个具有2040位(密钥情况下的8个前导零)或2052位(如果您将BigInteger转换为十六进制字符串,则可以获得额外的前导零)的密钥。您必须确切地提供2048位。它影响OAEP填充方案。 - Wheezil
如 https://stackoverflow.com/questions/11729893/data-encrypted-in-c-sharp-is-1-byte-too-long-to-be-decrypted-in-java 所解释的那样,用C#加密的数据在Java中解密时会多一个字节。 - Wheezil

5
(编辑:在了解到生成RSA密钥的素数所受限制之前,我写下了这篇回答。 http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf 现在我相信任何好的密钥生成器都应确保模数介于2 ^(n-1)和2 ^ n-1之间。因此,模数的最小二进制补码表示始终只有在密钥创建时指定密钥长度的位数。因此,例如,如果您创建一个2048位的密钥,则key.getModulus().bitLength()将始终返回2048。)

恕我直言,当模数的最高有效位为0时,key.getModulus().bitLength()不会返回正确的值吧? 例如,对于2048位密钥,如果模数的最高有效位为0,则key.getModulus().bitLength()将返回2047(如果更多位是0,则返回更少)。在这种情况下,我认为期望的结果实际上应该是2048。

BigInteger.bitLength()的文档如下:

返回此BigInteger最小二进制补码的位数,不包括符号位。对于正BigIntegers,这相当于普通二进制表示中的位数。(计算(ceil(log2(this < 0?-this:this +1)))。)

恐怕需要对密钥可能的大小作出一些假设。例如,您必须假设只会看到1024、2048或4096位的密钥,然后执行以下操作:

int keySize;
int bitLength = key.getModulus().bitLength();
if (bitLength <= 512) {
  throw new IllegalArgumentException(...)
}
else if (bitLength <= 1024) {
  keySize = 1024;
}
else if (bitLength <= 2048) {
  keySize = 2048;
}
else if (bitLength <= 4096) {
  keySize = 4096;
}
else {
  throw new IllegalArgumentException(...)
}
return keySize;

有时候,即使是这段代码也可能出现错误(非常罕见),例如,当2048位密钥的前1048位全部为0时。不过我认为这并不是什么需要担心的问题。


这段代码在任何情况下都是错误的。key.getModulus().bitLength() 是由“密钥大小”或“密钥长度”术语定义的值。用任意选择的值替换它是完全错误的。 - Oleg Estekhin
Oleg,你当然是对的。我添加了编辑说明我的误解关于限制进入密钥生成的质因数。 - John L
如果仍然对劣质密钥生成器感到不放心,可以使用四舍五入:keySize = ((bitLength + 127)/128)*128。这比猜测要好。而高128位全为零的概率大约是2^128中的1。 - Seva Alekseyev
1
不幸的是,有些互操作情况下选择2的幂是必要的。如果您尝试将Java生成的公钥导入到.Net中并使用它来加密数据,则除非密钥长度符合预期,否则它将无法在Java中解密。如果您生成了一个2048位的密钥,则不能给.Net一个具有2040位(密钥情况下的8个前导零)或2052位(如果您将BigInteger转换为十六进制字符串,则可以获得额外的前导零)的密钥。您必须确切地提供2048位。这并不总是会导致错误,但它会影响OAEP填充方案。 - Wheezil
正如 https://stackoverflow.com/questions/11729893/data-encrypted-in-c-sharp-is-1-byte-too-long-to-be-decrypted-in-java 中所解释的那样,用C#加密的数据比Java解密时多了一个字节。 - Wheezil

4
RSA密钥的大小是其模数中的位数,因此您需要使用myRSAKey.getModulus().bitLength()来获取它的大小。

4分钟晚了,该死!:P - user979222

3

我分享了在被接受的答案中提到的Wheezil的担忧。带有8个前导0位数的模数将会破坏这种方法。这种情况发生的概率是0.4%,在我看来是不可接受的。因此,我个人使用以下方法:

Cipher rsa = Cipher.getInstance("RSA");
rsa.init(Cipher.ENCRYPT_MODE, yourKey);
int keyBitSize = rsa.getOutputSize(0) * Byte.SIZE;

由于RSA输出大小始终与密钥模数相同,因此即使输入为0长度,输出的大小也相同。


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