The phpseclib/v1 code encrypts with OAEP, using the private exponent for encryption and the public exponent for decryption.
In phpseclib/v3, however, this is no longer possible, as the private key does not expose an encrypt()-function and the public key no decrypt()-function.
Nevertheless, the functionality can be ported to v3 by swapping the public and private exponents in the keys and then encrypting with the public key and decrypting with the private key as usual.
Example: Decryption of a ciphertext in v3, which was generated with the posted v1 code:
$privateKeyPem = "-----BEGIN PRIVATE KEY-----
MIIEvg...
-----END PRIVATE KEY-----";
$privateKeySwapped = null;
$publicKeySwapped = null;
swapKeys($privateKeyPem, $privateKeySwapped, $publicKeySwapped);
$ciphertext = base64_decode('<Base64 encoded ciphertext generated with the posted v1 code>');
$decrypted = $privateKeySwapped->withHash('sha1')->withMGFHash('sha1')->decrypt($ciphertext);
print($decrypted . PHP_EOL); // my fancy string
with:
function swapKeys($privateKeyPem, &$privateKeySwapped, &$publicKeySwapped) {
$privateKey = PublicKeyLoader::load($privateKeyPem);
$privateKeyParams = unserialize($privateKey->toString('Raw'));
$privateKeyParamsSwapped = [
'e' => $privateKeyParams['d'],
'n' => $privateKeyParams['n'],
'd' => $privateKeyParams['e'],
'p' => $privateKeyParams['primes'][1],
'q' => $privateKeyParams['primes'][2]
];
$privateKeySwapped = PublicKeyLoader::loadPrivateKey($privateKeyParamsSwapped);
$publicKeyParamsSwapped = [
'e' => $privateKeyParams['d'],
'n' => $privateKeyParams['n']
];
$publicKeySwapped = PublicKeyLoader::loadPublicKey($publicKeyParamsSwapped);
}
Here it must be taken into account that v1 uses SHA-1 by default for the OAEP- and MGF1-digest, while v3 applies SHA-256 by default, so that in v3 both digests must be explicitly set to SHA-1.
Note that the process "encryption with the private key" is, strictly speaking, not defined at all (but only (1) "encryption with the public key"/"decryption with the private key" and (2) "signing with the private key"/"verification with the public key").
Often the paraphrase "encryption with the private key" refers to signing, which may also be the case here. And accordingly, many libraries (if they support such a process at all) sign under the hood (and use the padding for signing), and do not encrypt, unlike phpseclib/v1, which, as described above, really encrypts (with OAEP and swapped exponents)!
Also note that the PHP/OpenSSL code posted in your answer generates a signature with PKCS#1 v1.5 padding, where the (unhashed) message is signed directly (and not the DER encoding of the DigestInfo value as it actually should be to comply with the specification in RFC 8017).
It may even be what you need, but it is not compatible with the logic implemented by the phpseclib/v1 code posted in the question.