1

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.

neubert
  • 15,947
  • 24
  • 120
  • 212
Bondavalli69
  • 63
  • 1
  • 7
  • `$publickey` is a *text* string starting with `-----BEGIN PUBLIC KEY-----`, but `X509EncodedKeySpec` requires a *byte array*. How did you convert the key? My guess is incorrectly, given the "invalid key format" exception. Since the error is in the code doing that, don't you think we need to see that part, in order to help you? – Andreas Oct 19 '19 at 21:27
  • yeah PHP return public key in base64 and that java function should decrypt it. I pass the key got from PHP to the method by doing : Base64.getDecoder().decode(res.split(":")[1]) and X509EncodedKeySpec gets the bytes of decoded public key string. What part do you want to see, this is the full code that encrypts/decrypts. – Bondavalli69 Oct 19 '19 at 21:32

1 Answers1

2
  • 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).

Community
  • 1
  • 1
Topaco
  • 40,594
  • 4
  • 35
  • 62
  • Thanks, @Topaco for your brilliant answer. I understood why it doesn't work but I have difficulty in building the Java code that determines en-/decrypting-context. Once done that I should be able to decrypt my messages. P.s: Encrypted messages are short and once they get encrypted via RSA, then they get encrypted again with AES. So I have a double encrypted contest. In java, I built the code to decrypt AES and it works, returning me the RSA information but still missing how it is possible to decrypt practically. If there is a simpler way than PhpSecLib, any suggestion would be accepted anyway. – Bondavalli69 Oct 21 '19 at 12:56
  • "_In PHP/phpseclib, the encrypt/decrypt-method always uses RSAES-PKCS1-v1_5-padding, regardless of whether the public or private key is used to encrypt (or decrypt)_". phpsecib uses OAEP padding by default - not PKCS1. PKCS1 is only used if you do `$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);` (as depicted in the link you linked to). Same thing for signatures. It uses PSS by default. PKCS1 has to be manually specified. – neubert Oct 21 '19 at 15:07
  • @Bondavalli69 - in response to your "_If there is a simpler way than PhpSecLib_" comment... the code sample in your original post doesn't make use of AES so maybe you could update your original post with your latest code? It's hard to provide feedback on non-existant code. In the code that you _currently_ have posted the phpseclib approach certainly looks less verbose (ie. easier to understand) than the Java approach but I suppose there's an element of subjectivity there.. – neubert Oct 21 '19 at 15:15
  • 1
    @neubert: At this point I implicitly assumed PKCS1-v1_5-padding as the selected padding because it is the padding used in the posted code. But obviously this has been misleading, which is why I put the sentence _In the following, it is assumed that PKCS1-v1_5-padding is used as in the posted code._ at the beginning of the section. That makes it clear, in my opinion. – Topaco Oct 21 '19 at 16:28
  • Unfortunately, your comment is not clear to me. You should describe in detail what your difficulties are in implementing the Java-code. Alternatively you can try an implementation and then ask Stackoverflow if you get stuck. The last part of the comment (from the PS) is completely unclear to me. Especially your RSA-AES construct I don't understand. Please, describe target, process and architecture more understandable or post your code as already mentioned by @neubert. It is best to edit your question, because the comment function offers only limited possibilities. – Topaco Oct 21 '19 at 16:40
  • @Topaco neubert. Please ignore my previous comment about AES. It doesn't make sense in this context it is confusional, just ignore it... Back to our RSA, my question is simple guys, I want to create a client-server encrypted communication with RSA, PHP will encrypt for every request a message with new key pair and client (java) should be able to decrypt it. I made an attempt by posting that code, but as Topaco said this is the wrong way and I found nothing useful online to reach my goal. – Bondavalli69 Oct 21 '19 at 19:31
  • I'll describe a scenario that is konfom to RSA en-/decryption: (1) The recipient (Java) generates a key pair and transmits the public key to the sender (PHP). (2) The sender encrypts the message with the recipient's public key and sends the encrypted message to the recipient. (3) The recipient decrypts the encrypted message with his private key. End. In your scenario in the last comment, the key generation takes place at the sender (instead of the recipient), which is pointless with regard to the described RSA en-/decryption, see [here](https://en.wikipedia.org/wiki/Public-key_cryptography). – Topaco Oct 22 '19 at 06:50