mirror of
https://github.com/danog/tgseclib.git
synced 2024-11-30 04:39:02 +01:00
RSA: make it so PSS keys can be saved
This commit is contained in:
parent
5b89ff4177
commit
8e03f5bfb2
@ -53,6 +53,7 @@ use phpseclib\Crypt\RSA\PublicKey;
|
||||
use phpseclib\Math\BigInteger;
|
||||
use phpseclib\Exceptions\UnsupportedAlgorithmException;
|
||||
use phpseclib\Exceptions\InconsistentSetupException;
|
||||
use phpseclib\Crypt\RSA\Keys\PSS;
|
||||
|
||||
/**
|
||||
* Pure-PHP PKCS#1 compliant implementation of RSA.
|
||||
@ -405,14 +406,26 @@ abstract class RSA extends AsymmetricKey
|
||||
|
||||
if ($components['isPublicKey']) {
|
||||
$key->exponent = $key->publicExponent;
|
||||
return $key;
|
||||
} else {
|
||||
$key->privateExponent = $components['privateExponent'];
|
||||
$key->exponent = $key->privateExponent;
|
||||
$key->primes = $components['primes'];
|
||||
$key->exponents = $components['exponents'];
|
||||
$key->coefficients = $components['coefficients'];
|
||||
}
|
||||
|
||||
$key->privateExponent = $components['privateExponent'];
|
||||
$key->exponent = $key->privateExponent;
|
||||
$key->primes = $components['primes'];
|
||||
$key->exponents = $components['exponents'];
|
||||
$key->coefficients = $components['coefficients'];
|
||||
if ($components['format'] == PSS::class) {
|
||||
$key = $key->withPadding(self::SIGNATURE_PSS);
|
||||
if (isset($components['hash'])) {
|
||||
$key = $key->withHash($components['hash']);
|
||||
}
|
||||
if (isset($components['MGFHash'])) {
|
||||
$key = $key->withMGFHash($components['MGFHash']);
|
||||
}
|
||||
if (isset($components['saltLength'])) {
|
||||
$key = $key->withSaltLength($components['saltLength']);
|
||||
}
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
@ -9,11 +9,11 @@
|
||||
*
|
||||
* Processes keys with the following headers:
|
||||
*
|
||||
* -----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
* -----BEGIN PRIVATE KEY-----
|
||||
* -----BEGIN PUBLIC KEY-----
|
||||
*
|
||||
* Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
|
||||
* is specific to private keys it's basically creating a DER-encoded wrapper
|
||||
* for keys. This just extends that same concept to public keys (much like ssh-keygen)
|
||||
* Analogous to "openssl genpkey -algorithm rsa-pss".
|
||||
*
|
||||
* @category Crypt
|
||||
* @package RSA
|
||||
@ -28,6 +28,7 @@ namespace phpseclib\Crypt\RSA\Keys;
|
||||
use phpseclib\Math\BigInteger;
|
||||
use phpseclib\Crypt\Common\Keys\PKCS8 as Progenitor;
|
||||
use phpseclib\File\ASN1;
|
||||
use phpseclib\File\ASN1\Maps;
|
||||
|
||||
/**
|
||||
* PKCS#8 Formatted RSA-PSS Key Handler
|
||||
@ -122,13 +123,13 @@ abstract class PSS extends Progenitor
|
||||
if ($decoded === false) {
|
||||
throw new \UnexpectedValueException('Unable to decode parameters');
|
||||
}
|
||||
$params = ASN1::asn1map($decoded[0], ASN1\Maps\RSASSA_PSS_params::MAP);
|
||||
$params = ASN1::asn1map($decoded[0], Maps\RSASSA_PSS_params::MAP);
|
||||
if (isset($params['maskGenAlgorithm']['parameters'])) {
|
||||
$decoded = ASN1::decodeBER($params['maskGenAlgorithm']['parameters']);
|
||||
if ($decoded === false) {
|
||||
throw new \UnexpectedValueException('Unable to decode parameters');
|
||||
}
|
||||
$params['maskGenAlgorithm']['parameters'] = ASN1::asn1map($decoded[0], ASN1\Maps\HashAlgorithm::MAP);
|
||||
$params['maskGenAlgorithm']['parameters'] = ASN1::asn1map($decoded[0], Maps\HashAlgorithm::MAP);
|
||||
} else {
|
||||
$params['maskGenAlgorithm'] = [
|
||||
'algorithm' => 'id-mgf1',
|
||||
@ -136,6 +137,10 @@ abstract class PSS extends Progenitor
|
||||
];
|
||||
}
|
||||
|
||||
if (!isset($params['hashAlgorithm']['algorithm'])) {
|
||||
$params['hashAlgorithm']['algorithm'] = 'id-sha1';
|
||||
}
|
||||
|
||||
$result['hash'] = str_replace('id-', '', $params['hashAlgorithm']['algorithm']);
|
||||
$result['MGFHash'] = str_replace('id-', '', $params['maskGenAlgorithm']['parameters']['algorithm']);
|
||||
$result['saltLength'] = (int) $params['saltLength']->toString();
|
||||
@ -146,4 +151,90 @@ abstract class PSS extends Progenitor
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a private key to the appropriate format.
|
||||
*
|
||||
* @access public
|
||||
* @param \phpseclib\Math\BigInteger $n
|
||||
* @param \phpseclib\Math\BigInteger $e
|
||||
* @param \phpseclib\Math\BigInteger $d
|
||||
* @param array $primes
|
||||
* @param array $exponents
|
||||
* @param array $coefficients
|
||||
* @param string $password optional
|
||||
* @param array $options optional
|
||||
* @return string
|
||||
*/
|
||||
public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, $primes, $exponents, $coefficients, $password = '', $options = [])
|
||||
{
|
||||
self::initialize_static_variables();
|
||||
|
||||
$key = PKCS1::savePrivateKey($n, $e, $d, $primes, $exponents, $coefficients);
|
||||
$key = ASN1::extractBER($key);
|
||||
$params = self::savePSSParams($options);
|
||||
return self::wrapPrivateKey($key, [], $params, $password, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a public key to the appropriate format
|
||||
*
|
||||
* @access public
|
||||
* @param \phpseclib\Math\BigInteger $n
|
||||
* @param \phpseclib\Math\BigInteger $e
|
||||
* @param array $options optional
|
||||
* @return string
|
||||
*/
|
||||
public static function savePublicKey(BigInteger $n, BigInteger $e, $options = [])
|
||||
{
|
||||
$key = PKCS1::savePublicKey($n, $e);
|
||||
$key = ASN1::extractBER($key);
|
||||
$params = self::savePSSParams($options);
|
||||
return self::wrapPublicKey($key, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes PSS parameters
|
||||
*
|
||||
* @access public
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
private static function savePSSParams($options)
|
||||
{
|
||||
/*
|
||||
The trailerField field is an integer. It provides
|
||||
compatibility with IEEE Std 1363a-2004 [P1363A]. The value
|
||||
MUST be 1, which represents the trailer field with hexadecimal
|
||||
value 0xBC. Other trailer fields, including the trailer field
|
||||
composed of HashID concatenated with 0xCC that is specified in
|
||||
IEEE Std 1363a, are not supported. Implementations that
|
||||
perform signature generation MUST omit the trailerField field,
|
||||
indicating that the default trailer field value was used.
|
||||
Implementations that perform signature validation MUST
|
||||
recognize both a present trailerField field with value 1 and an
|
||||
absent trailerField field.
|
||||
|
||||
source: https://tools.ietf.org/html/rfc4055#page-9
|
||||
*/
|
||||
$params = [
|
||||
'trailerField' => new BigInteger(1)
|
||||
];
|
||||
if (isset($options['hash'])) {
|
||||
$params['hashAlgorithm']['algorithm'] = 'id-' . $options['hash'];
|
||||
}
|
||||
if (isset($options['MGFHash'])) {
|
||||
$temp = ['algorithm' => 'id-' . $options['MGFHash']];
|
||||
$temp = ASN1::encodeDER($temp, Maps\HashAlgorithm::MAP);
|
||||
$params['maskGenAlgorithm'] = [
|
||||
'algorithm' => 'id-mgf1',
|
||||
'parameters' => new ASN1\Element($temp)
|
||||
];
|
||||
}
|
||||
if (isset($options['saltLength'])) {
|
||||
$params['saltLength'] = new BigInteger($options['saltLength']);
|
||||
}
|
||||
|
||||
return new ASN1\Element(ASN1::encodeDER($params, Maps\RSASSA_PSS_params::MAP));
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ use phpseclib\Exceptions\NoKeyLoadedException;
|
||||
use phpseclib\Exception\UnsupportedFormatException;
|
||||
use phpseclib\Crypt\Random;
|
||||
use phpseclib\Crypt\Common;
|
||||
use phpseclib\Crypt\RSA\Keys\PSS;
|
||||
|
||||
/**
|
||||
* Raw RSA Key Handler
|
||||
@ -524,9 +525,21 @@ class PrivateKey extends RSA implements Common\PrivateKey
|
||||
$type,
|
||||
empty($this->primes) ? 'savePublicKey' : 'savePrivateKey'
|
||||
);
|
||||
|
||||
if ($type == PSS::class) {
|
||||
if ($this->signaturePadding == self::SIGNATURE_PSS) {
|
||||
$options+= [
|
||||
'hash' => $this->hash->getHash(),
|
||||
'MGFHash' => $this->mgfHash->getHash(),
|
||||
'saltLength' => $this->sLen
|
||||
];
|
||||
} else {
|
||||
throw new UnsupportedFormatException('The PSS format can only be used when the signature method has been explicitly set to PSS');
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($this->primes)) {
|
||||
return $type::savePublicKey($this->modulus, $this->exponent);
|
||||
return $type::savePublicKey($this->modulus, $this->exponent, $options);
|
||||
}
|
||||
|
||||
return $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients, $this->password, $options);
|
||||
|
@ -22,6 +22,7 @@ use phpseclib\Exceptions\NoKeyLoadedException;
|
||||
use phpseclib\Crypt\Random;
|
||||
use phpseclib\Crypt\Common;
|
||||
use phpseclib\File\ASN1\Maps\DigestInfo;
|
||||
use phpseclib\Crypt\RSA\Keys\PSS;
|
||||
|
||||
/**
|
||||
* Raw RSA Key Handler
|
||||
@ -472,6 +473,18 @@ class PublicKey extends RSA implements Common\PublicKey
|
||||
{
|
||||
$type = self::validatePlugin('Keys', $type, 'savePublicKey');
|
||||
|
||||
if ($type == PSS::class) {
|
||||
if ($this->signaturePadding == self::SIGNATURE_PSS) {
|
||||
$options+= [
|
||||
'hash' => $this->hash->getHash(),
|
||||
'MGFHash' => $this->mgfHash->getHash(),
|
||||
'saltLength' => $this->sLen
|
||||
];
|
||||
} else {
|
||||
throw new UnsupportedFormatException('The PSS format can only be used when the signature method has been explicitly set to PSS');
|
||||
}
|
||||
}
|
||||
|
||||
return $type::savePublicKey($this->modulus, $this->publicExponent, $options);
|
||||
}
|
||||
|
||||
|
@ -35,13 +35,13 @@ abstract class RSASSA_PSS_params
|
||||
'constant' => 0,
|
||||
'optional' => true,
|
||||
'explicit' => true,
|
||||
'default' => 'sha1Identifier'
|
||||
//'default' => 'sha1Identifier'
|
||||
] + HashAlgorithm::MAP,
|
||||
'maskGenAlgorithm' => [
|
||||
'constant' => 1,
|
||||
'optional' => true,
|
||||
'explicit' => true,
|
||||
'default' => 'mgf1SHA1Identifier'
|
||||
//'default' => 'mgf1SHA1Identifier'
|
||||
] + MaskGenAlgorithm::MAP,
|
||||
'saltLength' => [
|
||||
'type' => ASN1::TYPE_INTEGER,
|
||||
|
@ -13,6 +13,7 @@ use phpseclib\Crypt\RSA\Keys\PKCS1;
|
||||
use phpseclib\Crypt\RSA\Keys\PKCS8;
|
||||
use phpseclib\Crypt\RSA\Keys\PuTTY;
|
||||
use phpseclib\Crypt\RSA\Keys\OpenSSH;
|
||||
use phpseclib\Crypt\RSA\Keys\PSS;
|
||||
use phpseclib\Math\BigInteger;
|
||||
|
||||
class Unit_Crypt_RSA_LoadKeyTest extends PhpseclibTestCase
|
||||
@ -914,5 +915,14 @@ IBgv3a3Lyb+IQtT75LE1yjE=
|
||||
$rsa = PublicKeyLoader::load($key);
|
||||
$this->assertInstanceOf(PrivateKey::class, $rsa);
|
||||
$this->assertInstanceOf(PublicKey::class, $rsa->getPublicKey());
|
||||
|
||||
$r = PSS::load($key);
|
||||
|
||||
$key = $rsa->toString('PSS');
|
||||
$r2 = PSS::load($key);
|
||||
|
||||
$this->assertSame($r['hash'], $r2['hash']);
|
||||
$this->assertSame($r['MGFHash'], $r2['MGFHash']);
|
||||
$this->assertSame($r['saltLength'], $r2['saltLength']);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user