Skip to main content
Version: 3.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 phpseclib3\Crypt\PublicKeyLoader;
    use phpseclib3\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.

    Creating Keys

    Keys can be created thusly:

    use phpseclib3\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.

    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.

    RSA::SIGNATURE_RELAXED_PKCS1

    This is basically the same as PKCS1 with the caveat that the "DigestInfo" no longer has a fixed length.

    PKCS#1 § 9.2. EMSA-PKCS1-v1_5 says the following:

    In version 1.5 of this document, T was defined as the BER encoding, rather than the DER encoding, of the DigestInfo value. In particular, it is possible -- at least in theory -- that the verification operation defined in this document (as well as in version 2.0) rejects a signature that is valid with respect to the specification given in PKCS #1 v1.5. This occurs if other rules than DER are applied to DigestInfo (e.g., an indefinite length encoding of the underlying SEQUENCE type). While this is unlikely to be a concern in practice, a cautious implementor may choose to employ a verification operation based on a BER decoding operation as specified in PKCS #1 v1.5. In this manner, compatibility with any valid implementation based on PKCS #1 v1.5 is obtained. Such a verification operation should indicate whether the underlying BER encoding is a DER encoding and hence whether the signature is valid with respect to the specification given in this document.

    DER in this case refers to the Distinguished Encoding Rules, a subset of the Basic Encoding Rules (BER). Anything encoded using DER is valid BER but not everything encoded in BER is valid DER. Signatures phpseclib creates are valid DER regardless of whether or not PKCS1 or RELAXED_PKCS1 modes are used but when it comes to signature verification RELAXED_PKCS1 actually decodes the BER instead of just matching strings of fixed length.

    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