Skip to main content
Version: 4.0

RSA

Loading and saving keys is discussed in Public Keys: Overview.

Supported Formats

  • PKCS1 1
    • Keys start with -----BEGIN RSA PRIVATE KEY----- or -----BEGIN RSA PUBLIC KEY-----
  • PKCS8 1
    • Keys start with -----BEGIN PRIVATE KEY----- or -----BEGIN ENCRYPTED PRIVATE KEY----- or -----BEGIN PUBLIC KEY-----
  • PSS 1
    • Corresponds to the openssl genpkey -algorithm rsa-pss command
    • Similar to PKCS8 but with RSASSA-PSS-params for the optional "parameters" attribute and a different OID (id-RSASSA-PSS vs rsaEncryption).
    • Format is unique to RSA.
  • PuTTY
    • Public keys start off with ---- BEGIN SSH2 PUBLIC KEY ----
  • OpenSSH
    • Private keys start with -----BEGIN OPENSSH PRIVATE KEY-----
  • JWK
    • Keys are JSON encoded
  • XML
  • MSBLOB

A more in-depth discussion of the common formats (ie. those that aren't noted as being unique to RSA) can be found in Common Key Formats. See Sample RSA Keys for actual samples.

Raw RSA Public Keys

Let's say you had the public key exponent and the public key modulo as distinct string values. Let's further say that they were hex-encoded. At that point you could load the key thusly:

use phpseclib4\Crypt\PublicKeyLoader;
use phpseclib4\Math\BigInteger;

$key = PublicKeyLoader::load([
'e' => new BigInteger($e, 16),
'n' => new BigInteger($n, 16)
]);

echo $key;

The key that was output would be a PKCS8 key. $key->getLoadedFormat() would return Raw.

To convert an RSA key object to a "Raw" array key one would need to call $key->toArray().

Creating Keys

Keys can be created thusly:

use phpseclib4\Crypt\RSA;

$private = RSA::createKey();
$public = $private->getPublicKey();

By default keys are 2048 bits. Alternate key lengths can be specified by doing RSA::createKey(1024) or whatever.

The exponent, by default, is 65537. It can be set by doing RSA::setExponent(37) or whatever (37 is what puttygen uses for the RSA keys it creates).

Multi-prime RSA can be employed by calling RSA::setSmallestPrime(256). By default it is 4096. As for why you'd want to use multi-prime RSA... generating a 2048 bit RSA key, without GMP or OpenSSL installed, can be a time consuming process as it requires 2x 1024-bit prime numbers be generated. Generating 8x 256-bit prime numbers is considerably faster.

Note that multi-prime RSA has limited interoperability: most tools (OpenSSL, most CAs, most SSH and TLS implementations) only handle 2-prime RSA. Use setSmallestPrime with a value smaller than 4096 only when you control both sides of the exchange.

Creating / Verifying Signatures

Signatures can be created / verified thusly:

//$private = $private->withPadding(RSA::SIGNATURE_PSS);
$signature = $private->sign($message);
echo $private->getPublicKey()->verify($message, $signature) ?
'valid signature' :
'invalid signature';

RSA::SIGNATURE_PSS

The Probabilistic Signature Scheme. This is the default method. It is more secure than the other methods but is less commonly used.

The following methods can be used to configure this method:

  • withHash (defaults to sha256)
  • withMGFHash (defaults to sha256)
  • withSaltLength (defaults to the length of the hash in bytes)

The minimum key length ($key->getLength()) is 8 * ($key->getHash()->getLengthInBytes() + $key->getSaltLength() + 2).

Employs randomized padding so signing the same message twice will not yield the same signature twice.

RSA::SIGNATURE_PKCS1

More commonly used, less secure.

Uses withHash, which defaults to sha256.

The minimum key length ($key->getLength()) depends on the hash being used:

HashMin Key Length (in bits)
md2360
md5360
sha1368
sha256496
sha384624
sha512752
sha224464
sha512/224464
sha512/256496

A "simple" formula isn't possible because, for this format, the "DigestInfo" of the hash algorithm is encoded in the base number, prior to modular exponentiation, and the length of this "DigestInfo" varies depending on the hash algorithm used (for example, for sha1, it's 15 bytes, and for sha256, it's 19 bytes).

Does not employee randomized padding so signing the same message twice will yield the same signature twice.

Encryption / Decryption

Encryption / decryption can be done thusly:

//$private = $private->withPadding(RSA::ENCRYPTION_OAEP);
$ciphertext = $private->getPublicKey()->encrypt($plaintext);
echo $private->decrypt($ciphertext);

All encryption schemes (save for RSA::ENCRYPTION_NONE) employ randomized padding so encrypting the same plaintext twice will yield different ciphertext's each time.

RSA::ENCRYPTION_OAEP

Optimal Asymmetric Encryption Padding. This is the default method. It is more secure than the other methods but is less commonly used.

The following methods can be used to configure this method:

  • withHash (defaults to sha256)
  • withMGFHash (defaults to sha256)
  • withLabel (defaults to the empty string)

The maxmimum plaintext length (in bytes) is ($key->getLength() - 2 * $key->getHash()->getLength() - 16) >> 3

RSA::ENCRYPTION_PKCS1

More commonly used, less secure.

No methods can be used to configure this method.

The maximum plaintext length (in bytes) is ($key->getLength() - 88) >> 3.

RSA::ENCRYPTION_NONE

Performs textbook RSA / basic modular exponentiation. This is not at all secure but may sometimes be needed for interoperability and also just general diagnosing of why ciphertext's aren't successfully decrypting even if the correct keys are being used (eg. it can be be used to provide insight into the "structure" of the decrypted message)

The maximum plaintext length (in bytes) is $key->getLength() >> 3.

Key Attributes

The bit length of the key (well, the modulus) can be determined by calling $key->getLength().

All the with methods have corresponding get methods as follows:

SetterGetter
withHashgetHash
withMGFHashgetMGFHash
withSaltLengthgetSaltLength
withLabelgetLabel
withPaddinggetPadding

While withHash and withMGFHash accept strings, getHash and getMGFHash return a Hash object (that can be cast to a string via __toString). This enables you to do such things as determine what the minimum size of a key is by formula, as depicted in the RSA::SIGNATURE_PSS and RSA::ENCRYPTION_OAEP sections.

Padding is, internally, denoted as a bitmask. So the default value returned by getPadding() is RSA::ENCRYPTION_OAEP | RSA::SIGNATURE_PSS. Only one encryption mode and one signature mode can be set at a time.

Public Keys as Private Keys

In theory, RSA public and private keys are indistinguishable from one another and are interchangeable. In theory, both consist simply of an exponent and a modulo.

In practice, RSA private keys have additional parameters to speed up computation by means of the Chinese Remainder Theorem.

For the sake of argument, however, let's say you had a key that just had the public key parameters but you wanted to use it as a private key. You could do so by doing $key->asPrivateKey().

Blinding

Blinding is enabled by default. It can be disabled / enabled by doing RSA::disableBlinding() / RSA::enableBlinding().

Footnotes

  1. These are the only formats that support multi-prime RSA. 2 3