Java(Android) 中的 RSA 密钥签名和验证

RSA key signing and verifying in Java(Android)

我在理解 RSA 签名和验证的概念时遇到了一些小问题。 问题是我可以创建密钥对(public 和私钥),这非常好。

        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
        random = SecureRandom.getInstance("SHA1PRNG");
        keyGen.initialize(2048, random);
        KeyPair pair = keyGen.generateKeyPair();
        myPrivateKey = pair.getPrivate();
        myPublicKey = pair.getPublic();

签字验证如下:

        //Singing with private key
        Signature s = Signature.getInstance("SHA1withRSA");
        s.initSign(javaPrivateKey);

        //Verifying with public key
        Signature s = Signature.getInstance("SHA1withRSA");
        s.initVerify(javaPublicKey);

当我打印 myPrivateKey 和 myPublicKey 时,我看到 public 和私钥的模数 (n) 和 public 指数 (e) 相同。

我已将 public 和私钥转换为 base64 和十六进制,我得到了不同的值,这非常好。但是,我无法使用 base64 或 hex 对消息进行签名。我只能用我从中得到的东西来签名:

         myPrivateKey = pair.getPrivate();  

我知道需要使用每个人都可以看到的 public 密钥进行验证。当接收方验证消息时,接收方是否仅使用模数和指数?发件人需要共享 public 密钥的哪一部分?密钥的模数和指数或 Base64 或十六进制值?

是的,接收器仅使用模数和指数;从数学上讲,不需要其他组件来验证 RSA 的签名。

数学运算是使用大数执行的(BigInteger 值通常在软件中实现 RSA 时)。要执行任何类型的计算,实现必须重新生成这些数字。这些数字如何传输与算法无关。

通常 RSA public 密钥使用基于 PKCS#1 的内容进行编码,它使用 ASN.1(定义结构)和 BER/DER(指定 public 密钥格式它定义了该结构的编码)。当然,不同的协议可能使用不同的 public 键编码。例如,PGP 使用完全不同的 "package format" 来编码密钥。


Java 但是 returns X.509(证书和 CRL)规范中定义的 SubjectPublicKeyInfo 结构;除了模数和指数之外,还包含一个算法标识符,表明它 一个 RSA public 密钥。所以这个结构也可以用来分发其他类型的键值。它可以通过在 RSAPublicKey 实例上调用 getEncoded() 来检索 - 假设此实例与 Oracle 提供的实例兼容 - 它们通常是。 Android 的实现当然应该与这个结构兼容。请注意,SubjectPublicKeyInfo 结构包含 PKCS#1 public 密钥结构 在其内部

要反转你需要一个 KeyFactory.getInstance("RSA") 并使用 X509EncodedKeySpec 导出密钥,用给定的字节数组初始化。


如果 您需要文本字符串而不是二进制字符串,那么您可以将 getEncoded() 返回的字节转换为 base 64 和十六进制。当然,在那种情况下,您需要先反转编码(即解码)结果,然后才能解码字节本身。

也可以自己编码模数和 public 指数。您可以使用 RSAPublicKey.getModulus()RSAPublicKey.getPublicExponent() 检索它们。要将它们反转回 RSAPublicKey,您可以使用 KeyFactory.getInstance("RSA")RSAPublicKeySpec。这样你就可以创建一个字符串 "(<modulus>, <exp>)" 并使用它来分发密钥。不过,通常您会希望遵守预定义的标准。


此答案未涵盖的事实是,要使用 public 密钥进行验证,您首先需要在 public 密钥中建立 trust。如果您不信任 public 密钥,那么您就不知道是谁创建了 public 密钥。在那种情况下,您也不能相信您的验证操作的结果;签名可能是用对手的密钥对创建的。不过,深入研究 Public 关键基础设施 (PKI / PKIX) 对于这个答案来说有点过分了。

同样:SHA-1 不再被认为是安全的,尤其是 用于签名生成/验证。您至少要使用 SHA256withRSA 或 - 稍微更高级和更安全的 - 使用 PSS 的 RSA 方案。 2048 位对于舒适度来说太小了;如果您的方案允许,建议使用 4096 位密钥。