繁体   English   中英

为什么我通过尝试使用 java 解密 RSA 但使用 php 加密来获得此异常?

[英]Why I get this exception by trying to decrypt RSA with java but encrypted with php?

我正在使用 PHPSecLib 使用 RSA 加密我的文本:

      $rsa = new Crypt_RSA(); 
      $rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);

      extract($rsa->createKey());
      $rsa->loadKey($privatekey);

      $ciphertext = $rsa->encrypt($plaintext);

      $rsa->loadKey($publickey);
      return base64_encode($ciphertext) . ":" . base64_encode($publickey);

我得到类似 encryptedBased64:publicKeyBased64 的东西。

它似乎有效,如果我尝试使用与 PHP 相同的方法解密它也可以。 但是尝试用 java 解密,给了我 java.security.InvalidKeyException:无效的密钥格式。 这是代码:

    public static String decrypt(byte[] msg, byte[] key)
            throws Exception{

        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(key);
        PublicKey keys = keyFactory.generatePublic(publicKeySpec);

        Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding","BC");
        cipher.init(Cipher.DECRYPT_MODE, keys);
        return new String(cipher.doFinal(msg));
    }

    public static void main(String[] args) throws Exception{
      String res = "encryptedBased64:publicKeyBased64";
      decrypt(Base64.getDecoder().decode(res.split(":")[0]),Base64.getDecoder().decode(res.split(":")[1]));
    }

无法理解为什么。

  • RSA 用于两个上下文en-/decryptingsign-/verifying 对于加密/解密,发送者使用接收者的公钥加密,接收者使用自己的私钥解密。 对于签名/验证,发件人使用自己的私钥进行签名,收件人使用发件人的公钥进行验证。 这在此处此处进行了详细说明。

    在这一点上,很明显,在发布的代码中这两种上下文都被混淆了:对于加密/解密,发送者用他自己的私钥加密消息,而接收者用发送者的公钥解密它。 这是错误的。 如果在上述两个代码中都进行了修改以进行加密/解密,则 Java 中的解密也将起作用!

  • 要理解为什么解密在 PHP 的情况下有效,而在 Java 的情况下无效,必须考虑填充。 出于安全原因,实际上 RSA 必须始终与填充一起使用,例如 PKCS1-v1_5-padding 或 OAEP。 但是,填充因上下文而异。 用于加密/解密 RSAES-PKCS1-v1_5-padding 并用于签名/验证 RSASSA-PKCS1-v1_5-padding。 这同样适用于 OAEP(分别为 RSAES-OAEP 和 RSASSA-PSS)。 这在RFC8017这里有详细解释。 重要的是,用于解密的填充与用于加密的填充相同。 否则解密将失败。 这同样适用于签名/验证。

    在下文中,假设 PKCS1-v1_5-padding 与发布的代码一样使用。

    在 PHP/phpseclib 中,encrypt / decrypt方法使用 RSAES-PKCS1-v1_5-padding,无论是使用公钥还是私钥进行加密(或解密)。 这意味着encrypt / decrypt方法始终对应于加密/解密上下文。 对于签名/验证,有相应的方法sign / verify 这意味着在发布的代码中,RSAES-PKCS1-v1_5-padding 在 PHP 端用于加密和解密,这就是解密在 PHP 端起作用的原因。

    在 Java 中, Cipher类根据模式和密钥类型的组合确定上下文或填充。 加密模式/公钥- 和解密模式/私钥- 组合使用 RSAES-PKCS1-v1_5-padding 定义加密/解密上下文,其他组合使用 RSASSA-PKCS1-v1_5- 定义签名/验证上下文填充。 这意味着在发布的代码中,在 Java 端使用 RSASSA-PKCS1-v1_5-padding 进行解密。 因此,由于填充不同,在 PHP 中加密的消息的解密在 Java 中失败。

    注意:通常在 Java 中, Signature类用于签名/验证(而Cipher类用于加密/解密)。

  • 由于发布的代码没有透露要加密的内容,因此还应该提及的是,只有相对较短的消息可以使用 RSA 加密,对于较长的消息,将使用对称算法(如 AES),或两者结合使用(混合密码系统)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM