Bondavalli69
Bondavalli69

Reputation: 63

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

I'm using PHPSecLib to encrypt my text with 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);

I get something like encryptedBased64:publicKeyBased64.

It seems to work and if I try to decrypt using the same method with PHP it works as well. But trying to decrypt with java, gives me java.security.InvalidKeyException: invalid key format. This is the code:

    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]));
    }

Cannot understand why.

Upvotes: 1

Views: 954

Answers (1)

Topaco
Topaco

Reputation: 49276

  • RSA is used in the two contexts en-/decrypting and sign-/verifying. For en-/decrypting, the sender encrypts with the recipient's public key and the recipient decrypts with his own private key. For sign-/verifying, the sender signs with his own private key and the recipient verifies with the sender's public key. This is explained in detail here and here.

    At this point it becomes clear that both contexts have been confused in the posted code: For en-/decrypting, the sender encrypts the message with his own private key and the recipient decrypts it with the sender's public key. This is wrong. If this is modified in both codes as described above for en-/decrypting, the decryption in Java will also work!

  • To understand why decryption works in the case of PHP and not in the case of Java, padding must be considered. For security reasons, in practice RSA must always be used in conjunction with padding, e.g. PKCS1-v1_5-padding or OAEP. However, the padding differs depending on the context. For en-/decrypting RSAES-PKCS1-v1_5-padding is used and for sign-/verifying RSASSA-PKCS1-v1_5-padding. The same applies to OAEP (RSAES-OAEP and RSASSA-PSS, repectively). This is explained in detail in RFC8017 and here. It is important that the padding for decryption is identical to the padding for encryption. Otherwise the decryption will fail. The same applies to sign-/verifying.

    In the following, it is assumed that PKCS1-v1_5-padding is used as in the posted code.

    In PHP/phpseclib, the encrypt/decrypt-method uses RSAES-PKCS1-v1_5-padding, regardless of whether the public or private key is used to encrypt (or decrypt). This means that the encrypt/decrypt-method always corresponds to the en-/decrypting-context. For sign-/verifying there are the corresponding methods sign/verify. This means that in the posted code RSAES-PKCS1-v1_5-padding is used on the PHP-side for encryption and decryption which is why decryption works on the PHP-side.

    In Java, the Cipher-class determines the context or padding based on the combination of mode and type of key. The encryption mode/public key- and decryption mode/private key-combinations define the en-/decrypting-context with RSAES-PKCS1-v1_5-padding and the other combinations define the sign-/verifying-context with RSASSA-PKCS1-v1_5-padding. This means that in the posted code RSASSA-PKCS1-v1_5-padding is used on the Java-side for decryption. The decryption of the message encrypted in PHP therefore fails in Java because of the different padding.

    Note: Usually in Java, the Signature-class is used for sign-/verifying (and the Cipher-class for en-/decrypting).

  • Since the posted code doesn't reveal what is to be encrypted, it should also be mentioned that only relatively short messages can be encrypted with RSA and that for longer messages a symmetrical algorithm such as AES is to be used, or both processes in combination (hyprid cryptosystem).

Upvotes: 2

Related Questions