phpseclib

phpseclib

  • Docs
  • API
  • Support
  • GitHub

›Public Keys

Introduction

  • Why phpseclib?
  • Installation
  • Speed
  • Versioning

SSH2

  • Connecting
  • Authenticating
  • Running Commands
  • SFTP
  • Diagnosing Issues

Public Keys

  • Overview
  • RSA
  • DSA
  • Elliptic Curves
  • (EC)DH
  • Example: JWT

Symmetric Keys

  • Overview

X.509

  • X.509
  • CSR
  • SPKAC
  • CRL

Interoperability

  • Overview
  • Python
  • Java
  • JavaScript
  • Node.js
  • Go
  • Ruby
  • C#
  • C
  • PHP

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).
  • 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 *
    • Private keys correspond to the format described in Private Key BLOBs
    • Public keys correspond to the format described in Public Key BLOBs

A more in-depth discussion of the common formats (ie. ones that don't have a red asterisk* next to them) can be found in Common Key Formats. See Sample RSA Keys for actual samples.

[1] These are the only formats that support multi-prime RSA.

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 encoding 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().

← OverviewDSA →
  • Supported Formats
    • Raw RSA Public Keys
  • Creating Keys
  • Creating / Verifying Signatures
    • RSA::SIGNATURE_PSS
    • RSA::SIGNATURE_PKCS1
    • RSA::SIGNATURE_RELAXED_PKCS1
  • Encryption / Decryption
    • RSA::ENCRYPTION_OAEP
    • RSA::ENCRYPTION_PKCS1
    • RSA::ENCRYPTION_NONE
  • Key Attributes
  • Public Keys as Private Keys
  • Blinding
phpseclib
Docs
IntroductionSSH2 / SFTPPublic Key CryptoSymmetric Key CryptoX.509 / CSR / SPKAC / CRLInteroperability
Support
Docs (1.0 / 2.0)Stack OverflowGitHubStar
Sponsor
PatreonGitHubPayPal
Copyright © 2025 Jim Wigginton