```
<?php
/* Don't actually use this code! It's to demonstrate a concept. */
$public_key = '-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyaTgTt53ph3p
5GHgwoGWwz5hRfWXSQA08NCOwe0FEgALWos9GCjNFCd723nCHxBtN1qd
74MSh/uN88JPIbwxKheDp4kxo4YMN5trPaF0e9G6Bj1N02HnanxFLW+g
mLbgYO/SZYfWF/M8yLBcu5Y1Ot0ZxDDDXS9wIQTtBE0ne3YbxgZJAZTU
5XqyQ1DxdzYyC5lF6yBaR5UQtCYTnXAApVRuUI2Sd6L1E2vl9bSBumZ5
IpNxkRnAwIMjeTJB/0AIELh0mE5vwdihOCbdV6alUyhKC1+1w/FW6HWc
p/JG1kKC8DPIidZ78Bbqv9YFzkAbNni5eSBOsXVBKG78Zsc8owIDAQAB
-----END PUBLIC KEY-----';
// The letter "A" repeated 246 times:
$msg = str_repeat('A', 246);
// Encrypt
$ciphertext = '';
$encrypted = openssl_public_encrypt($msg, $ciphertext, $public_key); // Note: Insecure padding mode by default
var_dump(bin2hex($ciphertext));
```

The above script fails. It will print out `string(0) ""` to your console. Now change the `246` to an integer between 1 and 245, and you'll be greeted with a string of 512 hex characters.
Some libraries work around this limitation by breaking the input message into distinct blocks and encrypting them separately, similar to [ECB mode](https://blog.filippo.io/the-ecb-penguin). Not only is this *very slow*, it's also dangerous.
Since each block is encrypted separately, you can simply reorder ciphertext blocks and the changes will be reflected in the decrypted plaintext.
That's the danger of directly encrypting messages with RSA. There are other asymmetric cryptography algorithms that have their own security considerations, but generally, you want a hybrid cryptosystem.
**Hybrid encryption** involves encrypting each message with symmetric cryptography (e.g. AES-GCM), then encrypting the key asymmetric cryptography (rather than encrypting the message).
Since most symmetric encryption algorithms call for, at most, 256-bit keys (which is 32 bytes, far below the maximum 245 byte message that 2048-bit RSA allows), this tends to work out better for security *and* performance.
See, for example, [Zend\Crypt's Hybrid encryption documentation](https://github.com/zendframework/zend-crypt/blob/d67dd451489583966bd606b321fdf43ebddcb580/docs/book/hybrid.md).
#### What algorithms should developers use?
Avoid RSA, [unless you absolutely must support it](https://paragonie.com/blog/2018/04/protecting-rsa-based-protocols-against-adaptive-chosen-ciphertext-attacks).
Our general recommendation is to use [libsodium's crypto_box_seal API](https://paragonie.com/blog/2017/06/libsodium-quick-reference-quick-comparison-similar-functions-and-which-one-use#crypto-box-seal).
You don't even need to know what libsodium does here in order to use it safely. That's the entire point of libsodium.
But for the sake of completeness, the crypto_box_seal algorithm looks roughly like this:
1. Generate a random X25519 secret key and its associated public key.
2. Hash the public key from step 1 with the recipient's public key, using BLAKE2b, to get the nonce.
3. Perform an Elliptic Curve Diffie-Hellman key exchange between the random secret key and the recipient's public key.
4. Encrypt the message with XSalsa20-Poly1305 using the HSalsa20 hash of the ECDH output in step 3 as the message key, and the nonce from step 2.
5. Prepend the public key from step 1 to the ciphertext output from step 4.
If it helps, also look at [how `sodium_crypto_box_seal()` is implemented in sodium_compat](https://github.com/paragonie/sodium_compat/blob/aaaf71f3f7713248703c23cfbd1189ce1215f57f/src/Crypto.php#L438-L482).