java - 为什么我尝试用 java 解密 RSA 但用 php 加密时会出现此异常?

标签 java php rsa phpseclib

我正在使用 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);

我得到类似 cryptoBased64:publicKeyBased64 的信息。

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

    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 用于加密/解密签名/验证两种环境。对于加密/解密,发送者使用接收者的公钥进行加密,接收者使用他自己的私钥进行解密。对于签名/验证,发送者使用自己的私钥进行签名,接收者使用发送者的公钥进行验证。这个有详细解释herehere .

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

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

    下面,假设使用 PKCS1-v1_5-padding,如发布的代码中所示。

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

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

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

  • 由于发布的代码没有透露要加密的内容,因此还应该提到的是,只有相对较短的消息可以使用 RSA 进行加密,而对于较长的消息,将使用对称算法(例如 AES) ,或两个进程的组合 ( hyprid cryptosystem )。

关于java - 为什么我尝试用 java 解密 RSA 但用 php 加密时会出现此异常?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58468087/

相关文章:

Python M2Crypto - 生成 DSA key 对并分离公共(public)/私有(private)组件

java - 单击文本时没有任何反应

java - 使Jackson在序列化时不输出类名(使用Spring MVC)

Java:获取未倒置的字符串数组对的所有组合的算法

php - 使用 php 和 mysql 更新某些用户

javascript - 从数字生成公钥

java - Mac OS Java 7 JDialog.dispose 内存泄漏

php - 有没有办法在 gmail/wave/gdocs 中粘贴代码片段/ block ?

php - 用数组重写 PHP 函数

ubuntu - Amazon EC2 RSA key 停止身份验证 - 权限被拒绝(公钥)