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-----
- Keys start with
- PKCS8 [1]
- Keys start with
-----BEGIN PRIVATE KEY-----
or-----BEGIN ENCRYPTED PRIVATE KEY-----
or-----BEGIN PUBLIC KEY-----
- Keys start with
- 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
vsrsaEncryption
).
- Corresponds to the
- PuTTY
- Public keys start off with
---- BEGIN SSH2 PUBLIC KEY ----
- Public keys start off with
- OpenSSH
- Private keys start with
-----BEGIN OPENSSH PRIVATE KEY-----
- Private keys start with
- 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:
Hash | Min Key Length (in bits) |
---|---|
md2 | 360 |
md5 | 360 |
sha1 | 368 |
sha256 | 496 |
sha384 | 624 |
sha512 | 752 |
sha224 | 464 |
sha512/224 | 464 |
sha512/256 | 496 |
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:
Setter | Getter |
---|---|
withHash | getHash |
withMGFHash | getMGFHash |
withSaltLength | getSaltLength |
withLabel | getLabel |
withPadding | getPadding |
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()
.