如何通过提供私钥获取RSA公钥?

12
我正在寻找一个Java函数,该函数将获取RSA PrivateKey并返回正确的RSA PublicKey。
或者,是否有一种函数可以告诉我们RSA PrivateKey / PublicKey是否有效?

1
保持冷静。论坛与实时聊天有很大的不同。请始终记住,用户是出于自己的意愿在这里,并且可以随时离开。 - Reporter
请定义您所说的“有效”的含义。 - President James K. Polk
除了已经给出的答案,如果您可以始终执行签名/验证(对任何值),以查看密钥是否匹配。请注意,偶尔会有些麻烦,例如,如果为私钥保留使用计数(某些HSM和智能卡会这样做)。仅比较模数或模数的哈希也可以,每个密钥对的模数应该是唯一的。不需要公共指数。 - Maarten Bodewes
6个回答

13
如果您有 RSAPrivateCrtKey 对象作为私钥,您可以获取公共指数以及模数。
然后,您可以像这样创建公钥:
RSAPublicKeySpec publicKeySpec = new java.security.spec.RSAPublicKeySpec(modulus, exponent);   
try {   
     KeyFactory keyFactory = KeyFactory.getInstance("RSA");   

     PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);   
} catch (Exception e) {   
     e.printStackTrace();   
} 

构造函数实际上是RSAPublicKeySpec(BigInteger modulus, BigInteger publicExponent) http://docs.oracle.com/javase/7/docs/api/java/security/spec/RSAPublicKeySpec.html - Eric Woodruff

6
我想不出你需要这个的任何好理由。但是在这里呈现它:
static boolean isValidRSAPair(KeyPair pair)
{
  Key key = pair.getPrivate();
  if (key instanceof RSAPrivateCrtKey) {
    RSAPrivateCrtKey pvt = (RSAPrivateCrtKey) key;
    BigInteger e = pvt.getPublicExponent();
    RSAPublicKey pub = (RSAPublicKey) pair.getPublic();
    return e.equals(pub.getPublicExponent()) && 
      pvt.getModulus().equals(pub.getModulus());
  }
  else {
    throw new IllegalArgumentException("Not a CRT RSA key.");
  }
}

1
正如其他人所指出的那样,如果您有一个RSA CRT KEY,则可以从中提取公钥。但是,实际上无法从纯私钥中检索公钥。
原因很简单:在生成RSA密钥时,私钥和公钥实际上没有区别。其中一个被选择为私钥,其余的则为公钥。
因此,如果您可以从纯私钥计算出公钥,那么您就可以根据定义从公钥计算出私钥...
如果您同时拥有两个,实际上可以轻松测试它们是否匹配:
RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;
return rsaPublicKey.getModulus().equals( rsaPrivateKey.getModulus() )
  && BigInteger.valueOf( 2 ).modPow(
  rsaPublicKey.getPublicExponent().multiply( rsaPrivateKey.getPrivateExponent() )
    .subtract( BigInteger.ONE ), 
  rsaPublicKey.getModulus() ).equals( BigInteger.ONE );

0

如果您有一个类型为RSAPrivateKey的对象,则需要执行两个操作:

  1. 获取模数。很容易:privateKey.getModulus()
  2. 计算公共指数。这有点棘手,但不是不可能的。请参见公共指数的定义。通常,公共指数为65537

在获取模数和公共指数之后,您可以按照PeteyB的答案进行操作。


0
我终于找到了解决办法:
   public PublicKey generatePublicKey(String privateKey) {

        try {
            KeyFactory kf = KeyFactory.getInstance(ALGORITHM);
            byte[] encodedPv = Base64.getDecoder().decode(privateKey);

            PKCS8EncodedKeySpec keySpecPv = new PKCS8EncodedKeySpec(encodedPv);
            RSAPrivateCrtKey rsaPrivateKey = (RSAPrivateCrtKey) kf.generatePrivate(keySpecPv);


            RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(rsaPrivateKey.getModulus(), rsaPrivateKey.getPublicExponent());

            return kf.generatePublic(publicKeySpec);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }


首先,我们需要将Base64私钥转换为Byte[],然后通过使用以下两行代码找到PrivateKey:
 PKCS8EncodedKeySpec keySpecPv = new PKCS8EncodedKeySpec(encodedPv);
 RSAPrivateCrtKey rsaPrivateKey = (RSAPrivateCrtKey) kf.generatePrivate(keySpecPv);

使用RSAPrivateCrtKey类的方法rsaPrivateKey.getPublicExponent()可以获得公钥指数。
之后我们可以调用。
RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(rsaPrivateKey.getModulus(), rsaPrivateKey.getPublicExponent());
return kf.generatePublic(publicKeySpec);

我通过计算文件签名并在每次生成新的公钥后进行验证来测试这个解决方案。

-4
据我所知,如果已知RSA密钥对中的一个密钥,是无法推导出另一个密钥的。这相当于破解RSA加密算法。
要测试一对密钥,只需使用其中一个密钥加密某些内容,然后使用另一个密钥进行解密,看是否能够得到原始结果即可。

谢谢!你能对第二个答案再明确一点吗?你有实现它的Java代码吗? - DasDas
3
我认为这是错误的。根据https://dev59.com/am435IYBdhLWcg3wuiuz,您可以通过私钥获取公共RSA密钥。 - MK.
MK,你知道怎么用Java做吗? - DasDas
5
因为公钥包含在私钥中,所以才会出现这种情况。 - Mark Rotteveel
从私钥计算公钥是完全可行的。请查看我的回答。 - HRJ

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