make RSA / DSA / ECDSA immutable and add support to SSH2 / X509

This commit is contained in:
terrafrost 2019-05-19 15:35:29 -05:00
parent b09bc1883e
commit cc32cd2e95
36 changed files with 3413 additions and 3572 deletions

View File

@ -15,11 +15,13 @@
namespace phpseclib\Crypt\Common;
use phpseclib\Exception\UnsupportedFormatException;
use phpseclib\Exception\NoKeyLoadedException;
use phpseclib\Math\BigInteger;
use phpseclib\Crypt\Hash;
use ParagonIE\ConstantTime\Base64;
use phpseclib\Exception\UnsupportedOperationException;
use phpseclib\Exception\FileNotFoundException;
use phpseclib\Crypt\RSA;
use phpseclib\Crypt\DSA;
use phpseclib\Crypt\ECDSA;
/**
* Base Class for all stream cipher classes
@ -46,15 +48,36 @@ abstract class AsymmetricKey
protected static $one;
/**
* OpenSSL configuration file name.
* Format of the loaded key
*
* Set to null to use system configuration file.
*
* @see self::createKey()
* @var mixed
* @access public
* @var string
* @access private
*/
protected static $configFile;
protected $format;
/**
* Hash function
*
* @var \phpseclib\Crypt\Hash
* @access private
*/
protected $hash;
/**
* HMAC function
*
* @var \phpseclib\Crypt\Hash
* @access private
*/
private $hmac;
/**
* Enable Blinding?
*
* @var bool
* @access private
*/
protected static $enableBlinding = true;
/**
* Supported plugins (lower case)
@ -92,73 +115,6 @@ abstract class AsymmetricKey
*/
private static $signatureFileFormats = [];
/**
* Password
*
* @var string
* @access private
*/
protected $password = false;
/**
* Loaded File Format
*
* @var string
* @access private
*/
protected $format = false;
/**
* Private Key Format
*
* @var string
* @access private
*/
protected $privateKeyFormat = 'PKCS8';
/**
* Public Key Format
*
* @var string
* @access private
*/
protected $publicKeyFormat = 'PKCS8';
/**
* Parameters Format
*
* No setParametersFormat method exists because PKCS1 is the only format that supports
* parameters in both DSA and ECDSA (RSA doesn't have an analog)
*
* @var string
* @access private
*/
protected $parametersFormat = 'PKCS1';
/**
* Hash function
*
* @var \phpseclib\Crypt\Hash
* @access private
*/
protected $hash;
/**
* HMAC function
*
* @var \phpseclib\Crypt\Hash
* @access private
*/
private $hmac;
/**
* Hash manually set?
*
* @var bool
* @access private
*/
protected $hashManuallySet = false;
/**
* Available Engines
*
@ -169,10 +125,8 @@ abstract class AsymmetricKey
/**
* The constructor
*
* @access public
*/
public function __construct()
protected function __construct()
{
self::initialize_static_variables();
@ -180,51 +134,14 @@ abstract class AsymmetricKey
$this->hmac = new Hash('sha256');
}
/**
* Tests engine validity
*
* @access public
* @param int $val
*/
public static function useBestEngine()
{
static::$engines = [
'PHP' => true,
'OpenSSL' => extension_loaded('openssl') && file_exists(self::$configFile),
// this test can be satisfied by either of the following:
// http://php.net/manual/en/book.sodium.php
// https://github.com/paragonie/sodium_compat
'libsodium' => function_exists('sodium_crypto_sign_keypair')
];
return static::$engines;
}
/**
* Flag to use internal engine only (useful for unit testing)
*
* @access public
*/
public static function useInternalEngine()
{
static::$engines = [
'PHP' => true,
'OpenSSL' => false,
'libsodium' => false
];
}
/**
* Initialize static variables
*
* @access private
*/
protected static function initialize_static_variables()
{
if (!isset(self::$zero)) {
self::$zero= new BigInteger(0);
self::$one = new BigInteger(1);
self::$configFile = __DIR__ . '/../../openssl.cnf';
}
self::loadPlugins('Keys');
@ -233,6 +150,70 @@ abstract class AsymmetricKey
}
}
/**
* Load the key
*
* @param string $key
* @param string $type
* @param string $password
* @return array|bool
*/
protected static function load($key, $type, $password)
{
self::initialize_static_variables();
$components = false;
if ($type === false) {
foreach (self::$plugins[static::ALGORITHM]['Keys'] as $format) {
try {
$components = $format::load($key, $password);
} catch (\Exception $e) {
$components = false;
}
if ($components !== false) {
break;
}
}
} else {
$format = strtolower($type);
if (isset(self::$plugins[static::ALGORITHM]['Keys'][$format])) {
$format = self::$plugins[static::ALGORITHM]['Keys'][$format];
$components = $format::load($key, $password);
}
}
if ($components === false) {
throw new NoKeyLoadedException('Unable to read key');
}
$components['format'] = $format;
return $components;
}
/**
* Validate Plugin
*
* @access private
* @param string $format
* @param string $type
* @param string $method optional
* @return mixed
*/
protected static function validatePlugin($format, $type, $method = NULL)
{
$type = strtolower($type);
if (!isset(self::$plugins[static::ALGORITHM][$format][$type])) {
throw new UnsupportedFormatException("$type is not a supported format");
}
$type = self::$plugins[static::ALGORITHM][$format][$type];
if (isset($method) && !method_exists($type, $method)) {
throw new UnsupportedFormatException("$type does not implement $method");
}
return $type;
}
/**
* Load Plugins
*
@ -259,118 +240,6 @@ abstract class AsymmetricKey
}
}
/**
* Validate Plugin
*
* @access private
* @param string $format
* @param string $type
* @param string $method optional
* @return mixed
*/
protected static function validatePlugin($format, $type, $method = NULL)
{
$type = strtolower($type);
if (!isset(self::$plugins[static::ALGORITHM][$format][$type])) {
return false;
}
$type = self::$plugins[static::ALGORITHM][$format][$type];
if (isset($method) && !method_exists($type, $method)) {
return false;
}
return $type;
}
/**
* Load the key
*
* @access private
* @param string $key
* @param string $type
* @return array|bool
*/
protected function load($key, $type)
{
if ($key instanceof self) {
$this->hmac = $key->hmac;
return;
}
$components = false;
if ($type === false) {
foreach (self::$plugins[static::ALGORITHM]['Keys'] as $format) {
try {
$components = $format::load($key, $this->password);
} catch (\Exception $e) {
$components = false;
}
if ($components !== false) {
break;
}
}
} else {
$format = strtolower($type);
if (isset(self::$plugins[static::ALGORITHM]['Keys'][$format])) {
$format = self::$plugins[static::ALGORITHM]['Keys'][$format];
$components = $format::load($key, $this->password);
}
}
if ($components === false) {
$this->format = false;
return false;
}
$this->format = $format;
return $components;
}
/**
* Load the public key
*
* @access private
* @param string $key
* @param string $type
* @return array
*/
protected function setPublicKey($key, $type)
{
$components = false;
if ($type === false) {
foreach (self::$plugins[static::ALGORITHM]['Keys'] as $format) {
if (!method_exists($format, 'savePublicKey')) {
continue;
}
try {
$components = $format::load($key, $this->password);
} catch (\Exception $e) {
$components = false;
}
if ($components !== false) {
break;
}
}
} else {
$format = strtolower($type);
if (isset(self::$plugins[static::ALGORITHM]['Keys'][$format])) {
$format = self::$plugins[static::ALGORITHM]['Keys'][$format];
$components = $format::load($key, $this->password);
}
}
if ($components === false) {
$this->format = false;
return false;
}
$this->format = $format;
return $components;
}
/**
* Returns a list of supported formats.
*
@ -407,161 +276,6 @@ abstract class AsymmetricKey
}
}
/**
* Returns the public key's fingerprint
*
* The public key's fingerprint is returned, which is equivalent to running `ssh-keygen -lf rsa.pub`. If there is
* no public key currently loaded, false is returned.
* Example output (md5): "c1:b1:30:29:d7:b8:de:6c:97:77:10:d7:46:41:63:87" (as specified by RFC 4716)
*
* @access public
* @param string $algorithm The hashing algorithm to be used. Valid options are 'md5' and 'sha256'. False is returned
* for invalid values.
* @return mixed
*/
public function getPublicKeyFingerprint($algorithm = 'md5')
{
$type = self::validatePlugin('Keys', 'OpenSSH', 'getBinaryOutput');
if ($type === false) {
return false;
}
$status = $type::getBinaryOutput();
$type::setBinaryOutput(true);
$key = $this->getPublicKey('OpenSSH');
if ($key === false) {
return false;
}
$type::setBinaryOutput($status);
switch ($algorithm) {
case 'sha256':
$hash = new Hash('sha256');
$base = Base64::encode($hash->hash($key));
return substr($base, 0, strlen($base) - 1);
case 'md5':
return substr(chunk_split(md5($key), 2, ':'), 0, -1);
default:
return false;
}
}
/**
* __toString() magic method
*
* @access public
* @return string
*/
public function __toString()
{
try {
$key = $this->getPrivateKey($this->privateKeyFormat);
if (is_string($key)) {
return $key;
}
$key = $this->getPublicKey($this->publicKeyFormat);
if (is_string($key)) {
return $key;
}
if (!method_exists($this, 'getParameters')) {
return '';
}
$key = $this->getParameters($this->parametersFormat);
return is_string($key) ? $key : '';
} catch (\Exception $e) {
return '';
}
}
/**
* __clone() magic method
*
* @access public
* @return static
*/
public function __clone()
{
$key = new static();
$key->load($this);
return $key;
}
/**
* Determines the private key format
*
* @see self::__toString()
* @access public
* @param string $format
*/
public function setPrivateKeyFormat($format)
{
$type = self::validatePlugin('Keys', $format);
if ($type === false) {
throw new FileNotFoundException('Plugin not found');
}
$type = self::validatePlugin('Keys', $format, 'savePrivateKey');
if ($type === false) {
throw new UnsupportedOperationException('Plugin does not support private keys');
}
$this->privateKeyFormat = $format;
}
/**
* Determines the public key format
*
* @see self::__toString()
* @access public
* @param string $format
*/
public function setPublicKeyFormat($format)
{
$type = self::validatePlugin('Keys', $format);
if ($type === false) {
throw new FileNotFoundException('Plugin not found');
}
$type = self::validatePlugin('Keys', $format, 'savePublicKey');
if ($type === false) {
throw new UnsupportedOperationException('Plugin does not support public keys');
}
$this->publicKeyFormat = $format;
}
/**
* Determines the key format
*
* Sets both the public key and private key formats to the specified format if those formats support
* the key type
*
* @see self::__toString()
* @access public
* @param string $format
*/
public function setKeyFormat($format)
{
$type = self::validatePlugin('Keys', $format);
if ($type === false) {
throw new FileNotFoundException('Plugin not found');
}
try {
$this->setPrivateKeyFormat($format);
} catch (\Exception $e) {
}
try {
$this->setPublicKeyFormat($format);
} catch (\Exception $e) {
}
}
/**
* Returns the format of the loaded key.
*
@ -583,19 +297,47 @@ abstract class AsymmetricKey
}
/**
* Sets the password
* Tests engine validity
*
* Private keys can be encrypted with a password. To unset the password, pass in the empty string or false.
* Or rather, pass in $password such that empty($password) && !is_string($password) is true.
*
* @see self::createKey()
* @see self::load()
* @access public
* @param string|boolean $password
* @param int $val
*/
public function setPassword($password = false)
public static function useBestEngine()
{
$this->password = $password;
static::$engines = [
'PHP' => true,
'OpenSSL' => extension_loaded('openssl'),
// this test can be satisfied by either of the following:
// http://php.net/manual/en/book.sodium.php
// https://github.com/paragonie/sodium_compat
'libsodium' => function_exists('sodium_crypto_sign_keypair')
];
return static::$engines;
}
/**
* Flag to use internal engine only (useful for unit testing)
*
* @access public
*/
public static function useInternalEngine()
{
static::$engines = [
'PHP' => true,
'OpenSSL' => false,
'libsodium' => false
];
}
/**
* __toString() magic method
*
* @return string
*/
public function __toString()
{
return $this->toString('PKCS8');
}
/**
@ -604,12 +346,14 @@ abstract class AsymmetricKey
* @access public
* @param string $hash
*/
public function setHash($hash)
public function withHash($hash)
{
$this->hash = new Hash($hash);
$this->hmac = new Hash($hash);
$new = clone $this;
$this->hashManuallySet = true;
$new->hash = new Hash($hash);
$new->hmac = new Hash($hash);
return $new;
}
/**

View File

@ -0,0 +1,65 @@
<?php
/**
* Fingerprint Trait for Public Keys
*
* PHP version 5
*
* @category Crypt
* @package Common
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt\Common;
use phpseclib\Crypt\Hash;
/**
* Fingerprint Trait for Private Keys
*
* @package Common
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
trait Fingerprint
{
/**
* Returns the public key's fingerprint
*
* The public key's fingerprint is returned, which is equivalent to running `ssh-keygen -lf rsa.pub`. If there is
* no public key currently loaded, false is returned.
* Example output (md5): "c1:b1:30:29:d7:b8:de:6c:97:77:10:d7:46:41:63:87" (as specified by RFC 4716)
*
* @access public
* @param string $algorithm The hashing algorithm to be used. Valid options are 'md5' and 'sha256'. False is returned
* for invalid values.
* @return mixed
*/
public function getFingerprint($algorithm = 'md5')
{
$type = self::validatePlugin('Keys', 'OpenSSH', 'getBinaryOutput');
if ($type === false) {
return false;
}
$status = $type::getBinaryOutput();
$type::setBinaryOutput(true);
$key = $this->toString('OpenSSH');
if ($key === false) {
return false;
}
$type::setBinaryOutput($status);
switch ($algorithm) {
case 'sha256':
$hash = new Hash('sha256');
$base = base64_encode($hash->hash($key));
return substr($base, 0, strlen($base) - 1);
case 'md5':
return substr(chunk_split(md5($key), 2, ':'), 0, -1);
default:
return false;
}
}
}

View File

@ -479,7 +479,7 @@ abstract class PKCS8 extends PKCS
}
if (isset($private['publicKey'])) {
if ($private['publicKey'][0] != "\0") {
throw new \UnexpectedValueException('The first byte of the public key should be null - not ' . bin2hex($val));
throw new \UnexpectedValueException('The first byte of the public key should be null - not ' . bin2hex($private['publicKey'][0]));
}
$private['publicKey'] = substr($private['publicKey'], 1);
}
@ -494,7 +494,7 @@ abstract class PKCS8 extends PKCS
if (is_array($public)) {
if ($public['publicKey'][0] != "\0") {
throw new \UnexpectedValueException('The first byte of the public key should be null - not ' . bin2hex($val));
throw new \UnexpectedValueException('The first byte of the public key should be null - not ' . bin2hex($public['publicKey'][0]));
}
if (is_array(static::OID_NAME)) {
if (!in_array($public['publicKeyAlgorithm']['algorithm'], static::OID_NAME)) {

View File

@ -0,0 +1,51 @@
<?php
/**
* Password Protected Trait for Private Keys
*
* PHP version 5
*
* @category Crypt
* @package Common
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt\Common;
/**
* Password Protected Trait for Private Keys
*
* @package Common
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
trait PasswordProtected
{
/**
* Password
*
* @var string|bool
*/
private $password = false;
/**
* Sets the password
*
* Private keys can be encrypted with a password. To unset the password, pass in the empty string or false.
* Or rather, pass in $password such that empty($password) && !is_string($password) is true.
*
* @see self::createKey()
* @see self::load()
* @access public
* @param string|boolean $password
*/
public function withPassword($password = false)
{
$new = clone $this;
$new->password = $password;
return $new;
}
}

View File

@ -0,0 +1,30 @@
<?php
/**
* PrivateKey interface
*
* @category Crypt
* @package Common
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2009 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt\Common;
/**
* PrivateKey interface
*
* @package Common
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
interface PrivateKey
{
public function sign($message);
//public function decrypt($ciphertext);
public function getPublicKey();
public function toString($type);
public function withPassword($string);
}

View File

@ -0,0 +1,29 @@
<?php
/**
* PublicKey interface
*
* @category Crypt
* @package Common
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2009 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt\Common;
/**
* PublicKey interface
*
* @package Common
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
interface PublicKey
{
public function verify($message, $signature);
//public function encrypt($plaintext);
public function toString($type);
public function getFingerprint($algorithm);
}

View File

@ -10,13 +10,14 @@
* <?php
* include 'vendor/autoload.php';
*
* extract(\phpseclib\Crypt\DSA::createKey());
* $private = \phpseclib\Crypt\DSA::createKey();
* $public = $private->getPublicKey();
*
* $plaintext = 'terrafrost';
*
* $signature = $privatekey->sign($plaintext, 'ASN1');
* $signature = $private->sign($plaintext);
*
* echo $publickey->verify($plaintext, $signature) ? 'verified' : 'unverified';
* echo $public->verify($plaintext, $signature) ? 'verified' : 'unverified';
* ?>
* </code>
*
@ -30,14 +31,11 @@
namespace phpseclib\Crypt;
use ParagonIE\ConstantTime\Base64;
use phpseclib\File\ASN1;
use phpseclib\Math\BigInteger;
use phpseclib\Crypt\Common\AsymmetricKey;
use phpseclib\Math\PrimeField;
use phpseclib\Crypt\ECDSA\Signature\ASN1 as ASN1Signature;
use phpseclib\Exception\UnsupportedOperationException;
use phpseclib\Exception\NoKeyLoadedException;
use phpseclib\Crypt\DSA\PrivateKey;
use phpseclib\Crypt\DSA\PublicKey;
use phpseclib\Crypt\DSA\Parameters;
use phpseclib\Math\BigInteger;
use phpseclib\Exception\InsufficientSetupException;
/**
@ -47,7 +45,7 @@ use phpseclib\Exception\InsufficientSetupException;
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class DSA extends AsymmetricKey
abstract class DSA extends AsymmetricKey
{
/**
* Algorithm Name
@ -63,7 +61,7 @@ class DSA extends AsymmetricKey
* @var \phpseclib\Math\BigInteger
* @access private
*/
private $p;
protected $p;
/**
* DSA Group Order q
@ -81,15 +79,7 @@ class DSA extends AsymmetricKey
* @var \phpseclib\Math\BigInteger
* @access private
*/
private $g;
/**
* DSA secret exponent x
*
* @var \phpseclib\Math\BigInteger
* @access private
*/
protected $x;
protected $g;
/**
* DSA public key value y
@ -97,7 +87,23 @@ class DSA extends AsymmetricKey
* @var \phpseclib\Math\BigInteger
* @access private
*/
private $y;
protected $y;
/**
* Signature Format
*
* @var string
* @access private
*/
protected $format;
/**
* Signature Format (Short)
*
* @var string
* @access private
*/
protected $shortFormat;
/**
* Create DSA parameters
@ -133,7 +139,7 @@ class DSA extends AsymmetricKey
case $L == 3072 && $N == 256:
break;
default:
return false;
throw new \InvalidArgumentException('Invalid values for N and L');
}
$two = new BigInteger(2);
@ -163,7 +169,7 @@ class DSA extends AsymmetricKey
$h = $h->add(self::$one);
}
$dsa = new DSA();
$dsa = new Parameters;
$dsa->p = $p;
$dsa->q = $q;
$dsa->g = $g;
@ -174,17 +180,14 @@ class DSA extends AsymmetricKey
/**
* Create public / private key pair.
*
* This method is a bit polymorphic. It can take a DSA object (eg. pre-loaded with parameters),
* L / N as two distinct parameters or no parameters (at which point L and N will be generated
* with this method)
* This method is a bit polymorphic. It can take a DSA/Parameters object, L / N as two distinct parameters or
* no parameters (at which point L and N will be generated with this method)
*
* Returns an array with the following two elements:
* - 'privatekey': The private key.
* - 'publickey': The public key.
* Returns the private key, from which the publickey can be extracted
*
* @param $args[]
* @access public
* @return array|DSA
* @return DSA\PrivateKey
*/
public static function createKey(...$args)
{
@ -195,22 +198,29 @@ class DSA extends AsymmetricKey
}
if (count($args) == 2 && is_int($args[0]) && is_int($args[1])) {
$private = self::createParameters($args[0], $args[1]);
} else if (count($args) == 1 && $args[0] instanceof DSA) {
$private = clone $args[0];
$params = self::createParameters($args[0], $args[1]);
} else if (count($args) == 1 && $args[0] instanceof Parameters) {
$params = $args[0];
} else if (!count($args)) {
$private = self::createParameters();
$params = self::createParameters();
} else {
throw new InsufficientSetupException('Valid parameters are either two integers (L and N), a single DSA object or no parameters at all.');
}
$private = new PrivateKey;
$private->p = $params->p;
$private->q = $params->q;
$private->g = $params->g;
$private->x = BigInteger::randomRange(self::$one, $private->q->subtract(self::$one));
$private->y = $private->g->powMod($private->x, $private->p);
$public = clone $private;
unset($public->x);
//$public = clone $private;
//unset($public->x);
return ['privatekey' => $private, 'publickey' => $public];
return $private
->withHash($params->hash->getHash())
->withSignatureFormat($params->shortFormat);
}
/**
@ -220,9 +230,10 @@ class DSA extends AsymmetricKey
* @return bool
* @access public
* @param string $key
* @param int|bool $type optional
* @param string $type optional
* @param string $password optional
*/
public function load($key, $type = false)
public static function load($key, $type = false, $password = false)
{
self::initialize_static_variables();
@ -230,55 +241,38 @@ class DSA extends AsymmetricKey
self::useBestEngine();
}
if ($key instanceof DSA) {
$this->privateKeyFormat = $key->privateKeyFormat;
$this->publicKeyFormat = $key->publicKeyFormat;
$this->format = $key->format;
$this->p = $key->p;
$this->q = $key->q;
$this->g = $key->g;
$this->x = $key->x;
$this->y = $key->y;
$this->parametersFormat = $key->parametersFormat;
return true;
$components = parent::load($key, $type, $password);
if (!isset($components['x']) && !isset($components['y'])) {
$new = new Parameters;
} else if (isset($components['x'])) {
$new = new PrivateKey;
$new->x = $components['x'];
} else {
$new = new PublicKey;
}
$components = parent::load($key, $type);
if ($components === false) {
$this->format = null;
$this->p = null;
$this->q = null;
$this->g = null;
$this->x = null;
$this->y = null;
return false;
}
if (isset($components['p'])) {
switch (true) {
case isset($this->p) && !$this->p->equals($components['p']):
case isset($this->q) && !$this->q->equals($components['q']):
case isset($this->g) && !$this->g->equals($components['g']):
$this->x = $this->y = null;
}
$this->p = $components['p'];
$this->q = $components['q'];
$this->g = $components['g'];
}
$this->x = isset($components['x']) ? $components['x'] : null;
$new->p = $components['p'];
$new->q = $components['q'];
$new->g = $components['g'];
if (isset($components['y'])) {
$this->y = $components['y'];
$new->y = $components['y'];
}
//} else if (isset($components['x'])) {
// $this->y = $this->g->powMod($this->x, $this->p);
//}
return true;
return $new;
}
/**
* Constructor
*
* PublicKey and PrivateKey objects can only be created from abstract RSA class
*/
protected function __construct()
{
$this->format = self::validatePlugin('Signature', 'ASN1');
$this->shortFormat = 'ASN1';
parent::__construct();
}
/**
@ -291,140 +285,7 @@ class DSA extends AsymmetricKey
*/
public function getLength()
{
return isset($this->p) ?
['L' => $this->p->getLength(), 'N' => $this->q->getLength()] :
['L' => 0, 'N' => 0];
}
/**
* Returns the private key
*
* PKCS1 DSA private keys contain x and y. PKCS8 DSA private keys just contain x
* but y can be derived from x.
*
* @see self::getPublicKey()
* @access public
* @param string $type optional
* @return mixed
*/
public function getPrivateKey($type = 'PKCS8')
{
$type = self::validatePlugin('Keys', $type, 'savePrivateKey');
if ($type === false) {
return false;
}
if (!isset($this->x)) {
return false;
}
if (!isset($this->y)) {
$this->y = $this->g->powMod($this->x, $this->p);
}
return $type::savePrivateKey($this->p, $this->q, $this->g, $this->y, $this->x, $this->password);
}
/**
* Is the key a private key?
*
* @access public
* @return bool
*/
public function isPrivateKey()
{
return isset($this->x);
}
/**
* Is the key a public key?
*
* @access public
* @return bool
*/
public function isPublicKey()
{
return isset($this->p);
}
/**
* Returns the public key
*
* If you do "openssl rsa -in private.rsa -pubout -outform PEM" you get a PKCS8 formatted key
* that contains a publicKeyAlgorithm AlgorithmIdentifier and a publicKey BIT STRING.
* An AlgorithmIdentifier contains an OID and a parameters field. With RSA public keys this
* parameters field is NULL. With DSA PKCS8 public keys it is not - it contains the p, q and g
* variables. The publicKey BIT STRING contains, simply, the y variable. This can be verified
* by getting a DSA PKCS8 public key:
*
* "openssl dsa -in private.dsa -pubout -outform PEM"
*
* ie. just swap out rsa with dsa in the rsa command above.
*
* A PKCS1 public key corresponds to the publicKey portion of the PKCS8 key. In the case of RSA
* the publicKey portion /is/ the key. In the case of DSA it is not. You cannot verify a signature
* without the parameters and the PKCS1 DSA public key format does not include the parameters.
*
* @see self::getPrivateKey()
* @access public
* @param string $type optional
* @return mixed
*/
public function getPublicKey($type = null)
{
$returnObj = false;
if ($type === null) {
$returnObj = true;
$type = 'PKCS8';
}
$type = self::validatePlugin('Keys', $type, 'savePublicKey');
if ($type === false) {
return false;
}
if (!isset($this->y)) {
if (!isset($this->x) || !isset($this->p)) {
return false;
}
$this->y = $this->g->powMod($this->x, $this->p);
}
$key = $type::savePublicKey($this->p, $this->q, $this->g, $this->y);
if (!$returnObj) {
return $key;
}
$public = clone $this;
$public->load($key, 'PKCS8');
return $public;
}
/**
* Returns the parameters
*
* A public / private key is only returned if the currently loaded "key" contains an x or y
* value.
*
* @see self::getPublicKey()
* @see self::getPrivateKey()
* @access public
* @param string $type optional
* @return mixed
*/
public function getParameters($type = 'PKCS1')
{
$type = self::validatePlugin('Keys', $type, 'saveParameters');
if ($type === false) {
return false;
}
if (!isset($this->p) || !isset($this->q) || !isset($this->g)) {
return false;
}
return $type::saveParameters($this->p, $this->q, $this->g);
return ['L' => $this->p->getLength(), 'N' => $this->q->getLength()];
}
/**
@ -442,145 +303,39 @@ class DSA extends AsymmetricKey
}
/**
* Create a signature
* Returns the parameters
*
* @see self::verify()
* A public / private key is only returned if the currently loaded "key" contains an x or y
* value.
*
* @see self::getPublicKey()
* @access public
* @param string $message
* @param string $format optional
* @param string $type optional
* @return mixed
*/
public function sign($message, $format = 'ASN1')
public function getParameters()
{
$shortFormat = $format;
$format = self::validatePlugin('Signature', $format);
if ($format === false) {
return false;
}
$type = self::validatePlugin('Keys', 'PKCS1', 'saveParameters');
if (empty($this->x)) {
if (empty($this->y)) {
throw new NoKeyLoadedException('No key has been loaded');
}
throw new UnsupportedOperationException('A public key cannot be used to sign data');
}
if (empty($this->p)) {
throw new InsufficientSetupException('DSA Prime P is not set');
}
if (self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods())) {
$signature = '';
$result = openssl_sign($message, $signature, $this->getPrivateKey(), $this->hash->getHash());
if ($result) {
if ($shortFormat == 'ASN1') {
return $signature;
}
extract(ASN1Signature::load($signature));
return $format::save($r, $s);
}
}
$h = $this->hash->hash($message);
$h = $this->bits2int($h);
while (true) {
$k = BigInteger::randomRange(self::$one, $this->q->subtract(self::$one));
$r = $this->g->powMod($k, $this->p);
list(, $r) = $r->divide($this->q);
if ($r->equals(self::$zero)) {
continue;
}
$kinv = $k->modInverse($this->q);
$temp = $h->add($this->x->multiply($r));
$temp = $kinv->multiply($temp);
list(, $s) = $temp->divide($this->q);
if (!$s->equals(self::$zero)) {
break;
}
}
// the following is an RFC6979 compliant implementation of deterministic DSA
// it's unused because it's mainly intended for use when a good CSPRNG isn't
// available. if phpseclib's CSPRNG isn't good then even key generation is
// suspect
/*
$h1 = $this->hash->hash($message);
$k = $this->computek($h1);
$r = $this->g->powMod($k, $this->p);
list(, $r) = $r->divide($this->q);
$kinv = $k->modInverse($this->q);
$h1 = $this->bits2int($h1);
$temp = $h1->add($this->x->multiply($r));
$temp = $kinv->multiply($temp);
list(, $s) = $temp->divide($this->q);
*/
return $format::save($r, $s);
$key = $type::saveParameters($this->p, $this->q, $this->g);
return DSA::load($key, 'PKCS1')
->withHash($this->hash->getHash())
->withSignatureFormat($this->shortFormat);
}
/**
* Verify a signature
* Determines the signature padding mode
*
* Valid values are: ASN1, SSH2, Raw
*
* @see self::verify()
* @access public
* @param string $message
* @param string $signature
* @param string $format optional
* @return mixed
* @param string $padding
*/
public function verify($message, $signature, $format = 'ASN1')
public function withSignatureFormat($format)
{
$format = self::validatePlugin('Signature', $format);
if ($format === false) {
return false;
}
$params = $format::load($signature);
if ($params === false || count($params) != 2) {
return false;
}
extract($params);
if (empty($this->y)) {
if (empty($this->x)) {
throw new NoKeyLoadedException('No key has been loaded');
}
throw new UnsupportedOperationException('A private key cannot be used to sign data');
}
if (empty($this->p)) {
throw new InsufficientSetupException('DSA Prime P is not set');
}
if (self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods())) {
$sig = $format != 'ASN1' ? ASN1Signature::save($r, $s) : $signature;
$result = openssl_verify($message, $sig, $this->getPublicKey(), $this->hash->getHash());
if ($result != -1) {
return (bool) $result;
}
}
$q_1 = $this->q->subtract(self::$one);
if (!$r->between(self::$one, $q_1) || !$s->between(self::$one, $q_1)) {
return false;
}
$w = $s->modInverse($this->q);
$h = $this->hash->hash($message);
$h = $this->bits2int($h);
list(, $u1) = $h->multiply($w)->divide($this->q);
list(, $u2) = $r->multiply($w)->divide($this->q);
$v1 = $this->g->powMod($u1, $this->p);
$v2 = $this->y->powMod($u2, $this->p);
list(, $v) = $v1->multiply($v2)->divide($this->p);
list(, $v) = $v->divide($this->q);
return $v->equals($r);
$new = clone $this;
$new->shortFormat = $format;
$new->format = self::validatePlugin('Signature', $format);
return $new;
}
}

View File

@ -0,0 +1,39 @@
<?php
/**
* DSA Parameters
*
* @category Crypt
* @package DSA
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt\DSA;
use phpseclib\Crypt\DSA;
/**
* DSA Parameters
*
* @package DSA
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class Parameters extends DSA
{
/**
* Returns the public key
*
* @param string $type
* @return string
*/
public function toString($type = 'PKCS1')
{
$type = self::validatePlugin('Keys', 'PKCS1', 'saveParameters');
return $type::saveParameters($this->p, $this->q, $this->g);
}
}

View File

@ -0,0 +1,159 @@
<?php
/**
* DSA Private Key
*
* @category Crypt
* @package DSA
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt\DSA;
use phpseclib\Crypt\DSA;
use phpseclib\Crypt\ECDSA\Signature\ASN1 as ASN1Signature;
use phpseclib\Math\BigInteger;
use phpseclib\Crypt\Common;
/**
* DSA Private Key
*
* @package DSA
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class PrivateKey extends DSA implements Common\PrivateKey
{
use Common\PasswordProtected;
/**
* DSA secret exponent x
*
* @var \phpseclib\Math\BigInteger
* @access private
*/
protected $x;
/**
* Returns the public key
*
* If you do "openssl rsa -in private.rsa -pubout -outform PEM" you get a PKCS8 formatted key
* that contains a publicKeyAlgorithm AlgorithmIdentifier and a publicKey BIT STRING.
* An AlgorithmIdentifier contains an OID and a parameters field. With RSA public keys this
* parameters field is NULL. With DSA PKCS8 public keys it is not - it contains the p, q and g
* variables. The publicKey BIT STRING contains, simply, the y variable. This can be verified
* by getting a DSA PKCS8 public key:
*
* "openssl dsa -in private.dsa -pubout -outform PEM"
*
* ie. just swap out rsa with dsa in the rsa command above.
*
* A PKCS1 public key corresponds to the publicKey portion of the PKCS8 key. In the case of RSA
* the publicKey portion /is/ the key. In the case of DSA it is not. You cannot verify a signature
* without the parameters and the PKCS1 DSA public key format does not include the parameters.
*
* @see self::getPrivateKey()
* @access public
* @param string $type optional
* @return mixed
*/
public function getPublicKey()
{
$type = self::validatePlugin('Keys', 'PKCS8', 'savePublicKey');
if (!isset($this->y)) {
$this->y = $this->g->powMod($this->x, $this->p);
}
$key = $type::savePublicKey($this->p, $this->q, $this->g, $this->y);
return DSA::load($key, 'PKCS8')
->withHash($this->hash->getHash())
->withSignatureFormat($this->shortFormat);
}
/**
* Create a signature
*
* @see self::verify()
* @access public
* @param string $message
* @return mixed
*/
public function sign($message)
{
$format = $this->format;
if (self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods())) {
$signature = '';
$result = openssl_sign($message, $signature, $this->toString('PKCS8'), $this->hash->getHash());
if ($result) {
if ($this->shortFormat == 'ASN1') {
return $signature;
}
extract(ASN1Signature::load($signature));
return $format::save($r, $s);
}
}
$h = $this->hash->hash($message);
$h = $this->bits2int($h);
while (true) {
$k = BigInteger::randomRange(self::$one, $this->q->subtract(self::$one));
$r = $this->g->powMod($k, $this->p);
list(, $r) = $r->divide($this->q);
if ($r->equals(self::$zero)) {
continue;
}
$kinv = $k->modInverse($this->q);
$temp = $h->add($this->x->multiply($r));
$temp = $kinv->multiply($temp);
list(, $s) = $temp->divide($this->q);
if (!$s->equals(self::$zero)) {
break;
}
}
// the following is an RFC6979 compliant implementation of deterministic DSA
// it's unused because it's mainly intended for use when a good CSPRNG isn't
// available. if phpseclib's CSPRNG isn't good then even key generation is
// suspect
/*
$h1 = $this->hash->hash($message);
$k = $this->computek($h1);
$r = $this->g->powMod($k, $this->p);
list(, $r) = $r->divide($this->q);
$kinv = $k->modInverse($this->q);
$h1 = $this->bits2int($h1);
$temp = $h1->add($this->x->multiply($r));
$temp = $kinv->multiply($temp);
list(, $s) = $temp->divide($this->q);
*/
return $format::save($r, $s);
}
/**
* Returns the private key
*
* @param string $type
* @return string
*/
public function toString($type)
{
$type = self::validatePlugin('Keys', $type, 'savePrivateKey');
if (!isset($this->y)) {
$this->y = $this->g->powMod($this->x, $this->p);
}
return $type::savePrivateKey($this->p, $this->q, $this->g, $this->y, $this->x, $this->password);
}
}

View File

@ -0,0 +1,91 @@
<?php
/**
* DSA Public Key
*
* @category Crypt
* @package DSA
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt\DSA;
use phpseclib\Crypt\DSA;
use phpseclib\Crypt\ECDSA\Signature\ASN1 as ASN1Signature;
use phpseclib\Crypt\Common;
/**
* DSA Public Key
*
* @package DSA
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class PublicKey extends DSA implements Common\PublicKey
{
use Common\Fingerprint;
/**
* Verify a signature
*
* @see self::verify()
* @access public
* @param string $message
* @param string $signature
* @param string $format optional
* @return mixed
*/
public function verify($message, $signature)
{
$format = $this->format;
$params = $format::load($signature);
if ($params === false || count($params) != 2) {
return false;
}
extract($params);
if (self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods())) {
$sig = $format != 'ASN1' ? ASN1Signature::save($r, $s) : $signature;
$result = openssl_verify($message, $sig, $this->toString('PKCS8'), $this->hash->getHash());
if ($result != -1) {
return (bool) $result;
}
}
$q_1 = $this->q->subtract(self::$one);
if (!$r->between(self::$one, $q_1) || !$s->between(self::$one, $q_1)) {
return false;
}
$w = $s->modInverse($this->q);
$h = $this->hash->hash($message);
$h = $this->bits2int($h);
list(, $u1) = $h->multiply($w)->divide($this->q);
list(, $u2) = $r->multiply($w)->divide($this->q);
$v1 = $this->g->powMod($u1, $this->p);
$v2 = $this->y->powMod($u2, $this->p);
list(, $v) = $v1->multiply($v2)->divide($this->p);
list(, $v) = $v->divide($this->q);
return $v->equals($r);
}
/**
* Returns the public key
*
* @param string $type
* @return string
*/
public function toString($type)
{
$type = self::validatePlugin('Keys', $type, 'savePublicKey');
return $type::savePublicKey($this->p, $this->q, $this->g, $this->y);
}
}

View File

@ -10,13 +10,14 @@
* <?php
* include 'vendor/autoload.php';
*
* extract(\phpseclib\Crypt\ECDSA::createKey());
* $private = \phpseclib\Crypt\ECDSA::createKey('secp256k1');
* $public = $private->getPublicKey();
*
* $plaintext = 'terrafrost';
*
* $signature = $privatekey->sign($plaintext, 'ASN1');
* $signature = $private->sign($plaintext);
*
* echo $publickey->verify($plaintext, $signature) ? 'verified' : 'unverified';
* echo $public->verify($plaintext, $signature) ? 'verified' : 'unverified';
* ?>
* </code>
*
@ -30,21 +31,18 @@
namespace phpseclib\Crypt;
use phpseclib\Math\BigInteger;
use phpseclib\Crypt\Common\AsymmetricKey;
use phpseclib\Exception\UnsupportedCurveException;
use phpseclib\Exception\UnsupportedOperationException;
use phpseclib\Exception\UnsupportedAlgorithmException;
use phpseclib\Exception\NoKeyLoadedException;
use phpseclib\Exception\InsufficientSetupException;
use phpseclib\File\ASN1;
use phpseclib\File\ASN1\Maps\ECParameters;
use phpseclib\Crypt\ECDSA\PrivateKey;
use phpseclib\Crypt\ECDSA\PublicKey;
use phpseclib\Crypt\ECDSA\Parameters;
use phpseclib\Crypt\ECDSA\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
use phpseclib\Crypt\ECDSA\Curves\Ed25519;
use phpseclib\Crypt\ECDSA\Curves\Ed448;
use phpseclib\Crypt\ECDSA\Keys\PKCS1;
use phpseclib\Crypt\ECDSA\Keys\PKCS8;
use phpseclib\Crypt\ECDSA\Signature\ASN1 as ASN1Signature;
use phpseclib\File\ASN1\Maps\ECParameters;
use phpseclib\File\ASN1;
use phpseclib\Exception\UnsupportedCurveException;
use phpseclib\Exception\UnsupportedAlgorithmException;
/**
* Pure-PHP implementation of ECDSA.
@ -53,7 +51,7 @@ use phpseclib\Crypt\ECDSA\Signature\ASN1 as ASN1Signature;
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class ECDSA extends AsymmetricKey
abstract class ECDSA extends AsymmetricKey
{
/**
* Algorithm Name
@ -63,30 +61,35 @@ class ECDSA extends AsymmetricKey
*/
const ALGORITHM = 'ECDSA';
/**
* Private Key dA
*
* sign() converts this to a BigInteger so one might wonder why this is a FiniteFieldInteger instead of
* a BigInteger. That's because a FiniteFieldInteger, when converted to a byte string, is null padded by
* a certain amount whereas a BigInteger isn't.
*
* @var object
*/
private $dA;
/**
* Public Key QA
*
* @var object[]
*/
private $QA;
protected $QA;
/**
* Curve
*
* @var \phpseclib\Crypt\ECDSA\BaseCurves\Base
*/
private $curve;
protected $curve;
/**
* Signature Format
*
* @var string
* @access private
*/
protected $format;
/**
* Signature Format (Short)
*
* @var string
* @access private
*/
protected $shortFormat;
/**
* Curve Name
@ -96,44 +99,18 @@ class ECDSA extends AsymmetricKey
private $curveName;
/**
* Curve Order
* Context
*
* Used for deterministic ECDSA
*
* @var \phpseclib\Math\BigInteger
* @var string
*/
protected $q;
/**
* Alias for the private key
*
* Used for deterministic ECDSA. AsymmetricKey expects $x. I don't like x because
* with x you have x * the base point yielding an (x, y)-coordinate that is the
* public key. But the x is different depending on which side of the equal sign
* you're on. It's less ambiguous if you do dA * base point = (x, y)-coordinate.
*
* @var \phpseclib\Math\BigInteger
*/
protected $x;
/**
* Alias for the private key
*
* Used for deterministic ECDSA. AsymmetricKey expects $x. I don't like x because
* with x you have x * the base point yielding an (x, y)-coordinate that is the
* public key. But the x is different depending on which side of the equal sign
* you're on. It's less ambiguous if you do dA * base point = (x, y)-coordinate.
*
* @var \phpseclib\Math\BigInteger
*/
private $context;
protected $context;
/**
* Create public / private key pair.
*
* @access public
* @param string $curve
* @return \phpseclib\Crypt\ECDSA[]
* @return \phpseclib\Crypt\ECDSA\PrivateKey
*/
public static function createKey($curve)
{
@ -143,51 +120,62 @@ class ECDSA extends AsymmetricKey
self::useBestEngine();
}
if (self::$engines['libsodium'] && $curve == 'Ed25519' && function_exists('sodium_crypto_sign_keypair')) {
$curve = strtolower($curve);
if (self::$engines['libsodium'] && $curve == 'ed25519' && function_exists('sodium_crypto_sign_keypair')) {
$kp = sodium_crypto_sign_keypair();
$privatekey = new static();
$privatekey->load(sodium_crypto_sign_secretkey($kp));
$privatekey = ECDSA::load(sodium_crypto_sign_secretkey($kp), 'libsodium');
//$publickey = ECDSA::load(sodium_crypto_sign_publickey($kp), 'libsodium');
$publickey = new static();
$publickey->load(sodium_crypto_sign_publickey($kp));
$privatekey->curveName = 'Ed25519';
//$publickey->curveName = $curve;
$publickey->curveName = $privatekey->curveName = $curve;
return compact('privatekey', 'publickey');
return $privatekey;
}
$privatekey = new static();
$privatekey = new PrivateKey;
$curveName = $curve;
$curve = '\phpseclib\Crypt\ECDSA\Curves\\' . $curve;
if (!class_exists($curve)) {
throw new UnsupportedCurveException('Named Curve of ' . $curve . ' is not supported');
throw new UnsupportedCurveException('Named Curve of ' . $curveName . ' is not supported');
}
$reflect = new \ReflectionClass($curve);
$curveName = $reflect->isFinal() ?
$reflect->getParentClass()->getShortName() :
$reflect->getShortName();
$curve = new $curve();
$privatekey->dA = $dA = $curve->createRandomMultiplier();
$privatekey->QA = $curve->multiplyPoint($curve->getBasePoint(), $dA);
$privatekey->curve = $curve;
$publickey = clone $privatekey;
unset($publickey->dA);
unset($publickey->x);
//$publickey = clone $privatekey;
//unset($publickey->dA);
//unset($publickey->x);
$publickey->curveName = $privatekey->curveName = $curveName;
$privatekey->curveName = $curveName;
//$publickey->curveName = $curveName;
return compact('privatekey', 'publickey');
if ($privatekey->curve instanceof TwistedEdwardsCurve) {
return $privatekey->withHash($curve::HASH);
}
return $privatekey;
}
/**
* Loads a public or private key
*
* Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed)
*
* @return bool
* @access public
* @param string $key
* @param int $type optional
* @param string $type optional
* @param string $password optional
*/
public function load($key, $type = false)
public static function load($key, $type = false, $password = false)
{
self::initialize_static_variables();
@ -195,54 +183,42 @@ class ECDSA extends AsymmetricKey
self::useBestEngine();
}
if ($key instanceof ECDSA) {
$this->privateKeyFormat = $key->privateKeyFormat;
$this->publicKeyFormat = $key->publicKeyFormat;
$this->format = $key->format;
$this->dA = isset($key->dA) ? $key->dA : null;
$this->QA = $key->QA;
$this->curve = $key->curve;
$this->parametersFormat = $key->parametersFormat;
$this->hash = $key->hash;
$components = parent::load($key, $type, $password);
parent::load($key, false);
return true;
if (!isset($components['dA']) && !isset($components['QA'])) {
$new = new Parameters;
$new->curve = $components['curve'];
return $new;
}
$components = parent::load($key, $type);
if ($components === false) {
$this->clearKey();
return false;
$new = isset($components['dA']) ?
new PrivateKey :
new PublicKey;
$new->curve = $components['curve'];
$new->QA = $components['QA'];
if (isset($components['dA'])) {
$new->dA = $components['dA'];
}
if ($components['curve'] instanceof Ed25519 && $this->hashManuallySet && $this->hash->getHash() != 'sha512') {
$this->clearKey();
throw new UnsupportedAlgorithmException('Ed25519 only supports sha512 as a hash');
}
if ($components['curve'] instanceof Ed448 && $this->hashManuallySet && $this->hash->getHash() != 'shake256-912') {
$this->clearKey();
throw new UnsupportedAlgorithmException('Ed448 only supports shake256 with a length of 114 bytes');
if ($new->curve instanceof TwistedEdwardsCurve) {
return $new->withHash($components['curve']::HASH);
}
$this->curve = $components['curve'];
$this->QA = $components['QA'];
$this->dA = isset($components['dA']) ? $components['dA'] : null;
return true;
return $new;
}
/**
* Removes a key
* Constructor
*
* @access private
* PublicKey and PrivateKey objects can only be created from abstract RSA class
*/
private function clearKey()
protected function __construct()
{
$this->format = null;
$this->dA = null;
$this->QA = null;
$this->curve = null;
$this->format = self::validatePlugin('Signature', 'ASN1');
$this->shortFormat = 'ASN1';
parent::__construct();
}
/**
@ -306,117 +282,9 @@ class ECDSA extends AsymmetricKey
*/
public function getLength()
{
if (!isset($this->QA)) {
return 0;
}
return $this->curve->getLength();
}
/**
* Is the key a private key?
*
* @access public
* @return bool
*/
public function isPrivateKey()
{
return isset($this->dA);
}
/**
* Is the key a public key?
*
* @access public
* @return bool
*/
public function isPublicKey()
{
return isset($this->QA);
}
/**
* Returns the private key
*
* @see self::getPublicKey()
* @access public
* @param string $type optional
* @return mixed
*/
public function getPrivateKey($type = 'PKCS8')
{
$type = self::validatePlugin('Keys', $type, 'savePrivateKey');
if ($type === false) {
return false;
}
if (!isset($this->dA)) {
return false;
}
return $type::savePrivateKey($this->dA, $this->curve, $this->QA, $this->password);
}
/**
* Returns the public key
*
* @see self::getPrivateKey()
* @access public
* @param string $type optional
* @return mixed
*/
public function getPublicKey($type = null)
{
$returnObj = false;
if ($type === null) {
$returnObj = true;
$type = 'PKCS8';
}
$type = self::validatePlugin('Keys', $type, 'savePublicKey');
if ($type === false) {
return false;
}
if (!isset($this->QA)) {
return false;
}
$key = $type::savePublicKey($this->curve, $this->QA);
if ($returnObj) {
$public = clone $this;
$public->load($key, 'PKCS8');
return $public;
}
return $key;
}
/**
* Returns the parameters
*
* A public / private key is only returned if the currently loaded "key" contains an x or y
* value.
*
* @see self::getPublicKey()
* @see self::getPrivateKey()
* @access public
* @param string $type optional
* @return mixed
*/
public function getParameters($type = 'PKCS1')
{
$type = self::validatePlugin('Keys', $type, 'saveParameters');
if ($type === false) {
return false;
}
if (!isset($this->curve) || $this->curve instanceof TwistedEdwardsCurve) {
return false;
}
return $type::saveParameters($this->curve);
}
/**
* Returns the current engine being used
*
@ -427,10 +295,6 @@ class ECDSA extends AsymmetricKey
*/
public function getEngine()
{
if (!isset($this->curve)) {
throw new InsufficientSetupException('getEngine should not be called until after a key has been loaded');
}
if ($this->curve instanceof TwistedEdwardsCurve) {
return $this->curve instanceof Ed25519 && self::$engines['libsodium'] && !isset($this->context) ?
'libsodium' : 'PHP';
@ -440,6 +304,41 @@ class ECDSA extends AsymmetricKey
'OpenSSL' : 'PHP';
}
/**
* Returns the parameters
*
* @see self::getPublicKey()
* @access public
* @param string $type optional
* @return mixed
*/
public function getParameters($type = 'PKCS1')
{
$type = self::validatePlugin('Keys', $type, 'saveParameters');
$key = $type::saveParameters($this->curve);
return ECDSA::load($key, 'PKCS1')
->withHash($this->hash->getHash())
->withSignatureFormat($this->shortFormat);
}
/**
* Determines the signature padding mode
*
* Valid values are: ASN1, SSH2, Raw
*
* @access public
* @param string $padding
*/
public function withSignatureFormat($format)
{
$new = clone $this;
$new->shortFormat = $format;
$new->format = self::validatePlugin('Signature', $format);
return $new;
}
/**
* Sets the context
*
@ -450,11 +349,16 @@ class ECDSA extends AsymmetricKey
* @access public
* @param string $context optional
*/
public function setContext($context = null)
public function withContext($context = null)
{
if (!$this->curve instanceof TwistedEdwardsCurve) {
throw new UnsupportedCurveException('Only Ed25519 and Ed448 support contexts');
}
$new = clone $this;
if (!isset($context)) {
$this->context = null;
return;
$new->context = null;
return $new;
}
if (!is_string($context)) {
throw new \InvalidArgumentException('setContext expects a string');
@ -462,7 +366,8 @@ class ECDSA extends AsymmetricKey
if (strlen($context) > 255) {
throw new \LengthException('The context is supposed to be, at most, 255 bytes long');
}
$this->context = $context;
$new->context = $context;
return $new;
}
/**
@ -471,7 +376,7 @@ class ECDSA extends AsymmetricKey
* @access public
* @param string $hash
*/
public function setHash($hash)
public function withHash($hash)
{
if ($this->curve instanceof Ed25519 && $hash != 'sha512') {
throw new UnsupportedAlgorithmException('Ed25519 only supports sha512 as a hash');
@ -480,279 +385,6 @@ class ECDSA extends AsymmetricKey
throw new UnsupportedAlgorithmException('Ed448 only supports shake256 with a length of 114 bytes');
}
parent::setHash($hash);
}
/**
* Create a signature
*
* @see self::verify()
* @access public
* @param string $message
* @param string $format optional
* @return mixed
*/
public function sign($message, $format = 'ASN1')
{
if (!isset($this->dA)) {
if (!isset($this->QA)) {
throw new NoKeyLoadedException('No key has been loaded');
}
throw new UnsupportedOperationException('A public key cannot be used to sign data');
}
$dA = $this->dA->toBigInteger();
$order = $this->curve->getOrder();
if ($this->curve instanceof TwistedEdwardsCurve) {
if ($this->curve instanceof Ed25519 && self::$engines['libsodium'] && !isset($this->context)) {
return sodium_crypto_sign_detached($message, $this->getPrivateKey('libsodium'));
}
// contexts (Ed25519ctx) are supported but prehashing (Ed25519ph) is not.
// quoting https://tools.ietf.org/html/rfc8032#section-8.5 ,
// "The Ed25519ph and Ed448ph variants ... SHOULD NOT be used"
$A = $this->curve->encodePoint($this->QA);
$curve = $this->curve;
$hash = new Hash($curve::HASH);
$secret = substr($hash->hash($this->dA->secret), $curve::SIZE);
if ($curve instanceof Ed25519) {
$dom = !isset($this->context) ? '' :
'SigEd25519 no Ed25519 collisions' . "\0" . chr(strlen($this->context)) . $this->context;
} else {
$context = isset($this->context) ? $this->context : '';
$dom = 'SigEd448' . "\0" . chr(strlen($context)) . $context;
}
// SHA-512(dom2(F, C) || prefix || PH(M))
$r = $hash->hash($dom . $secret . $message);
$r = strrev($r);
$r = new BigInteger($r, 256);
list(, $r) = $r->divide($order);
$R = $curve->multiplyPoint($curve->getBasePoint(), $curve->convertInteger($r));
$R = $curve->encodePoint($R);
$k = $hash->hash($dom . $R . $A . $message);
$k = strrev($k);
$k = new BigInteger($k, 256);
list(, $k) = $k->divide($order);
$S = $k->multiply($dA)->add($r);
list(, $S) = $S->divide($order);
$S = str_pad(strrev($S->toBytes()), $curve::SIZE, "\0");
return $R . $S;
}
$shortFormat = $format;
$format = self::validatePlugin('Signature', $format);
if ($format === false) {
return false;
}
if (self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods())) {
$namedCurves = PKCS8::isUsingNamedCurves();
// use specified curves to avoid issues with OpenSSL possibly not supporting a given named curve;
// doing this may mean some curve-specific optimizations can't be used but idk if OpenSSL even
// has curve-specific optimizations
PKCS8::useSpecifiedCurve();
$signature = '';
// altho PHP's OpenSSL bindings only supported ECDSA key creation in PHP 7.1 they've long
// supported signing / verification
$result = openssl_sign($message, $signature, $this->getPrivateKey(), $this->hash->getHash());
if ($namedCurves) {
PKCS8::useNamedCurve();
}
if ($result) {
if ($shortFormat == 'ASN1') {
return $signature;
}
extract(ASN1Signature::load($signature));
return $shortFormat == 'SSH2' ? $format::save($r, $s, $this->getCurve()) : $format::save($r, $s);
}
}
$e = $this->hash->hash($message);
$e = new BigInteger($e, 256);
$Ln = $this->hash->getLength() - $order->getLength();
$z = $Ln > 0 ? $e->bitwise_rightShift($Ln) : $e;
while (true) {
$k = BigInteger::randomRange(self::$one, $order->subtract(self::$one));
list($x, $y) = $this->curve->multiplyPoint($this->curve->getBasePoint(), $this->curve->convertInteger($k));
$x = $x->toBigInteger();
list(, $r) = $x->divide($order);
if ($r->equals(self::$zero)) {
continue;
}
$kinv = $k->modInverse($order);
$temp = $z->add($dA->multiply($r));
$temp = $kinv->multiply($temp);
list(, $s) = $temp->divide($order);
if (!$s->equals(self::$zero)) {
break;
}
}
// the following is an RFC6979 compliant implementation of deterministic ECDSA
// it's unused because it's mainly intended for use when a good CSPRNG isn't
// available. if phpseclib's CSPRNG isn't good then even key generation is
// suspect
/*
// if this were actually being used it'd probably be better if this lived in load() and createKey()
$this->q = $this->curve->getOrder();
$dA = $this->dA->toBigInteger();
$this->x = $dA;
$h1 = $this->hash->hash($message);
$k = $this->computek($h1);
list($x, $y) = $this->curve->multiplyPoint($this->curve->getBasePoint(), $this->curve->convertInteger($k));
$x = $x->toBigInteger();
list(, $r) = $x->divide($this->q);
$kinv = $k->modInverse($this->q);
$h1 = $this->bits2int($h1);
$temp = $h1->add($dA->multiply($r));
$temp = $kinv->multiply($temp);
list(, $s) = $temp->divide($this->q);
*/
return $shortFormat == 'SSH2' ? $format::save($r, $s, $this->getCurve()) : $format::save($r, $s);
}
/**
* Verify a signature
*
* @see self::verify()
* @access public
* @param string $message
* @param string $format optional
* @return mixed
*/
public function verify($message, $signature, $format = 'ASN1')
{
if (!isset($this->QA)) {
if (!isset($this->dA)) {
throw new NoKeyLoadedException('No key has been loaded');
}
throw new UnsupportedOperationException('A private key cannot be used to verify data');
}
$order = $this->curve->getOrder();
if ($this->curve instanceof TwistedEdwardsCurve) {
if ($this->curve instanceof Ed25519 && self::$engines['libsodium'] && !isset($this->context)) {
return sodium_crypto_sign_verify_detached($signature, $message, $this->getPublicKey('libsodium'));
}
$curve = $this->curve;
if (strlen($signature) != 2 * $curve::SIZE) {
return false;
}
$R = substr($signature, 0, $curve::SIZE);
$S = substr($signature, $curve::SIZE);
try {
$R = PKCS1::extractPoint($R, $curve);
$R = $this->curve->convertToInternal($R);
} catch (\Exception $e) {
return false;
}
$S = strrev($S);
$S = new BigInteger($S, 256);
if ($S->compare($order) >= 0) {
return false;
}
$A = $curve->encodePoint($this->QA);
if ($curve instanceof Ed25519) {
$dom2 = !isset($this->context) ? '' :
'SigEd25519 no Ed25519 collisions' . "\0" . chr(strlen($this->context)) . $this->context;
} else {
$context = isset($this->context) ? $this->context : '';
$dom2 = 'SigEd448' . "\0" . chr(strlen($context)) . $context;
}
$hash = new Hash($curve::HASH);
$k = $hash->hash($dom2 . substr($signature, 0, $curve::SIZE) . $A . $message);
$k = strrev($k);
$k = new BigInteger($k, 256);
list(, $k) = $k->divide($order);
$qa = $curve->convertToInternal($this->QA);
$lhs = $curve->multiplyPoint($curve->getBasePoint(), $curve->convertInteger($S));
$rhs = $curve->multiplyPoint($qa, $curve->convertInteger($k));
$rhs = $curve->addPoint($rhs, $R);
$rhs = $curve->convertToAffine($rhs);
return $lhs[0]->equals($rhs[0]) && $lhs[1]->equals($rhs[1]);
}
$format = self::validatePlugin('Signature', $format);
if ($format === false) {
return false;
}
$params = $format::load($signature);
if ($params === false || count($params) != 2) {
return false;
}
extract($params);
if (self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods())) {
$namedCurves = PKCS8::isUsingNamedCurves();
PKCS8::useSpecifiedCurve();
$sig = $format != 'ASN1' ? ASN1Signature::save($r, $s) : $signature;
$result = openssl_verify($message, $sig, $this->getPublicKey(), $this->hash->getHash());
if ($namedCurves) {
PKCS8::useNamedCurve();
}
if ($result != -1) {
return (bool) $result;
}
}
$n_1 = $order->subtract(self::$one);
if (!$r->between(self::$one, $n_1) || !$s->between(self::$one, $n_1)) {
return false;
}
$e = $this->hash->hash($message);
$e = new BigInteger($e, 256);
$Ln = $this->hash->getLength() - $order->getLength();
$z = $Ln > 0 ? $e->bitwise_rightShift($Ln) : $e;
$w = $s->modInverse($order);
list(, $u1) = $z->multiply($w)->divide($order);
list(, $u2) = $r->multiply($w)->divide($order);
$u1 = $this->curve->convertInteger($u1);
$u2 = $this->curve->convertInteger($u2);
list($x1, $y1) = $this->curve->multiplyAddPoints(
[$this->curve->getBasePoint(), $this->QA],
[$u1, $u2]
);
$x1 = $x1->toBigInteger();
list(, $x1) = $x1->divide($order);
return $x1->equals($r);
return parent::withHash($hash);
}
}

View File

@ -0,0 +1,39 @@
<?php
/**
* ECDSA Parameters
*
* @category Crypt
* @package ECDSA
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt\ECDSA;
use phpseclib\Crypt\ECDSA;
/**
* ECDSA Parameters
*
* @package ECDSA
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class Parameters extends ECDSA
{
/**
* Returns the public key
*
* @param string $type
* @return string
*/
public function toString($type = 'PKCS1')
{
$type = self::validatePlugin('Keys', 'PKCS1', 'saveParameters');
return $type::saveParameters($this->curve);
}
}

View File

@ -0,0 +1,214 @@
<?php
/**
* ECDSA Private Key
*
* @category Crypt
* @package ECDSA
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt\ECDSA;
use phpseclib\Crypt\ECDSA;
use phpseclib\Crypt\ECDSA\Signature\ASN1 as ASN1Signature;
use phpseclib\Math\BigInteger;
use phpseclib\Crypt\ECDSA\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
use phpseclib\Crypt\Hash;
use phpseclib\Crypt\ECDSA\Curves\Ed25519;
use phpseclib\Crypt\ECDSA\Keys\PKCS8;
use phpseclib\Crypt\Common;
/**
* ECDSA Private Key
*
* @package ECDSA
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class PrivateKey extends ECDSA implements Common\PrivateKey
{
use Common\PasswordProtected;
/**
* Private Key dA
*
* sign() converts this to a BigInteger so one might wonder why this is a FiniteFieldInteger instead of
* a BigInteger. That's because a FiniteFieldInteger, when converted to a byte string, is null padded by
* a certain amount whereas a BigInteger isn't.
*
* @var object
*/
protected $dA;
/**
* Create a signature
*
* @see self::verify()
* @access public
* @param string $message
* @return mixed
*/
public function sign($message)
{
$dA = $this->dA->toBigInteger();
$order = $this->curve->getOrder();
if ($this->curve instanceof TwistedEdwardsCurve) {
if ($this->curve instanceof Ed25519 && self::$engines['libsodium'] && !isset($this->context)) {
return sodium_crypto_sign_detached($message, $this->toString('libsodium'));
}
// contexts (Ed25519ctx) are supported but prehashing (Ed25519ph) is not.
// quoting https://tools.ietf.org/html/rfc8032#section-8.5 ,
// "The Ed25519ph and Ed448ph variants ... SHOULD NOT be used"
$A = $this->curve->encodePoint($this->QA);
$curve = $this->curve;
$hash = new Hash($curve::HASH);
$secret = substr($hash->hash($this->dA->secret), $curve::SIZE);
if ($curve instanceof Ed25519) {
$dom = !isset($this->context) ? '' :
'SigEd25519 no Ed25519 collisions' . "\0" . chr(strlen($this->context)) . $this->context;
} else {
$context = isset($this->context) ? $this->context : '';
$dom = 'SigEd448' . "\0" . chr(strlen($context)) . $context;
}
// SHA-512(dom2(F, C) || prefix || PH(M))
$r = $hash->hash($dom . $secret . $message);
$r = strrev($r);
$r = new BigInteger($r, 256);
list(, $r) = $r->divide($order);
$R = $curve->multiplyPoint($curve->getBasePoint(), $curve->convertInteger($r));
$R = $curve->encodePoint($R);
$k = $hash->hash($dom . $R . $A . $message);
$k = strrev($k);
$k = new BigInteger($k, 256);
list(, $k) = $k->divide($order);
$S = $k->multiply($dA)->add($r);
list(, $S) = $S->divide($order);
$S = str_pad(strrev($S->toBytes()), $curve::SIZE, "\0");
return $R . $S;
}
$shortFormat = $this->shortFormat;
$format = $this->format;
if ($format === false) {
return false;
}
if (self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods())) {
$namedCurves = PKCS8::isUsingNamedCurves();
// use specified curves to avoid issues with OpenSSL possibly not supporting a given named curve;
// doing this may mean some curve-specific optimizations can't be used but idk if OpenSSL even
// has curve-specific optimizations
PKCS8::useSpecifiedCurve();
$signature = '';
// altho PHP's OpenSSL bindings only supported ECDSA key creation in PHP 7.1 they've long
// supported signing / verification
$result = openssl_sign($message, $signature, $this->toString('PKCS8'), $this->hash->getHash());
if ($namedCurves) {
PKCS8::useNamedCurve();
}
if ($result) {
if ($shortFormat == 'ASN1') {
return $signature;
}
extract(ASN1Signature::load($signature));
return $shortFormat == 'SSH2' ? $format::save($r, $s, $this->getCurve()) : $format::save($r, $s);
}
}
$e = $this->hash->hash($message);
$e = new BigInteger($e, 256);
$Ln = $this->hash->getLength() - $order->getLength();
$z = $Ln > 0 ? $e->bitwise_rightShift($Ln) : $e;
while (true) {
$k = BigInteger::randomRange(self::$one, $order->subtract(self::$one));
list($x, $y) = $this->curve->multiplyPoint($this->curve->getBasePoint(), $this->curve->convertInteger($k));
$x = $x->toBigInteger();
list(, $r) = $x->divide($order);
if ($r->equals(self::$zero)) {
continue;
}
$kinv = $k->modInverse($order);
$temp = $z->add($dA->multiply($r));
$temp = $kinv->multiply($temp);
list(, $s) = $temp->divide($order);
if (!$s->equals(self::$zero)) {
break;
}
}
// the following is an RFC6979 compliant implementation of deterministic ECDSA
// it's unused because it's mainly intended for use when a good CSPRNG isn't
// available. if phpseclib's CSPRNG isn't good then even key generation is
// suspect
/*
// if this were actually being used it'd probably be better if this lived in load() and createKey()
$this->q = $this->curve->getOrder();
$dA = $this->dA->toBigInteger();
$this->x = $dA;
$h1 = $this->hash->hash($message);
$k = $this->computek($h1);
list($x, $y) = $this->curve->multiplyPoint($this->curve->getBasePoint(), $this->curve->convertInteger($k));
$x = $x->toBigInteger();
list(, $r) = $x->divide($this->q);
$kinv = $k->modInverse($this->q);
$h1 = $this->bits2int($h1);
$temp = $h1->add($dA->multiply($r));
$temp = $kinv->multiply($temp);
list(, $s) = $temp->divide($this->q);
*/
return $shortFormat == 'SSH2' ? $format::save($r, $s, $this->getCurve()) : $format::save($r, $s);
}
/**
* Returns the private key
*
* @param string $type
* @return string
*/
public function toString($type)
{
$type = self::validatePlugin('Keys', $type, 'savePrivateKey');
return $type::savePrivateKey($this->dA, $this->curve, $this->QA, $this->password);
}
/**
* Returns the public key
*
* @see self::getPrivateKey()
* @access public
* @return mixed
*/
public function getPublicKey()
{
$type = self::validatePlugin('Keys', 'PKCS8', 'savePublicKey');
$key = $type::savePublicKey($this->curve, $this->QA);
$key = ECDSA::load($key, 'PKCS8')
->withHash($this->hash->getHash())
->withSignatureFormat($this->shortFormat);
if ($this->curve instanceof TwistedEdwardsCurve) {
$key = $key->withContext($this->context);
}
return $key;
}
}

View File

@ -0,0 +1,170 @@
<?php
/**
* ECDSA Public Key
*
* @category Crypt
* @package ECDSA
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt\ECDSA;
use phpseclib\Crypt\ECDSA;
use phpseclib\Crypt\Hash;
use phpseclib\Math\BigInteger;
use phpseclib\Crypt\ECDSA\Signature\ASN1 as ASN1Signature;
use phpseclib\Crypt\ECDSA\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
use phpseclib\Crypt\ECDSA\Curves\Ed25519;
use phpseclib\Crypt\ECDSA\Keys\PKCS1;
use phpseclib\Crypt\ECDSA\Keys\PKCS8;
use phpseclib\Crypt\Common;
/**
* ECDSA Public Key
*
* @package ECDSA
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class PublicKey extends ECDSA implements Common\PublicKey
{
use Common\Fingerprint;
/**
* Verify a signature
*
* @see self::verify()
* @access public
* @param string $message
* @param string $signature
* @return mixed
*/
public function verify($message, $signature)
{
$order = $this->curve->getOrder();
if ($this->curve instanceof TwistedEdwardsCurve) {
if ($this->curve instanceof Ed25519 && self::$engines['libsodium'] && !isset($this->context)) {
return sodium_crypto_sign_verify_detached($signature, $message, $this->toString('libsodium'));
}
$curve = $this->curve;
if (strlen($signature) != 2 * $curve::SIZE) {
return false;
}
$R = substr($signature, 0, $curve::SIZE);
$S = substr($signature, $curve::SIZE);
try {
$R = PKCS1::extractPoint($R, $curve);
$R = $this->curve->convertToInternal($R);
} catch (\Exception $e) {
return false;
}
$S = strrev($S);
$S = new BigInteger($S, 256);
if ($S->compare($order) >= 0) {
return false;
}
$A = $curve->encodePoint($this->QA);
if ($curve instanceof Ed25519) {
$dom2 = !isset($this->context) ? '' :
'SigEd25519 no Ed25519 collisions' . "\0" . chr(strlen($this->context)) . $this->context;
} else {
$context = isset($this->context) ? $this->context : '';
$dom2 = 'SigEd448' . "\0" . chr(strlen($context)) . $context;
}
$hash = new Hash($curve::HASH);
$k = $hash->hash($dom2 . substr($signature, 0, $curve::SIZE) . $A . $message);
$k = strrev($k);
$k = new BigInteger($k, 256);
list(, $k) = $k->divide($order);
$qa = $curve->convertToInternal($this->QA);
$lhs = $curve->multiplyPoint($curve->getBasePoint(), $curve->convertInteger($S));
$rhs = $curve->multiplyPoint($qa, $curve->convertInteger($k));
$rhs = $curve->addPoint($rhs, $R);
$rhs = $curve->convertToAffine($rhs);
return $lhs[0]->equals($rhs[0]) && $lhs[1]->equals($rhs[1]);
}
$format = $this->format;
$params = $format::load($signature);
if ($params === false || count($params) != 2) {
return false;
}
extract($params);
if (self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods())) {
$namedCurves = PKCS8::isUsingNamedCurves();
PKCS8::useSpecifiedCurve();
$sig = $format != 'ASN1' ? ASN1Signature::save($r, $s) : $signature;
$result = openssl_verify($message, $sig, $this->toString('PKCS8'), $this->hash->getHash());
if ($namedCurves) {
PKCS8::useNamedCurve();
}
if ($result != -1) {
return (bool) $result;
}
}
$n_1 = $order->subtract(self::$one);
if (!$r->between(self::$one, $n_1) || !$s->between(self::$one, $n_1)) {
return false;
}
$e = $this->hash->hash($message);
$e = new BigInteger($e, 256);
$Ln = $this->hash->getLength() - $order->getLength();
$z = $Ln > 0 ? $e->bitwise_rightShift($Ln) : $e;
$w = $s->modInverse($order);
list(, $u1) = $z->multiply($w)->divide($order);
list(, $u2) = $r->multiply($w)->divide($order);
$u1 = $this->curve->convertInteger($u1);
$u2 = $this->curve->convertInteger($u2);
list($x1, $y1) = $this->curve->multiplyAddPoints(
[$this->curve->getBasePoint(), $this->QA],
[$u1, $u2]
);
$x1 = $x1->toBigInteger();
list(, $x1) = $x1->divide($order);
return $x1->equals($r);
}
/**
* Returns the public key
*
* @param string $type
* @return string
*/
public function toString($type)
{
$type = self::validatePlugin('Keys', $type, 'savePublicKey');
return $type::savePublicKey($this->curve, $this->QA);
}
}

View File

@ -0,0 +1,74 @@
<?php
/**
* PublicKeyLoader
*
* Returns a PublicKey or PrivateKey object.
*
* @category Crypt
* @package PublicKeyLoader
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2009 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt;
use phpseclib\Exception\NoKeyLoadedException;
use phpseclib\Crypt\Common\PrivateKey;
use phpseclib\File\X509;
/**
* PublicKeyLoader
*
* @package Common
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
abstract class PublicKeyLoader
{
/**
* Loads a public or private key
*
* @return AsymmetricKey
* @access public
* @param string $key
* @param string $password optional
*/
public static function load($key, $password = false)
{
try {
$new = ECDSA::load($key, false, $password);
} catch (\Exception $e) {}
if (!isset($new)) {
try {
$new = RSA::load($key, false, $password);
} catch (\Exception $e) {}
}
if (!isset($new)) {
try {
$new = DSA::load($key, false, $password);
} catch (\Exception $e) {}
}
if (isset($new)) {
return $new instanceof PrivateKey ?
$new->withPassword($password) :
$new;
}
try {
$x509 = new X509();
$x509->loadX509($key);
$key = $x509->getPublicKey();
if ($key) {
return $key;
}
} catch (\Exception $e) {}
throw new NoKeyLoadedException('Unable to read key');
}
}

File diff suppressed because it is too large Load Diff

View File

@ -102,7 +102,16 @@ abstract class XML
libxml_use_internal_errors($use_errors);
foreach ($components as $key => $value) {
if (is_array($value) && !count($value)) {
unset($components[$key]);
}
}
if (isset($components['modulus']) && isset($components['publicExponent'])) {
if (count($components) == 3) {
$components['isPublicKey'] = true;
}
return $components;
}

View File

@ -0,0 +1,561 @@
<?php
/**
* RSA Private Key
*
* @category Crypt
* @package RSA
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt\RSA;
use phpseclib\Crypt\RSA;
use phpseclib\Math\BigInteger;
use phpseclib\File\ASN1;
use phpseclib\Common\Functions\Strings;
use phpseclib\Crypt\Hash;
use phpseclib\Exceptions\NoKeyLoadedException;
use phpseclib\Exception\UnsupportedFormatException;
use phpseclib\Crypt\Random;
use phpseclib\Crypt\Common;
/**
* Raw RSA Key Handler
*
* @package RSA
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class PrivateKey extends RSA implements Common\PrivateKey
{
use Common\PasswordProtected;
/**
* Primes for Chinese Remainder Theorem (ie. p and q)
*
* @var array
* @access private
*/
protected $primes;
/**
* Exponents for Chinese Remainder Theorem (ie. dP and dQ)
*
* @var array
* @access private
*/
protected $exponents;
/**
* Coefficients for Chinese Remainder Theorem (ie. qInv)
*
* @var array
* @access private
*/
protected $coefficients;
/**
* Public Exponent
*
* @var mixed
* @access private
*/
protected $publicExponent = false;
/**
* RSADP
*
* See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}.
*
* @access private
* @param \phpseclib\Math\BigInteger $c
* @return bool|\phpseclib\Math\BigInteger
*/
private function rsadp($c)
{
if ($c->compare(self::$zero) < 0 || $c->compare($this->modulus) > 0) {
return false;
}
return $this->exponentiate($c);
}
/**
* RSASP1
*
* See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}.
*
* @access private
* @param \phpseclib\Math\BigInteger $m
* @return bool|\phpseclib\Math\BigInteger
*/
private function rsasp1($m)
{
if ($m->compare(self::$zero) < 0 || $m->compare($this->modulus) > 0) {
return false;
}
return $this->exponentiate($m);
}
/**
* Exponentiate
*
* @param \phpseclib\Math\BigInteger $x
* @return \phpseclib\Math\BigInteger
*/
protected function exponentiate(BigInteger $x)
{
switch (true) {
case empty($this->primes):
case $this->primes[1]->equals(self::$zero):
case empty($this->coefficients):
case $this->coefficients[2]->equals(self::$zero):
case empty($this->exponents):
case $this->exponents[1]->equals(self::$zero):
return $x->modPow($this->exponent, $this->modulus);
}
$num_primes = count($this->primes);
if (!static::$enableBlinding) {
$m_i = [
1 => $x->modPow($this->exponents[1], $this->primes[1]),
2 => $x->modPow($this->exponents[2], $this->primes[2])
];
$h = $m_i[1]->subtract($m_i[2]);
$h = $h->multiply($this->coefficients[2]);
list(, $h) = $h->divide($this->primes[1]);
$m = $m_i[2]->add($h->multiply($this->primes[2]));
$r = $this->primes[1];
for ($i = 3; $i <= $num_primes; $i++) {
$m_i = $x->modPow($this->exponents[$i], $this->primes[$i]);
$r = $r->multiply($this->primes[$i - 1]);
$h = $m_i->subtract($m);
$h = $h->multiply($this->coefficients[$i]);
list(, $h) = $h->divide($this->primes[$i]);
$m = $m->add($r->multiply($h));
}
} else {
$smallest = $this->primes[1];
for ($i = 2; $i <= $num_primes; $i++) {
if ($smallest->compare($this->primes[$i]) > 0) {
$smallest = $this->primes[$i];
}
}
$r = BigInteger::randomRange(self::$one, $smallest->subtract(self::$one));
$m_i = [
1 => $this->blind($x, $r, 1),
2 => $this->blind($x, $r, 2)
];
$h = $m_i[1]->subtract($m_i[2]);
$h = $h->multiply($this->coefficients[2]);
list(, $h) = $h->divide($this->primes[1]);
$m = $m_i[2]->add($h->multiply($this->primes[2]));
$r = $this->primes[1];
for ($i = 3; $i <= $num_primes; $i++) {
$m_i = $this->blind($x, $r, $i);
$r = $r->multiply($this->primes[$i - 1]);
$h = $m_i->subtract($m);
$h = $h->multiply($this->coefficients[$i]);
list(, $h) = $h->divide($this->primes[$i]);
$m = $m->add($r->multiply($h));
}
}
return $m;
}
/**
* Performs RSA Blinding
*
* Protects against timing attacks by employing RSA Blinding.
* Returns $x->modPow($this->exponents[$i], $this->primes[$i])
*
* @access private
* @param \phpseclib\Math\BigInteger $x
* @param \phpseclib\Math\BigInteger $r
* @param int $i
* @return \phpseclib\Math\BigInteger
*/
private function blind($x, $r, $i)
{
$x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i]));
$x = $x->modPow($this->exponents[$i], $this->primes[$i]);
$r = $r->modInverse($this->primes[$i]);
$x = $x->multiply($r);
list(, $x) = $x->divide($this->primes[$i]);
return $x;
}
/**
* EMSA-PSS-ENCODE
*
* See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}.
*
* @return string
* @access private
* @param string $m
* @throws \RuntimeException on encoding error
* @param int $emBits
*/
private function emsa_pss_encode($m, $emBits)
{
// if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
// be output.
$emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8)
$sLen = $this->sLen !== null ? $this->sLen : $this->hLen;
$mHash = $this->hash->hash($m);
if ($emLen < $this->hLen + $sLen + 2) {
return false;
}
$salt = Random::string($sLen);
$m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
$h = $this->hash->hash($m2);
$ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2);
$db = $ps . chr(1) . $salt;
$dbMask = $this->mgf1($h, $emLen - $this->hLen - 1);
$maskedDB = $db ^ $dbMask;
$maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0];
$em = $maskedDB . $h . chr(0xBC);
return $em;
}
/**
* RSASSA-PSS-SIGN
*
* See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}.
*
* @access private
* @param string $m
* @return bool|string
*/
private function rsassa_pss_sign($m)
{
// EMSA-PSS encoding
$em = $this->emsa_pss_encode($m, 8 * $this->k - 1);
// RSA signature
$m = $this->os2ip($em);
$s = $this->rsasp1($m);
$s = $this->i2osp($s, $this->k);
// Output the signature S
return $s;
}
/**
* RSASSA-PKCS1-V1_5-SIGN
*
* See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}.
*
* @access private
* @param string $m
* @throws \LengthException if the RSA modulus is too short
* @return bool|string
*/
private function rsassa_pkcs1_v1_5_sign($m)
{
// EMSA-PKCS1-v1_5 encoding
// If the encoding operation outputs "intended encoded message length too short," output "RSA modulus
// too short" and stop.
try {
$em = $this->emsa_pkcs1_v1_5_encode($m, $this->k);
} catch (\LengthException $e) {
throw new \LengthException('RSA modulus too short');
}
// RSA signature
$m = $this->os2ip($em);
$s = $this->rsasp1($m);
$s = $this->i2osp($s, $this->k);
// Output the signature S
return $s;
}
/**
* Create a signature
*
* @see self::verify()
* @access public
* @param string $message
* @return string
*/
public function sign($message)
{
switch ($this->signaturePadding) {
case self::SIGNATURE_PKCS1:
case self::SIGNATURE_RELAXED_PKCS1:
return $this->rsassa_pkcs1_v1_5_sign($message);
//case self::SIGNATURE_PSS:
default:
return $this->rsassa_pss_sign($message);
}
}
/**
* RSAES-PKCS1-V1_5-DECRYPT
*
* See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}.
*
* For compatibility purposes, this function departs slightly from the description given in RFC3447.
* The reason being that RFC2313#section-8.1 (PKCS#1 v1.5) states that ciphertext's encrypted by the
* private key should have the second byte set to either 0 or 1 and that ciphertext's encrypted by the
* public key should have the second byte set to 2. In RFC3447 (PKCS#1 v2.1), the second byte is supposed
* to be 2 regardless of which key is used. For compatibility purposes, we'll just check to make sure the
* second byte is 2 or less. If it is, we'll accept the decrypted string as valid.
*
* As a consequence of this, a private key encrypted ciphertext produced with \phpseclib\Crypt\RSA may not decrypt
* with a strictly PKCS#1 v1.5 compliant RSA implementation. Public key encrypted ciphertext's should but
* not private key encrypted ciphertext's.
*
* @access private
* @param string $c
* @return bool|string
*/
private function rsaes_pkcs1_v1_5_decrypt($c)
{
// Length checking
if (strlen($c) != $this->k) { // or if k < 11
return false;
}
// RSA decryption
$c = $this->os2ip($c);
$m = $this->rsadp($c);
$em = $this->i2osp($m, $this->k);
if ($em === false) {
return false;
}
// EME-PKCS1-v1_5 decoding
if (ord($em[0]) != 0 || ord($em[1]) > 2) {
return false;
}
$ps = substr($em, 2, strpos($em, chr(0), 2) - 2);
$m = substr($em, strlen($ps) + 3);
if (strlen($ps) < 8) {
return false;
}
// Output M
return $m;
}
/**
* RSAES-OAEP-DECRYPT
*
* See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}. The fact that the error
* messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2:
*
* Note. Care must be taken to ensure that an opponent cannot
* distinguish the different error conditions in Step 3.g, whether by
* error message or timing, or, more generally, learn partial
* information about the encoded message EM. Otherwise an opponent may
* be able to obtain useful information about the decryption of the
* ciphertext C, leading to a chosen-ciphertext attack such as the one
* observed by Manger [36].
*
* @access private
* @param string $c
* @return bool|string
*/
private function rsaes_oaep_decrypt($c)
{
// Length checking
// if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
// be output.
if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) {
return false;
}
// RSA decryption
$c = $this->os2ip($c);
$m = $this->rsadp($c);
$em = $this->i2osp($m, $this->k);
if ($em === false) {
return false;
}
// EME-OAEP decoding
$lHash = $this->hash->hash($this->label);
$y = ord($em[0]);
$maskedSeed = substr($em, 1, $this->hLen);
$maskedDB = substr($em, $this->hLen + 1);
$seedMask = $this->mgf1($maskedDB, $this->hLen);
$seed = $maskedSeed ^ $seedMask;
$dbMask = $this->mgf1($seed, $this->k - $this->hLen - 1);
$db = $maskedDB ^ $dbMask;
$lHash2 = substr($db, 0, $this->hLen);
$m = substr($db, $this->hLen);
$hashesMatch = hash_equals($lHash, $lHash2);
$leadingZeros = 1;
$patternMatch = 0;
$offset = 0;
for ($i = 0; $i < strlen($m); $i++) {
$patternMatch|= $leadingZeros & ($m[$i] === "\1");
$leadingZeros&= $m[$i] === "\0";
$offset+= $patternMatch ? 0 : 1;
}
// we do & instead of && to avoid https://en.wikipedia.org/wiki/Short-circuit_evaluation
// to protect against timing attacks
if (!$hashesMatch & !$patternMatch) {
return false;
}
// Output the message M
return substr($m, $offset + 1);
}
/**
* Raw Encryption / Decryption
*
* Doesn't use padding and is not recommended.
*
* @access private
* @param string $m
* @return bool|string
* @throws \LengthException if strlen($m) > $this->k
*/
private function raw_encrypt($m)
{
if (strlen($m) > $this->k) {
throw new \LengthException('Message too long');
}
$temp = $this->os2ip($m);
$temp = $this->rsadp($temp);
return $this->i2osp($temp, $this->k);
}
/**
* Decryption
*
* @see self::encrypt()
* @access public
* @param string $ciphertext
* @param int $padding optional
* @return bool|string
*/
public function decrypt($ciphertext)
{
switch ($this->encryptionPadding) {
case self::ENCRYPTION_NONE:
return $this->raw_encrypt($ciphertext);
case self::ENCRYPTION_PKCS1:
return $this->rsaes_pkcs1_v1_5_decrypt($ciphertext);
//case self::ENCRYPTION_OAEP:
default:
return $this->rsaes_oaep_decrypt($ciphertext);
}
}
/**
* Returns the public key
*
* @access public
* @param string $type optional
* @return mixed
*/
public function getPublicKey()
{
$type = self::validatePlugin('Keys', 'PKCS8', 'savePublicKey');
if (empty($this->modulus) || empty($this->publicExponent)) {
return false;
}
$key = $type::savePublicKey($this->modulus, $this->publicExponent);
return RSA::load($key, 'PKCS8')
->withHash($this->hash->getHash())
->withMGFHash($this->mgfHash->getHash())
->withSaltLength($this->sLen)
->withLabel($this->label)
->withPadding($this->signaturePadding | $this->encryptionPadding);
}
/**
* Returns the private key
*
* @param string $type
* @return string
*/
public function toString($type)
{
$type = self::validatePlugin(
'Keys',
$type,
empty($this->primes) ? 'savePublicKey' : 'savePrivateKey'
);
if (empty($this->primes)) {
return $type::savePublicKey($this->modulus, $this->exponent);
}
return $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients, $this->password);
/*
$key = $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients, $this->password);
if ($key !== false || count($this->primes) == 2) {
return $key;
}
$nSize = $this->getSize() >> 1;
$primes = [1 => clone self::$one, clone self::$one];
$i = 1;
foreach ($this->primes as $prime) {
$primes[$i] = $primes[$i]->multiply($prime);
if ($primes[$i]->getLength() >= $nSize) {
$i++;
}
}
$exponents = [];
$coefficients = [2 => $primes[2]->modInverse($primes[1])];
foreach ($primes as $i => $prime) {
$temp = $prime->subtract(self::$one);
$exponents[$i] = $this->modulus->modInverse($temp);
}
return $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $primes, $exponents, $coefficients, $this->password);
*/
}
}

View File

@ -0,0 +1,496 @@
<?php
/**
* RSA Public Key
*
* @category Crypt
* @package RSA
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt\RSA;
use phpseclib\Crypt\RSA;
use phpseclib\Math\BigInteger;
use phpseclib\File\ASN1;
use phpseclib\Common\Functions\Strings;
use phpseclib\Crypt\Hash;
use phpseclib\Exceptions\NoKeyLoadedException;
use phpseclib\Crypt\Random;
use phpseclib\Crypt\Common;
use phpseclib\File\ASN1\Maps\DigestInfo;
/**
* Raw RSA Key Handler
*
* @package RSA
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class PublicKey extends RSA implements Common\PublicKey
{
use Common\Fingerprint;
/**
* Exponentiate
*
* @param \phpseclib\Math\BigInteger $x
* @return \phpseclib\Math\BigInteger
*/
private function exponentiate(BigInteger $x)
{
return $x->modPow($this->exponent, $this->modulus);
}
/**
* RSAVP1
*
* See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}.
*
* @access private
* @param \phpseclib\Math\BigInteger $s
* @return bool|\phpseclib\Math\BigInteger
*/
private function rsavp1($s)
{
if ($s->compare(self::$zero) < 0 || $s->compare($this->modulus) > 0) {
return false;
}
return $this->exponentiate($s);
}
/**
* RSASSA-PKCS1-V1_5-VERIFY
*
* See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}.
*
* @access private
* @param string $m
* @param string $s
* @throws \LengthException if the RSA modulus is too short
* @return bool
*/
private function rsassa_pkcs1_v1_5_verify($m, $s)
{
// Length checking
if (strlen($s) != $this->k) {
return false;
}
// RSA verification
$s = $this->os2ip($s);
$m2 = $this->rsavp1($s);
$em = $this->i2osp($m2, $this->k);
if ($em === false) {
return false;
}
// EMSA-PKCS1-v1_5 encoding
// If the encoding operation outputs "intended encoded message length too short," output "RSA modulus
// too short" and stop.
try {
$em2 = $this->emsa_pkcs1_v1_5_encode($m, $this->k);
} catch (\LengthException $e) {
throw new \LengthException('RSA modulus too short');
}
// Compare
return hash_equals($em, $em2);
}
/**
* RSASSA-PKCS1-V1_5-VERIFY (relaxed matching)
*
* Per {@link http://tools.ietf.org/html/rfc3447#page-43 RFC3447#page-43} PKCS1 v1.5
* specified the use BER encoding rather than DER encoding that PKCS1 v2.0 specified.
* This means that under rare conditions you can have a perfectly valid v1.5 signature
* that fails to validate with _rsassa_pkcs1_v1_5_verify(). PKCS1 v2.1 also recommends
* that if you're going to validate these types of signatures you "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 [PKCS1 v2.0+]". so if you do
* $rsa->getLastPadding() and get RSA::PADDING_RELAXED_PKCS1 back instead of
* RSA::PADDING_PKCS1... that means BER encoding was used.
*
* @access private
* @param string $m
* @param string $s
* @return bool
*/
private function rsassa_pkcs1_v1_5_relaxed_verify($m, $s)
{
// Length checking
if (strlen($s) != $this->k) {
return false;
}
// RSA verification
$s = $this->os2ip($s);
$m2 = $this->rsavp1($s);
if ($m2 === false) {
return false;
}
$em = $this->i2osp($m2, $this->k);
if ($em === false) {
return false;
}
if (Strings::shift($em, 2) != "\0\1") {
return false;
}
$em = ltrim($em, "\xFF");
if (Strings::shift($em) != "\0") {
return false;
}
$decoded = ASN1::decodeBER($em);
if (!is_array($decoded) || empty($decoded[0]) || strlen($em) > $decoded[0]['length']) {
return false;
}
static $oids;
if (!isset($oids)) {
$oids = [
'md2' => '1.2.840.113549.2.2',
'md4' => '1.2.840.113549.2.4', // from PKCS1 v1.5
'md5' => '1.2.840.113549.2.5',
'id-sha1' => '1.3.14.3.2.26',
'id-sha256' => '2.16.840.1.101.3.4.2.1',
'id-sha384' => '2.16.840.1.101.3.4.2.2',
'id-sha512' => '2.16.840.1.101.3.4.2.3',
// from PKCS1 v2.2
'id-sha224' => '2.16.840.1.101.3.4.2.4',
'id-sha512/224' => '2.16.840.1.101.3.4.2.5',
'id-sha512/256' => '2.16.840.1.101.3.4.2.6',
];
ASN1::loadOIDs($oids);
}
$decoded = ASN1::asn1map($decoded[0], DigestInfo::MAP);
if (!isset($decoded) || $decoded === false) {
return false;
}
if (!isset($oids[$decoded['digestAlgorithm']['algorithm']])) {
return false;
}
$hash = $decoded['digestAlgorithm']['algorithm'];
$hash = substr($hash, 0, 3) == 'id-' ?
substr($hash, 3) :
$hash;
$hash = new Hash($hash);
$em = $hash->hash($m);
$em2 = $decoded['digest'];
return hash_equals($em, $em2);
}
/**
* EMSA-PSS-VERIFY
*
* See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}.
*
* @access private
* @param string $m
* @param string $em
* @param int $emBits
* @return string
*/
private function emsa_pss_verify($m, $em, $emBits)
{
// if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
// be output.
$emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8);
$sLen = $this->sLen !== null ? $this->sLen : $this->hLen;
$mHash = $this->hash->hash($m);
if ($emLen < $this->hLen + $sLen + 2) {
return false;
}
if ($em[strlen($em) - 1] != chr(0xBC)) {
return false;
}
$maskedDB = substr($em, 0, -$this->hLen - 1);
$h = substr($em, -$this->hLen - 1, $this->hLen);
$temp = chr(0xFF << ($emBits & 7));
if ((~$maskedDB[0] & $temp) != $temp) {
return false;
}
$dbMask = $this->mgf1($h, $emLen - $this->hLen - 1);
$db = $maskedDB ^ $dbMask;
$db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
$temp = $emLen - $this->hLen - $sLen - 2;
if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) {
return false;
}
$salt = substr($db, $temp + 1); // should be $sLen long
$m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
$h2 = $this->hash->hash($m2);
return hash_equals($h, $h2);
}
/**
* RSASSA-PSS-VERIFY
*
* See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}.
*
* @access private
* @param string $m
* @param string $s
* @return bool|string
*/
private function rsassa_pss_verify($m, $s)
{
// Length checking
if (strlen($s) != $this->k) {
return false;
}
// RSA verification
$modBits = 8 * $this->k;
$s2 = $this->os2ip($s);
$m2 = $this->rsavp1($s2);
$em = $this->i2osp($m2, $modBits >> 3);
if ($em === false) {
return false;
}
// EMSA-PSS verification
return $this->emsa_pss_verify($m, $em, $modBits - 1);
}
/**
* Verifies a signature
*
* @see self::sign()
* @param string $message
* @param string $signature
* @return bool
*/
public function verify($message, $signature)
{
switch ($this->signaturePadding) {
case self::SIGNATURE_RELAXED_PKCS1:
return $this->rsassa_pkcs1_v1_5_relaxed_verify($message, $signature);
case self::SIGNATURE_PKCS1:
return $this->rsassa_pkcs1_v1_5_verify($message, $signature);
//case self::SIGNATURE_PSS:
default:
return $this->rsassa_pss_verify($message, $signature);
}
}
/**
* RSAES-PKCS1-V1_5-ENCRYPT
*
* See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}.
*
* @access private
* @param string $m
* @param bool $pkcs15_compat optional
* @throws \LengthException if strlen($m) > $this->k - 11
* @return bool|string
*/
private function rsaes_pkcs1_v1_5_encrypt($m, $pkcs15_compat = false)
{
$mLen = strlen($m);
// Length checking
if ($mLen > $this->k - 11) {
throw new \LengthException('Message too long');
}
// EME-PKCS1-v1_5 encoding
$psLen = $this->k - $mLen - 3;
$ps = '';
while (strlen($ps) != $psLen) {
$temp = Random::string($psLen - strlen($ps));
$temp = str_replace("\x00", '', $temp);
$ps.= $temp;
}
$type = 2;
// see the comments of _rsaes_pkcs1_v1_5_decrypt() to understand why this is being done
if ($pkcs15_compat && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) {
$type = 1;
// "The padding string PS shall consist of k-3-||D|| octets. ... for block type 01, they shall have value FF"
$ps = str_repeat("\xFF", $psLen);
}
$em = chr(0) . chr($type) . $ps . chr(0) . $m;
// RSA encryption
$m = $this->os2ip($em);
$c = $this->rsaep($m);
$c = $this->i2osp($c, $this->k);
// Output the ciphertext C
return $c;
}
/**
* RSAES-OAEP-ENCRYPT
*
* See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1 RFC3447#section-7.1.1} and
* {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}.
*
* @access private
* @param string $m
* @throws \LengthException if strlen($m) > $this->k - 2 * $this->hLen - 2
* @return string
*/
private function rsaes_oaep_encrypt($m)
{
$mLen = strlen($m);
// Length checking
// if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
// be output.
if ($mLen > $this->k - 2 * $this->hLen - 2) {
throw new \LengthException('Message too long');
}
// EME-OAEP encoding
$lHash = $this->hash->hash($this->label);
$ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2);
$db = $lHash . $ps . chr(1) . $m;
$seed = Random::string($this->hLen);
$dbMask = $this->mgf1($seed, $this->k - $this->hLen - 1);
$maskedDB = $db ^ $dbMask;
$seedMask = $this->mgf1($maskedDB, $this->hLen);
$maskedSeed = $seed ^ $seedMask;
$em = chr(0) . $maskedSeed . $maskedDB;
// RSA encryption
$m = $this->os2ip($em);
$c = $this->rsaep($m);
$c = $this->i2osp($c, $this->k);
// Output the ciphertext C
return $c;
}
/**
* RSAEP
*
* See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}.
*
* @access private
* @param \phpseclib\Math\BigInteger $m
* @return bool|\phpseclib\Math\BigInteger
*/
private function rsaep($m)
{
if ($m->compare(self::$zero) < 0 || $m->compare($this->modulus) > 0) {
return false;
}
return $this->exponentiate($m);
}
/**
* Raw Encryption / Decryption
*
* Doesn't use padding and is not recommended.
*
* @access private
* @param string $m
* @return bool|string
* @throws \LengthException if strlen($m) > $this->k
*/
private function raw_encrypt($m)
{
if (strlen($m) > $this->k) {
throw new \LengthException('Message too long');
}
$temp = $this->os2ip($m);
$temp = $this->rsaep($temp);
return $this->i2osp($temp, $this->k);
}
/**
* Encryption
*
* Both self::PADDING_OAEP and self::PADDING_PKCS1 both place limits on how long $plaintext can be.
* If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will
* be concatenated together.
*
* @see self::decrypt()
* @access public
* @param string $plaintext
* @return bool|string
* @throws \LengthException if the RSA modulus is too short
*/
public function encrypt($plaintext)
{
switch ($this->encryptionPadding) {
case self::ENCRYPTION_NONE:
return $this->raw_encrypt($plaintext);
case self::ENCRYPTION_PKCS15_COMPAT:
case self::ENCRYPTION_PKCS1:
return $this->rsaes_pkcs1_v1_5_encrypt($plaintext, $padding == self::ENCRYPTION_PKCS15_COMPAT);
//case self::ENCRYPTION_OAEP:
default:
return $this->rsaes_oaep_encrypt($plaintext);
}
}
/**
* Returns the public key
*
* The public key is only returned under two circumstances - if the private key had the public key embedded within it
* or if the public key was set via setPublicKey(). If the currently loaded key is supposed to be the public key this
* function won't return it since this library, for the most part, doesn't distinguish between public and private keys.
*
* @param string $type
* @return mixed
*/
public function toString($type)
{
$type = self::validatePlugin('Keys', $type, 'savePublicKey');
return $type::savePublicKey($this->modulus, $this->publicExponent);
}
/**
* Converts a public key to a private key
*
* @return RSA
*/
public function asPrivateKey()
{
$new = new PrivateKey;
$new->exponent = $this->exponent;
$new->modulus = $this->modulus;
$new->k = $this->k;
$new->format = $this->format;
return $new
->withHash($this->hash->getHash())
->withMGFHash($this->mgfHash->getHash())
->withSaltLength($this->sLen)
->withLabel($this->label)
->withPadding($this->signaturePadding | $this->encryptionPadding);
}
}

View File

@ -0,0 +1,26 @@
<?php
/**
* UnsupportedFormatException
*
* PHP version 5
*
* @category Exception
* @package UnsupportedFormatException
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Exception;
/**
* UnsupportedFormatException
*
* @package UnsupportedFormatException
* @author Jim Wigginton <terrafrost@php.net>
*/
class UnsupportedFormatException extends \RuntimeException
{
}

View File

@ -31,6 +31,10 @@ use ParagonIE\ConstantTime\Hex;
use phpseclib\Crypt\Hash;
use phpseclib\Crypt\Random;
use phpseclib\Crypt\RSA;
use phpseclib\Crypt\DSA;
use phpseclib\Crypt\ECDSA;
use phpseclib\Crypt\Common\PublicKey;
use phpseclib\Crypt\Common\PrivateKey;
use phpseclib\Exception\UnsupportedAlgorithmException;
use phpseclib\File\ASN1\Element;
use phpseclib\Math\BigInteger;
@ -273,18 +277,18 @@ class X509
if (!self::$oidsLoaded) {
// OIDs from RFC5280 and those RFCs mentioned in RFC5280#section-4.1.1.2
ASN1::loadOIDs([
'id-pkix' => '1.3.6.1.5.5.7',
'id-pe' => '1.3.6.1.5.5.7.1',
'id-qt' => '1.3.6.1.5.5.7.2',
'id-kp' => '1.3.6.1.5.5.7.3',
'id-ad' => '1.3.6.1.5.5.7.48',
//'id-pkix' => '1.3.6.1.5.5.7',
//'id-pe' => '1.3.6.1.5.5.7.1',
//'id-qt' => '1.3.6.1.5.5.7.2',
//'id-kp' => '1.3.6.1.5.5.7.3',
//'id-ad' => '1.3.6.1.5.5.7.48',
'id-qt-cps' => '1.3.6.1.5.5.7.2.1',
'id-qt-unotice' => '1.3.6.1.5.5.7.2.2',
'id-ad-ocsp' =>'1.3.6.1.5.5.7.48.1',
'id-ad-caIssuers' => '1.3.6.1.5.5.7.48.2',
'id-ad-timeStamping' => '1.3.6.1.5.5.7.48.3',
'id-ad-caRepository' => '1.3.6.1.5.5.7.48.5',
'id-at' => '2.5.4',
//'id-at' => '2.5.4',
'id-at-name' => '2.5.4.41',
'id-at-surname' => '2.5.4.4',
'id-at-givenName' => '2.5.4.42',
@ -307,18 +311,19 @@ class X509
'id-at-role' => '2.5.4.72',
'id-at-postalAddress' => '2.5.4.16',
'id-domainComponent' => '0.9.2342.19200300.100.1.25',
'pkcs-9' => '1.2.840.113549.1.9',
//'id-domainComponent' => '0.9.2342.19200300.100.1.25',
//'pkcs-9' => '1.2.840.113549.1.9',
'pkcs-9-at-emailAddress' => '1.2.840.113549.1.9.1',
'id-ce' => '2.5.29',
//'id-ce' => '2.5.29',
'id-ce-authorityKeyIdentifier' => '2.5.29.35',
'id-ce-subjectKeyIdentifier' => '2.5.29.14',
'id-ce-keyUsage' => '2.5.29.15',
'id-ce-privateKeyUsagePeriod' => '2.5.29.16',
'id-ce-certificatePolicies' => '2.5.29.32',
'anyPolicy' => '2.5.29.32.0',
//'anyPolicy' => '2.5.29.32.0',
'id-ce-policyMappings' => '2.5.29.33',
'id-ce-subjectAltName' => '2.5.29.17',
'id-ce-issuerAltName' => '2.5.29.18',
'id-ce-subjectDirectoryAttributes' => '2.5.29.9',
@ -327,7 +332,7 @@ class X509
'id-ce-policyConstraints' => '2.5.29.36',
'id-ce-cRLDistributionPoints' => '2.5.29.31',
'id-ce-extKeyUsage' => '2.5.29.37',
'anyExtendedKeyUsage' => '2.5.29.37.0',
//'anyExtendedKeyUsage' => '2.5.29.37.0',
'id-kp-serverAuth' => '1.3.6.1.5.5.7.3.1',
'id-kp-clientAuth' => '1.3.6.1.5.5.7.3.2',
'id-kp-codeSigning' => '1.3.6.1.5.5.7.3.3',
@ -344,82 +349,47 @@ class X509
'id-ce-cRLReasons' => '2.5.29.21',
'id-ce-certificateIssuer' => '2.5.29.29',
'id-ce-holdInstructionCode' => '2.5.29.23',
'holdInstruction' => '1.2.840.10040.2',
//'holdInstruction' => '1.2.840.10040.2',
'id-holdinstruction-none' => '1.2.840.10040.2.1',
'id-holdinstruction-callissuer' => '1.2.840.10040.2.2',
'id-holdinstruction-reject' => '1.2.840.10040.2.3',
'id-ce-invalidityDate' => '2.5.29.24',
'md2' => '1.2.840.113549.2.2',
'md5' => '1.2.840.113549.2.5',
'id-sha1' => '1.3.14.3.2.26',
'id-dsa' => '1.2.840.10040.4.1',
'id-dsa-with-sha1' => '1.2.840.10040.4.3',
'pkcs-1' => '1.2.840.113549.1.1',
'rsaEncryption' => '1.2.840.113549.1.1.1',
'md2WithRSAEncryption' => '1.2.840.113549.1.1.2',
'md5WithRSAEncryption' => '1.2.840.113549.1.1.4',
'sha1WithRSAEncryption' => '1.2.840.113549.1.1.5',
'dhpublicnumber' => '1.2.840.10046.2.1',
'id-keyExchangeAlgorithm' => '2.16.840.1.101.2.1.1.22',
'ansi-X9-62' => '1.2.840.10045',
'id-ecSigType' => '1.2.840.10045.4',
'ecdsa-with-SHA1' => '1.2.840.10045.4.1',
'id-fieldType' => '1.2.840.10045.1',
'prime-field' => '1.2.840.10045.1.1',
'characteristic-two-field' => '1.2.840.10045.1.2',
'id-characteristic-two-basis' => '1.2.840.10045.1.2.3',
'gnBasis' => '1.2.840.10045.1.2.3.1',
'tpBasis' => '1.2.840.10045.1.2.3.2',
'ppBasis' => '1.2.840.10045.1.2.3.3',
'id-publicKeyType' => '1.2.840.10045.2',
'id-ecPublicKey' => '1.2.840.10045.2.1',
'ellipticCurve' => '1.2.840.10045.3',
'c-TwoCurve' => '1.2.840.10045.3.0',
'c2pnb163v1' => '1.2.840.10045.3.0.1',
'c2pnb163v2' => '1.2.840.10045.3.0.2',
'c2pnb163v3' => '1.2.840.10045.3.0.3',
'c2pnb176w1' => '1.2.840.10045.3.0.4',
'c2pnb191v1' => '1.2.840.10045.3.0.5',
'c2pnb191v2' => '1.2.840.10045.3.0.6',
'c2pnb191v3' => '1.2.840.10045.3.0.7',
'c2pnb191v4' => '1.2.840.10045.3.0.8',
'c2pnb191v5' => '1.2.840.10045.3.0.9',
'c2pnb208w1' => '1.2.840.10045.3.0.10',
'c2pnb239v1' => '1.2.840.10045.3.0.11',
'c2pnb239v2' => '1.2.840.10045.3.0.12',
'c2pnb239v3' => '1.2.840.10045.3.0.13',
'c2pnb239v4' => '1.2.840.10045.3.0.14',
'c2pnb239v5' => '1.2.840.10045.3.0.15',
'c2pnb272w1' => '1.2.840.10045.3.0.16',
'c2pnb304w1' => '1.2.840.10045.3.0.17',
'c2pnb359v1' => '1.2.840.10045.3.0.18',
'c2pnb368w1' => '1.2.840.10045.3.0.19',
'c2pnb431r1' => '1.2.840.10045.3.0.20',
'primeCurve' => '1.2.840.10045.3.1',
'prime192v1' => '1.2.840.10045.3.1.1',
'prime192v2' => '1.2.840.10045.3.1.2',
'prime192v3' => '1.2.840.10045.3.1.3',
'prime239v1' => '1.2.840.10045.3.1.4',
'prime239v2' => '1.2.840.10045.3.1.5',
'prime239v3' => '1.2.840.10045.3.1.6',
'prime256v1' => '1.2.840.10045.3.1.7',
'id-RSAES-OAEP' => '1.2.840.113549.1.1.7',
'id-pSpecified' => '1.2.840.113549.1.1.9',
'id-RSASSA-PSS' => '1.2.840.113549.1.1.10',
'id-mgf1' => '1.2.840.113549.1.1.8',
'sha224WithRSAEncryption' => '1.2.840.113549.1.1.14',
'sha256WithRSAEncryption' => '1.2.840.113549.1.1.11',
'sha384WithRSAEncryption' => '1.2.840.113549.1.1.12',
'sha512WithRSAEncryption' => '1.2.840.113549.1.1.13',
'id-sha224' => '2.16.840.1.101.3.4.2.4',
'id-sha256' => '2.16.840.1.101.3.4.2.1',
'id-sha384' => '2.16.840.1.101.3.4.2.2',
'id-sha512' => '2.16.840.1.101.3.4.2.3',
'id-GostR3411-94-with-GostR3410-94' => '1.2.643.2.2.4',
'id-GostR3411-94-with-GostR3410-2001' => '1.2.643.2.2.3',
'id-GostR3410-2001' => '1.2.643.2.2.20',
'id-GostR3410-94' => '1.2.643.2.2.19',
'id-ecPublicKey' => '1.2.840.10045.2.1',
'ecdsa-with-SHA1' => '1.2.840.10045.4.1',
// from https://tools.ietf.org/html/rfc5758#section-3.2
'ecdsa-with-SHA224' => '1.2.840.10045.4.3.1',
'ecdsa-with-SHA256' => '1.2.840.10045.4.3.2',
'ecdsa-with-SHA384' => '1.2.840.10045.4.3.3',
'ecdsa-with-SHA512' => '1.2.840.10045.4.3.4',
'id-dsa' => '1.2.840.10040.4.1',
'id-dsa-with-sha1' => '1.2.840.10040.4.3',
// from https://tools.ietf.org/html/rfc5758#section-3.1
'id-dsa-with-sha224' => '2.16.840.1.101.3.4.3.1',
'id-dsa-with-sha256' => '2.16.840.1.101.3.4.3.2',
// from https://tools.ietf.org/html/rfc8410:
'id-Ed25519' => '1.3.101.112',
'id-Ed448' => '1.3.101.113',
//'id-sha224' => '2.16.840.1.101.3.4.2.4',
//'id-sha256' => '2.16.840.1.101.3.4.2.1',
//'id-sha384' => '2.16.840.1.101.3.4.2.2',
//'id-sha512' => '2.16.840.1.101.3.4.2.3',
//'id-GostR3411-94-with-GostR3410-94' => '1.2.643.2.2.4',
//'id-GostR3411-94-with-GostR3410-2001' => '1.2.643.2.2.3',
//'id-GostR3410-2001' => '1.2.643.2.2.20',
//'id-GostR3410-94' => '1.2.643.2.2.19',
// Netscape Object Identifiers from "Netscape Certificate Extensions"
'netscape' => '2.16.840.1.113730',
'netscape-cert-extension' => '2.16.840.1.113730.1',
@ -499,8 +469,12 @@ class X509
$this->mapInDNs($x509, 'tbsCertificate/issuer/rdnSequence');
$this->mapInDNs($x509, 'tbsCertificate/subject/rdnSequence');
$key = &$x509['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'];
$key = $this->reformatKey($x509['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $key);
$key = $x509['tbsCertificate']['subjectPublicKeyInfo'];
$key = ASN1::encodeDER($key, Maps\SubjectPublicKeyInfo::MAP);
$x509['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'] =
"-----BEGIN PUBLIC KEY-----\r\n" .
chunk_split(base64_encode($key), 64) .
"-----END PUBLIC KEY-----";
$this->currentCert = $x509;
$this->dn = $x509['tbsCertificate']['subject'];
@ -531,21 +505,14 @@ class X509
case is_object($cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']):
break;
default:
switch ($algorithm) {
case 'rsaEncryption':
$cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']
= "\0" . Base64::decode(preg_replace('#-.+-|[\r\n]#', '', $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']));
/* "[For RSA keys] the parameters field MUST have ASN.1 type NULL for this algorithm identifier."
-- https://tools.ietf.org/html/rfc3279#section-2.3.1
$cert['tbsCertificate']['subjectPublicKeyInfo'] = new Element(
base64_decode(preg_replace('#-.+-|[\r\n]#', '', $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']))
);
}
given that and the fact that RSA keys appear to be the only key type for which the parameters field can be blank,
it seems like perhaps the ASN.1 description ought not say the parameters field is OPTIONAL, but whatever.
*/
$cert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['parameters'] = null;
// https://tools.ietf.org/html/rfc3279#section-2.2.1
$cert['signatureAlgorithm']['parameters'] = null;
$cert['tbsCertificate']['signature']['parameters'] = null;
}
if ($algorithm == 'rsaEncryption') {
$cert['signatureAlgorithm']['parameters'] = null;
$cert['tbsCertificate']['signature']['parameters'] = null;
}
$filters = [];
@ -1389,9 +1356,7 @@ class X509
{
switch ($publicKeyAlgorithm) {
case 'rsaEncryption':
$rsa = new RSA();
$rsa->load($publicKey);
$key = RSA::load($publicKey, 'PKCS8');
switch ($signatureAlgorithm) {
case 'md2WithRSAEncryption':
case 'md5WithRSAEncryption':
@ -1400,10 +1365,41 @@ class X509
case 'sha256WithRSAEncryption':
case 'sha384WithRSAEncryption':
case 'sha512WithRSAEncryption':
$rsa->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm));
if (!@$rsa->verify($signatureSubject, $signature, RSA::PADDING_PKCS1)) {
return false;
}
$key = $key
->withHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm))
->withPadding(RSA::SIGNATURE_PKCS1);
break;
default:
throw new UnsupportedAlgorithmException('Signature algorithm unsupported');
}
break;
case 'id-Ed25519':
case 'id-Ed448':
$key = ECDSA::load($publicKey, 'PKCS8');
break;
case 'id-ecPublicKey':
$key = ECDSA::load($publicKey, 'PKCS8');
switch ($signatureAlgorithm) {
case 'ecdsa-with-SHA1':
case 'ecdsa-with-SHA224':
case 'ecdsa-with-SHA256':
case 'ecdsa-with-SHA384':
case 'ecdsa-with-SHA512':
$key = $key
->withHash(preg_replace('#^ecdsa-with-#', '', strtolower($signatureAlgorithm)));
break;
default:
throw new UnsupportedAlgorithmException('Signature algorithm unsupported');
}
break;
case 'id-dsa':
$key = DSA::load($publicKey, 'PKCS8');
switch ($signatureAlgorithm) {
case 'id-dsa-with-sha1':
case 'id-dsa-with-sha224':
case 'id-dsa-with-sha256':
$key = $key
->withHash(preg_replace('#^id-dsa-with-#', '', strtolower($signatureAlgorithm)));
break;
default:
throw new UnsupportedAlgorithmException('Signature algorithm unsupported');
@ -1413,7 +1409,7 @@ class X509
throw new UnsupportedAlgorithmException('Public key algorithm unsupported');
}
return true;
return $key->verify($signatureSubject, $signature);
}
/**
@ -1451,32 +1447,6 @@ class X509
self::$disable_url_fetch = false;
}
/**
* Reformat public keys
*
* Reformats a public key to a format supported by phpseclib (if applicable)
*
* @param string $algorithm
* @param string $key
* @access private
* @return string
*/
private function reformatKey($algorithm, $key)
{
switch ($algorithm) {
case 'rsaEncryption':
return
"-----BEGIN RSA PUBLIC KEY-----\r\n" .
// subjectPublicKey is stored as a bit string in X.509 certs. the first byte of a bit string represents how many bits
// in the last byte should be ignored. the following only supports non-zero stuff but as none of the X.509 certs Firefox
// uses as a cert authority actually use a non-zero bit I think it's safe to assume that none do.
chunk_split(Base64::encode(substr($key, 1)), 64) .
'-----END RSA PUBLIC KEY-----';
default:
return $key;
}
}
/**
* Decodes an IP address
*
@ -2053,9 +2023,8 @@ class X509
* @access public
* @return bool
*/
public function setPublicKey($key)
public function setPublicKey(PublicKey $key)
{
$key->setPublicKey();
$this->publicKey = $key;
}
@ -2067,7 +2036,7 @@ class X509
* @param object $key
* @access public
*/
public function setPrivateKey($key)
public function setPrivateKey(PrivateKey $key)
{
$this->privateKey = $key;
}
@ -2115,15 +2084,16 @@ class X509
switch ($keyinfo['algorithm']['algorithm']) {
case 'rsaEncryption':
$publicKey = new RSA();
$publicKey->load($key);
$publicKey->setPublicKey();
break;
default:
return false;
return RSA::load($key, 'PKCS8');
case 'id-ecPublicKey':
case 'id-Ed25519':
case 'id-Ed448':
return ECDSA::load($key, 'PKCS8');
case 'id-dsa':
return DSA::load($key, 'PKCS8');
}
return $publicKey;
return false;
}
/**
@ -2185,19 +2155,15 @@ class X509
$this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);
$algorithm = &$csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'];
$key = &$csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'];
$key = $this->reformatKey($algorithm, $key);
$key = $csr['certificationRequestInfo']['subjectPKInfo'];
$key = ASN1::encodeDER($key, Maps\SubjectPublicKeyInfo::MAP);
$csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'] =
"-----BEGIN PUBLIC KEY-----\r\n" .
chunk_split(base64_encode($key), 64) .
"-----END PUBLIC KEY-----";
switch ($algorithm) {
case 'rsaEncryption':
$this->publicKey = new RSA();
$this->publicKey->load($key);
$this->publicKey->setPublicKey();
break;
default:
$this->publicKey = null;
}
$this->publicKey = null;
$this->publicKey = $this->getPublicKey();
$this->currentKeyIdentifier = null;
$this->currentCert = $csr;
@ -2224,14 +2190,9 @@ class X509
case is_object($csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']):
break;
default:
switch ($algorithm) {
case 'rsaEncryption':
$csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']
= "\0" . Base64::decode(preg_replace('#-.+-|[\r\n]#', '', $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']));
$csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['parameters'] = null;
$csr['signatureAlgorithm']['parameters'] = null;
$csr['certificationRequestInfo']['signature']['parameters'] = null;
}
$csr['certificationRequestInfo']['subjectPKInfo'] = new Element(
base64_decode(preg_replace('#-.+-|[\r\n]#', '', $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']))
);
}
$filters = [];
@ -2305,18 +2266,15 @@ class X509
$this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);
$algorithm = &$spkac['publicKeyAndChallenge']['spki']['algorithm']['algorithm'];
$key = &$spkac['publicKeyAndChallenge']['spki']['subjectPublicKey'];
$key = $this->reformatKey($algorithm, $key);
$key = $spkac['publicKeyAndChallenge']['spki'];
$key = ASN1::encodeDER($key, Maps\SubjectPublicKeyInfo::MAP);
$spkac['publicKeyAndChallenge']['spki']['subjectPublicKey'] =
"-----BEGIN PUBLIC KEY-----\r\n" .
chunk_split(base64_encode($key), 64) .
"-----END PUBLIC KEY-----";
switch ($algorithm) {
case 'rsaEncryption':
$this->publicKey = new RSA();
$this->publicKey->load($key);
break;
default:
$this->publicKey = null;
}
$this->publicKey = null;
$this->publicKey = $this->getPublicKey();
$this->currentKeyIdentifier = null;
$this->currentCert = $spkac;
@ -2344,11 +2302,9 @@ class X509
case is_object($spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']):
break;
default:
switch ($algorithm) {
case 'rsaEncryption':
$spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']
= "\0" . Base64::decode(preg_replace('#-.+-|[\r\n]#', '', $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']));
}
$spkac['publicKeyAndChallenge']['spki'] = new Element(
base64_decode(preg_replace('#-.+-|[\r\n]#', '', $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']))
);
}
$spkac = ASN1::encodeDER($spkac, Maps\SignedPublicKeyAndChallenge::MAP);
@ -2535,7 +2491,7 @@ class X509
}
$currentCert = isset($this->currentCert) ? $this->currentCert : null;
$signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject: null;
$signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : null;
if (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertificate'])) {
$this->currentCert = $subject->currentCert;
@ -2713,17 +2669,12 @@ class X509
}
$origPublicKey = $this->publicKey;
$class = get_class($this->privateKey);
$this->publicKey = new $class();
$this->publicKey->load($this->privateKey->getPublicKey());
$this->publicKey->setPublicKey();
if (!($publicKey = $this->formatSubjectPublicKey())) {
return false;
}
$this->publicKey = $this->privateKey->getPublicKey();
$publicKey = $this->formatSubjectPublicKey();
$this->publicKey = $origPublicKey;
$currentCert = isset($this->currentCert) ? $this->currentCert : null;
$signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject: null;
$signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : null;
if (isset($this->currentCert) && is_array($this->currentCert) && isset($this->currentCert['certificationRequestInfo'])) {
$this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm;
@ -2772,18 +2723,12 @@ class X509
}
$origPublicKey = $this->publicKey;
$class = get_class($this->privateKey);
$this->publicKey = new $class();
$this->publicKey->load($this->privateKey->getPublicKey());
$this->publicKey->setPublicKey();
$this->publicKey = $this->privateKey->getPublicKey();
$publicKey = $this->formatSubjectPublicKey();
if (!$publicKey) {
return false;
}
$this->publicKey = $origPublicKey;
$currentCert = isset($this->currentCert) ? $this->currentCert : null;
$signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject: null;
$signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : null;
// re-signing a SPKAC seems silly but since everything else supports re-signing why not?
if (isset($this->currentCert) && is_array($this->currentCert) && isset($this->currentCert['publicKeyAndChallenge'])) {
@ -2966,7 +2911,7 @@ class X509
* @throws \phpseclib\Exception\UnsupportedAlgorithmException if the algorithm is unsupported
* @return mixed
*/
private function signHelper($key, $signatureAlgorithm)
private function signHelper(PrivateKey $key, $signatureAlgorithm)
{
if ($key instanceof RSA) {
switch ($signatureAlgorithm) {
@ -2977,9 +2922,52 @@ class X509
case 'sha256WithRSAEncryption':
case 'sha384WithRSAEncryption':
case 'sha512WithRSAEncryption':
$key->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm));
$key = $key
->withHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm))
->withPadding(RSA::SIGNATURE_PKCS1);
$this->currentCert['signature'] = "\0" . $key->sign($this->signatureSubject);
return $this->currentCert;
default:
throw new UnsupportedAlgorithmException('Signature algorithm unsupported');
}
}
$this->currentCert['signature'] = "\0" . $key->sign($this->signatureSubject, RSA::PADDING_PKCS1);
if ($key instanceof DSA) {
switch ($signatureAlgorithm) {
case 'id-dsa-with-sha1':
case 'id-dsa-with-sha224':
case 'id-dsa-with-sha256':
$key = $key
->withHash(preg_replace('#^id-dsa-with-#', '', strtolower($signatureAlgorithm)));
$this->currentCert['signature'] = "\0" . $key->sign($this->signatureSubject);
return $this->currentCert;
default:
throw new UnsupportedAlgorithmException('Signature algorithm unsupported');
}
}
if ($key instanceof ECDSA) {
switch ($signatureAlgorithm) {
case 'id-Ed25519':
if ($key->getCurve() !== 'Ed25519') {
throw new UnsupportedAlgorithmException('Loaded ECDSA does not use the Ed25519 key and yet that is the signature algorithm that has been chosen');
}
$this->currentCert['signature'] = "\0" . $key->sign($this->signatureSubject);
return $this->currentCert;
case 'id-Ed448':
if ($key->getCurve() !== 'Ed448') {
throw new UnsupportedAlgorithmException('Loaded ECDSA does not use the Ed448 key and yet that is the signature algorithm that has been chosen');
}
$this->currentCert['signature'] = "\0" . $key->sign($this->signatureSubject);
return $this->currentCert;
case 'ecdsa-with-SHA1':
case 'ecdsa-with-SHA224':
case 'ecdsa-with-SHA256':
case 'ecdsa-with-SHA384':
case 'ecdsa-with-SHA512':
$key = $key
->withHash(preg_replace('#^ecdsa-with-#', '', strtolower($signatureAlgorithm)));
$this->currentCert['signature'] = "\0" . $key->sign($this->signatureSubject);
return $this->currentCert;
default:
throw new UnsupportedAlgorithmException('Signature algorithm unsupported');
@ -3669,17 +3657,14 @@ class X509
*/
private function formatSubjectPublicKey()
{
if ($this->publicKey instanceof RSA) {
// the following two return statements do the same thing. i dunno.. i just prefer the later for some reason.
// the former is a good example of how to do fuzzing on the public key
//return new Element(preg_replace('#-.+-|[\r\n]#', '', $this->publicKey->getPublicKey()));
return [
'algorithm' => ['algorithm' => 'rsaEncryption'],
'subjectPublicKey' => $this->publicKey->getPublicKey('PKCS1')
];
}
$publicKey = base64_decode(preg_replace('#-.+-|[\r\n]#', '', $this->publicKey));
return false;
$decoded = ASN1::decodeBER($publicKey);
$mapped = ASN1::asn1map($decoded[0], Maps\SubjectPublicKeyInfo::MAP);
$mapped['subjectPublicKey'] = (string) $this->publicKey;
return $mapped;
}
/**

View File

@ -16,6 +16,7 @@
namespace phpseclib\Math\BigInteger\Engines;
use phpseclib\Crypt\RSA;
use phpseclib\Crypt\RSA\Keys\PKCS8;
use phpseclib\Math\BigInteger;
/**
@ -51,11 +52,11 @@ abstract class OpenSSL
throw new \OutOfRangeException('Only modulo between 31 and 16384 bits are accepted');
}
$rsa = new RSA();
$rsa->load([
'e' => new BigInteger($e),
'n' => new BigInteger($n)
]);
$key = PKCS8::savePublicKey(
new BigInteger($n),
new BigInteger($e)
);
$rsa = RSA::load($key);
//$rsa->setPublicKeyFormat('PKCS1');
$plaintext = str_pad($x->toBytes(), strlen($n->toBytes(true)) - 1, "\0", STR_PAD_LEFT);

View File

@ -24,7 +24,7 @@
* <?php
* include 'vendor/autoload.php';
*
* $key = new \phpseclib\Crypt\RSA();
* \phpseclib\Crypt\PublicKeyLoader::load('...');
* //$key->setPassword('whatever');
* $key->load(file_get_contents('privatekey'));
*
@ -49,12 +49,12 @@
namespace phpseclib\Net;
use ParagonIE\ConstantTime\Base64;
use phpseclib\Crypt\Blowfish;
use phpseclib\Crypt\Hash;
use phpseclib\Crypt\Random;
use phpseclib\Crypt\RC4;
use phpseclib\Crypt\Rijndael;
use phpseclib\Crypt\Common\PrivateKey;
use phpseclib\Crypt\RSA;
use phpseclib\Crypt\DSA;
use phpseclib\Crypt\ECDSA;
@ -66,6 +66,7 @@ use phpseclib\System\SSH\Agent;
use phpseclib\System\SSH\Agent\Identity as AgentIdentity;
use phpseclib\Exception\NoSupportedAlgorithmsException;
use phpseclib\Exception\UnsupportedAlgorithmException;
use phpseclib\Exception\UnsupportedCurveException;
use phpseclib\Common\Functions\Strings;
/**
@ -2112,9 +2113,11 @@ class SSH2
return !is_string($password) && !is_array($password) ? false : $this->keyboard_interactive_process($password);
}
if ($password instanceof RSA) {
if ($password instanceof PrivateKey) {
return $this->privatekey_login($username, $password);
} elseif ($password instanceof Agent) {
}
if ($password instanceof Agent) {
return $this->ssh_agent_login($username, $password);
}
@ -2126,6 +2129,10 @@ class SSH2
return false;
}
if (!is_string($password)) {
throw new \UnexpectedValueException('$password needs to either be an instance of \phpseclib\Crypt\Common\PrivateKey, \System\SSH\Agent, an array or a string');
}
if (!isset($password)) {
$packet = Strings::packSSH2(
'Cs3',
@ -2361,7 +2368,7 @@ class SSH2
* @return bool
* @access private
*/
private function ssh_agent_login($username, $agent)
private function ssh_agent_login($username, Agent $agent)
{
$this->agent = $agent;
$keys = $agent->requestIdentities();
@ -2378,42 +2385,69 @@ class SSH2
* Login with an RSA private key
*
* @param string $username
* @param \phpseclib\Crypt\RSA $password
* @param \phpseclib\Crypt\Common\PrivateKey $privatekey
* @return bool
* @throws \RuntimeException on connection error
* @access private
* @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
* by sending dummy SSH_MSG_IGNORE messages.
*/
private function privatekey_login($username, $privatekey)
private function privatekey_login($username, PrivateKey $privatekey)
{
// see http://tools.ietf.org/html/rfc4253#page-15
$publickey = $privatekey->getPublicKey('Raw');
if ($publickey === false) {
return false;
$publickey = $privatekey->getPublicKey();
if ($publickey instanceof RSA) {
$privatekey = $privatekey->withPadding(RSA::SIGNATURE_PKCS1);
switch ($this->signature_format) {
case 'rsa-sha2-512':
$hash = 'sha512';
$signatureType = 'rsa-sha2-512';
break;
case 'rsa-sha2-256':
$hash = 'sha256';
$signatureType = 'rsa-sha2-256';
break;
//case 'ssh-rsa':
default:
$hash = 'sha1';
$signatureType = 'ssh-rsa';
}
} else if ($publickey instanceof ECDSA) {
$privatekey = $privatekey->withSignatureFormat('SSH2');
$curveName = $privatekey->getCurve();
switch ($curveName) {
case 'Ed25519':
$hash = 'sha512';
$signatureType = 'ssh-ed25519';
break;
case 'secp256r1': // nistp256
$hash = 'sha256';
$signatureType = 'ecdsa-sha2-nistp256';
break;
case 'secp384r1': // nistp384
$hash = 'sha384';
$signatureType = 'ecdsa-sha2-nistp384';
break;
case 'secp521r1': // nistp521
$hash = 'sha512';
$signatureType = 'ecdsa-sha2-nistp521';
break;
default:
if (is_array($curveName)) {
throw new UnsupportedCurveException('Specified Curves are not supported by SSH2');
}
throw new UnsupportedCurveException('Named Curve of ' . $curveName . ' is not supported by phpseclib\'s SSH2 implementation');
}
} else if ($publickey instanceof DSA) {
$privatekey = $privatekey->withSignatureFormat('SSH2');
$hash = 'sha1';
$signatureType = 'ssh-dss';
} else {
throw new UnsupportedAlgorithmException('Please use either an RSA key, an ECDSA one or a DSA key');
}
$publickey = Strings::packSSH2(
'sii',
'ssh-rsa',
$publickey['e'],
$publickey['n']
);
switch ($this->signature_format) {
case 'rsa-sha2-512':
$hash = 'sha512';
$signatureType = 'rsa-sha2-512';
break;
case 'rsa-sha2-256':
$hash = 'sha256';
$signatureType = 'rsa-sha2-256';
break;
//case 'ssh-rsa':
default:
$hash = 'sha1';
$signatureType = 'ssh-rsa';
}
$publickeyStr = $publickey->toString('OpenSSH');
$publickeyStr = base64_decode(preg_replace('#(^.*? )|( .*?)$#', '', $publickeyStr));
$part1 = Strings::packSSH2(
'Csss',
@ -2422,7 +2456,7 @@ class SSH2
'ssh-connection',
'publickey'
);
$part2 = Strings::packSSH2('ss', $signatureType, $publickey);
$part2 = Strings::packSSH2('ss', $signatureType, $publickeyStr);
$packet = $part1 . chr(0) . $part2;
$this->send_binary_packet($packet);
@ -2438,7 +2472,6 @@ class SSH2
case NET_SSH2_MSG_USERAUTH_FAILURE:
list($message) = Strings::unpackSSH2('s', $response);
$this->errors[] = 'SSH_MSG_USERAUTH_FAILURE: ' . $message;
return false;
case NET_SSH2_MSG_USERAUTH_PK_OK:
// we'll just take it on faith that the public key blob and the public key algorithm name are as
@ -2453,9 +2486,11 @@ class SSH2
}
$packet = $part1 . chr(1) . $part2;
$privatekey->setHash($hash);
$signature = $privatekey->sign(Strings::packSSH2('s', $this->session_id) . $packet, RSA::PADDING_PKCS1);
$signature = Strings::packSSH2('ss', $signatureType, $signature);
$privatekey = $privatekey->withHash($hash);
$signature = $privatekey->sign(Strings::packSSH2('s', $this->session_id) . $packet);
if ($publickey instanceof RSA) {
$signature = Strings::packSSH2('ss', $signatureType, $signature);
}
$packet.= Strings::packSSH2('s', $signature);
$this->send_binary_packet($packet);
@ -4258,6 +4293,8 @@ class SSH2
return [
'ssh-ed25519', // https://tools.ietf.org/html/draft-ietf-curdle-ssh-ed25519-02
'ecdsa-sha2-nistp256', // RFC 5656
'ecdsa-sha2-nistp384', // RFC 5656
'ecdsa-sha2-nistp521', // RFC 5656
'rsa-sha2-256', // RFC 8332
'rsa-sha2-512', // RFC 8332
'ssh-rsa', // RECOMMENDED sign Raw RSA Key
@ -4553,7 +4590,7 @@ class SSH2
if ($this->signature_validated) {
return $this->bitmap ?
$this->signature_format . ' ' . Base64::encode($this->server_public_host_key) :
$this->signature_format . ' ' . $server_public_host_key :
false;
}
@ -4562,27 +4599,30 @@ class SSH2
switch ($this->signature_format) {
case 'ssh-ed25519':
case 'ecdsa-sha2-nistp256':
$ec = new ECDSA();
$ec->load($server_public_host_key, 'OpenSSH');
case 'ecdsa-sha2-nistp384':
case 'ecdsa-sha2-nistp521':
$key = ECDSA::load($server_public_host_key, 'OpenSSH')
->withSignatureFormat('SSH2');
switch ($this->signature_format) {
case 'ssh-ed25519':
//$ec->setHash('sha512');
Strings::shift($signature, 4 + strlen('ssh-ed25519') + 4);
$hash = 'sha512';
break;
case 'ecdsa-sha2-nistp256':
$ec->setHash('sha256');
$hash = 'sha256';
break;
case 'ecdsa-sha2-nistp384':
$hash = 'sha384';
break;
case 'ecdsa-sha2-nistp521':
$hash = 'sha512';
}
if (!$ec->verify($this->exchange_hash, $signature, 'SSH2')) {
return $this->disconnect_helper(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
};
$key = $key->withHash($hash);
break;
case 'ssh-dss':
$dsa = new DSA();
$dsa->load($server_public_host_key, 'OpenSSH');
$dsa->setHash('sha1');
if (!$dsa->verify($this->exchange_hash, $signature, 'SSH2')) {
return $this->disconnect_helper(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
};
$key = DSA::load($server_public_host_key, 'OpenSSH')
->withSignatureFormat('SSH2')
->withHash('sha1');
break;
case 'ssh-rsa':
case 'rsa-sha2-256':
@ -4594,8 +4634,8 @@ class SSH2
$temp = unpack('Nlength', Strings::shift($signature, 4));
$signature = Strings::shift($signature, $temp['length']);
$rsa = new RSA();
$rsa->load($server_public_host_key, 'OpenSSH');
$key = RSA::load($server_public_host_key, 'OpenSSH')
->withPadding(RSA::SIGNATURE_PKCS1);
switch ($this->signature_format) {
case 'rsa-sha2-512':
$hash = 'sha512';
@ -4607,17 +4647,18 @@ class SSH2
default:
$hash = 'sha1';
}
$rsa->setHash($hash);
if (!$rsa->verify($this->exchange_hash, $signature, RSA::PADDING_PKCS1)) {
return $this->disconnect_helper(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
}
$key = $key->withHash($hash);
break;
default:
$this->disconnect_helper(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
throw new NoSupportedAlgorithmsException('Unsupported signature format');
}
return $this->signature_format . ' ' . Base64::encode($this->server_public_host_key);
if (!$key->verify($this->exchange_hash, $signature)) {
return $this->disconnect_helper(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
};
return $this->signature_format . ' ' . $server_public_host_key;
}
/**

View File

@ -38,6 +38,7 @@ use phpseclib\Crypt\RSA;
use phpseclib\Exception\BadConfigurationException;
use phpseclib\System\SSH\Agent\Identity;
use phpseclib\Common\Functions\Strings;
use phpseclib\Crypt\PublicKeyLoader;
/**
* Pure-PHP ssh-agent client identity factory
@ -198,9 +199,8 @@ class Agent
$temp = $key_blob;
list($key_type) = Strings::unpackSSH2('s', $temp);
switch ($key_type) {
case 'ssh-rsa':
$key = new RSA();
$key->load($key_str);
case 'ssh-rsa':
$key = PublicKeyLoader::load(base64_encode($key_blob));
break;
case 'ssh-dss':
// not currently supported

View File

@ -20,6 +20,8 @@ use phpseclib\Crypt\RSA;
use phpseclib\Exception\UnsupportedAlgorithmException;
use phpseclib\System\SSH\Agent;
use phpseclib\Common\Functions\Strings;
use phpseclib\Crypt\Common\PrivateKey;
/**
* Pure-PHP ssh-agent client identity object
@ -34,7 +36,7 @@ use phpseclib\Common\Functions\Strings;
* @author Jim Wigginton <terrafrost@php.net>
* @access internal
*/
class Identity
class Identity implements PrivateKey
{
/**@+
* Signature Flags
@ -107,7 +109,6 @@ class Identity
public function setPublicKey($key)
{
$this->key = $key;
$this->key->setPublicKey();
}
/**
@ -135,32 +136,48 @@ class Identity
*/
public function getPublicKey($type = 'PKCS8')
{
return $this->key->getPublicKey($type);
return $this->key;
}
/**
* Sets the hash
*
* ssh-agent only supports signatures with sha1 hashes but to maintain BC with RSA.php this function exists
*
* @param string $hash optional
* @param string $hash
* @access public
*/
public function setHash($hash)
public function withHash($hash)
{
$this->flags = 0;
$new = clone $this;
$new->flags = 0;
switch ($hash) {
case 'sha1':
break;
case 'sha256':
$this->flags = self::SSH_AGENT_RSA2_256;
$new->flags = self::SSH_AGENT_RSA2_256;
break;
case 'sha512':
$this->flags = self::SSH_AGENT_RSA2_512;
$new->flags = self::SSH_AGENT_RSA2_512;
break;
default:
throw new UnsupportedAlgorithmException('The only supported hashes for RSA are sha1, sha256 and sha512');
}
return $new;
}
/**
* Sets the padding
*
* Only PKCS1 padding is supported
*
* @param string $padding
* @access public
*/
public function withPadding($padding = RSA::SIGNATURE_PKCS1)
{
if ($padding != RSA::SIGNATURE_PKCS1 && $padding != RSA::SIGNATURE_RELAXED_PKCS1) {
throw new UnsupportedAlgorithmException('ssh-agent can only create PKCS1 signatures');
}
return $this;
}
/**
@ -175,12 +192,8 @@ class Identity
* @throws \phpseclib\Exception\UnsupportedAlgorithmException if the algorithm is unsupported
* @access public
*/
public function sign($message, $padding = RSA::PADDING_PKCS1)
public function sign($message)
{
if ($padding != RSA::PADDING_PKCS1 && $padding != RSA::PADDING_RELAXED_PKCS1) {
throw new UnsupportedAlgorithmException('ssh-agent can only create PKCS1 signatures');
}
// the last parameter (currently 0) is for flags and ssh-agent only defines one flag (for ssh-dss): SSH_AGENT_OLD_SIGNATURE
$packet = Strings::packSSH2(
'CssN',
@ -206,4 +219,26 @@ class Identity
return $signature_blob;
}
/**
* Returns the private key
*
* @param string $type
* @return string
*/
public function toString($type)
{
throw new \RuntimeException('ssh-agent does not provide a mechanism to get the private key');
}
/**
* Sets the password
*
* @access public
* @param string|boolean $password
*/
public function withPassword($password = false)
{
throw new \RuntimeException('ssh-agent does not provide a mechanism to get the private key');
}
}

View File

@ -7,6 +7,9 @@
*/
use phpseclib\Crypt\DSA;
use phpseclib\Crypt\DSA\Parameters;
use phpseclib\Crypt\DSA\PublicKey;
use phpseclib\Crypt\DSA\PrivateKey;
/**
* @requires PHP 7.0
@ -16,14 +19,17 @@ class Unit_Crypt_DSA_CreateKeyTest extends PhpseclibTestCase
public function testCreateParameters()
{
$dsa = DSA::createParameters();
$this->assertInstanceOf('\phpseclib\Crypt\DSA', $dsa);
$this->assertInstanceOf(Parameters::class, $dsa);
$this->assertRegexp('#BEGIN DSA PARAMETERS#', "$dsa");
$dsa = DSA::createParameters(100, 100);
$this->assertFalse($dsa);
try {
$dsa = DSA::createParameters(100, 100);
} catch (Exception $e) {
$this->assertInstanceOf(Exception::class, $e);
}
$dsa = DSA::createParameters(512, 160);
$this->assertInstanceOf('\phpseclib\Crypt\DSA', $dsa);
$this->assertInstanceOf(Parameters::class, $dsa);
$this->assertRegexp('#BEGIN DSA PARAMETERS#', "$dsa");
return $dsa;
@ -34,17 +40,17 @@ class Unit_Crypt_DSA_CreateKeyTest extends PhpseclibTestCase
*/
public function testCreateKey($params)
{
extract(DSA::createKey());
$this->assertInstanceOf('\phpseclib\Crypt\DSA', $privatekey);
$this->assertInstanceOf('\phpseclib\Crypt\DSA', $publickey);
$privatekey = DSA::createKey();
$this->assertInstanceOf(PrivateKey::class, $privatekey);
$this->assertInstanceOf(PublicKey::class, $privatekey->getPublicKey());
extract(DSA::createKey($params));
$this->assertInstanceOf('\phpseclib\Crypt\DSA', $privatekey);
$this->assertInstanceOf('\phpseclib\Crypt\DSA', $publickey);
$privatekey = DSA::createKey($params);
$this->assertInstanceOf(PrivateKey::class, $privatekey);
$this->assertInstanceOf(PublicKey::class, $privatekey->getPublicKey());
extract(DSA::createKey(512, 160));
$this->assertInstanceOf('\phpseclib\Crypt\DSA', $privatekey);
$this->assertInstanceOf('\phpseclib\Crypt\DSA', $publickey);
$privatekey = DSA::createKey(512, 160);
$this->assertInstanceOf(PrivateKey::class, $privatekey);
$this->assertInstanceOf(PublicKey::class, $privatekey->getPublicKey());
}
}

View File

@ -5,7 +5,10 @@
* @license http://www.opensource.org/licenses/mit-license.html MIT License
*/
use phpseclib\Crypt\DSA;
use phpseclib\Crypt\PublicKeyLoader;
use phpseclib\Crypt\DSA\PrivateKey;
use phpseclib\Crypt\DSA\PublicKey;
use phpseclib\Crypt\DSA\Parameters;
use phpseclib\Crypt\DSA\Keys\PKCS1;
use phpseclib\Crypt\DSA\Keys\PKCS8;
use phpseclib\Crypt\DSA\Keys\PuTTY;
@ -13,18 +16,17 @@ use phpseclib\Math\BigInteger;
class Unit_Crypt_DSA_LoadKeyTest extends PhpseclibTestCase
{
/**
* @expectedException \phpseclib\Exception\NoKeyLoadedException
*/
public function testBadKey()
{
$dsa = new DSA();
$key = 'zzzzzzzzzzzzzz';
$this->assertFalse($dsa->load($key));
PublicKeyLoader::load($key);
}
public function testPuTTYKey()
{
$dsa = new DSA();
$key = 'PuTTY-User-Key-File-2: ssh-dss
Encryption: none
Comment: dsa-key-20161223
@ -52,23 +54,19 @@ AAAAFFMy7BG9rPXwzqZzIY/lqsHEILNf
Private-MAC: 62b92ddd8b341b9414d640c24ba6ae929a78e039
';
$dsa->setPrivateKeyFormat('PuTTY');
$dsa->setPublicKeyFormat('PuTTY');
$dsa = PublicKeyLoader::load($key);
$this->assertTrue($dsa->load($key));
$this->assertInstanceOf(PrivateKey::class, $dsa);
$this->assertInternalType('string', "$dsa");
$this->assertSame("$dsa", $dsa->getPrivateKey('PuTTY'));
$this->assertInternalType('string', $dsa->getPublicKey('PuTTY'));
$this->assertInternalType('string', $dsa->getParameters());
$this->assertInternalType('string', $dsa->getPublicKey()->toString('PuTTY'));
$this->assertInternalType('string', $dsa->getParameters()->toString('PuTTY'));
$dsa->setPassword('password');
$dsa = $dsa->withPassword('password');
$this->assertGreaterThan(0, strlen("$dsa"));
}
public function testPKCS1Key()
{
$dsa = new DSA();
$key = '-----BEGIN DSA PRIVATE KEY-----
MIIDPQIBAAKCAQEAiwfUDxLuCgQSd5boP/MleHXPKllGUqXDu81onvJeL2+pSQqd
NJcr2VHj+djLhJVNxUCljSwRTZFIOuJ0tPLjRl4w8Csf6zFHuUJJnYC42r2xDG7p
@ -90,20 +88,16 @@ yVFGWdP2B4Gyj85IXCm3r+JNVoV5tVX9IUBTXnUor7YfWNncwWn56Lc+RQIUUzLs
Eb2s9fDOpnMhj+WqwcQgs18=
-----END DSA PRIVATE KEY-----';
$dsa->setPrivateKeyFormat('PKCS1');
$dsa->setPublicKeyFormat('PKCS1');
$dsa = PublicKeyLoader::load($key);
$this->assertTrue($dsa->load($key));
$this->assertInstanceOf(PrivateKey::class, $dsa);
$this->assertInternalType('string', "$dsa");
$this->assertSame("$dsa", $dsa->getPrivateKey('PKCS1'));
$this->assertInternalType('string', $dsa->getPublicKey('PKCS1'));
$this->assertInternalType('string', $dsa->getParameters());
$this->assertInternalType('string', $dsa->getPublicKey()->toString('PKCS1'));
$this->assertInternalType('string', (string) $dsa->getParameters());
}
public function testParameters()
{
$dsa = new DSA();
$key = '-----BEGIN DSA PARAMETERS-----
MIIBHgKBgQDandMycPZNOEwDXpIDSdFODWOQVO5tlnt38wK0X33TJh4wQdqOSiVF
I+g+X8reP43ag3TEHu5bstrk6Znm7y1htTTvXQVTEwp6X3YHXbJG4Faul3g08Vud
@ -114,16 +108,15 @@ L1cwyXx0KMaaampd34MzOIHbC44SHY+cE3aVVUsnmt6Ur1nQaVYVszl+AO6m8bPm
4Vg=
-----END DSA PARAMETERS-----';
$key = str_replace(["\n", "\r"], '', $key);
$dsa = PublicKeyLoader::load($key);
$this->assertTrue($dsa->load($key));
$this->assertInstanceOf(Parameters::class, $dsa);
$this->assertSame($key, str_replace(["\n", "\r"], '', "$dsa"));
$this->assertSame($key, str_replace(["\n", "\r"], '', $dsa->getParameters()));
$this->assertSame($key, str_replace(["\n", "\r"], '', (string) $dsa->getParameters()));
}
public function testPKCS8Public()
{
$dsa = new DSA();
$key = '-----BEGIN PUBLIC KEY-----
MIIBtjCCASsGByqGSM44BAEwggEeAoGBANqd0zJw9k04TANekgNJ0U4NY5BU7m2W
e3fzArRffdMmHjBB2o5KJUUj6D5fyt4/jdqDdMQe7luy2uTpmebvLWG1NO9dBVMT
@ -137,14 +130,14 @@ ZpmyOpXM/0opRMIRdmqVW4ardBFNokmlqngwcbaptfRnk9W2cQtx0lmKy6X/vnis
3AElwP86TYgBhw==
-----END PUBLIC KEY-----';
$this->assertTrue($dsa->load($key));
$dsa = PublicKeyLoader::load($key);
$this->assertInstanceOf(PublicKey::class, $dsa);
$this->assertInternalType('string', "$dsa");
}
public function testPKCS8Private()
{
$dsa = new DSA();
$key = '-----BEGIN PRIVATE KEY-----
MIIBSgIBADCCASsGByqGSM44BAEwggEeAoGBANqd0zJw9k04TANekgNJ0U4NY5BU
7m2We3fzArRffdMmHjBB2o5KJUUj6D5fyt4/jdqDdMQe7luy2uTpmebvLWG1NO9d
@ -155,20 +148,19 @@ rgPJisERm7NDMd6J9o7qUG8NI18vVzDJfHQoxppqal3fgzM4gdsLjhIdj5wTdpVV
Syea3pSvWdBpVhWzOX4A7qbxs+bhWAQWAhQiF7sFfCtZ7oOgCb2aJ9ySC9sTug==
-----END PRIVATE KEY-----';
$this->assertTrue($dsa->load($key));
$dsa = PublicKeyLoader::load($key);
$this->assertInstanceOf(PrivateKey::class, $dsa);
$this->assertInternalType('string', "$dsa");
$this->assertSame("$dsa", $dsa->getPrivateKey());
$this->assertInstanceOf(DSA::class, $dsa->getPublicKey());
$this->assertInternalType('string', $dsa->getParameters());
$this->assertInstanceOf(PublicKey::class, $dsa->getPublicKey());
$this->assertInstanceOf(Parameters::class, $dsa->getParameters());
}
/**
* @expectedException \UnexpectedValueException
* @expectedException \phpseclib\Exception\NoKeyLoadedException
*/
public function testPuTTYBadMAC()
{
$dsa = new DSA();
$key = 'PuTTY-User-Key-File-2: ssh-dss
Encryption: none
Comment: dsa-key-20161223
@ -196,14 +188,11 @@ AAAAFFMy7BG9rPXwzqZzIY/lqsHEILNf
Private-MAC: aaaaaadd8b341b9414d640c24ba6ae929a78e039
';
$this->assertFalse($dsa->load($key));
$dsa->load($key, 'PuTTY');
PublicKeyLoader::load($key);
}
public function testXML()
{
$dsa = new DSA();
$key = '-----BEGIN PUBLIC KEY-----
MIIBtjCCASsGByqGSM44BAEwggEeAoGBANqd0zJw9k04TANekgNJ0U4NY5BU7m2W
e3fzArRffdMmHjBB2o5KJUUj6D5fyt4/jdqDdMQe7luy2uTpmebvLWG1NO9dBVMT
@ -217,13 +206,12 @@ ZpmyOpXM/0opRMIRdmqVW4ardBFNokmlqngwcbaptfRnk9W2cQtx0lmKy6X/vnis
3AElwP86TYgBhw==
-----END PUBLIC KEY-----';
$dsa->load($key);
$xml = $dsa->getPublicKey('XML');
$dsa = PublicKeyLoader::load($key);
$xml = $dsa->toString('XML');
$this->assertContains('DSAKeyValue', $xml);
$dsa = new DSA();
$dsa->load($xml);
$pkcs8 = $dsa->getPublicKey('PKCS8');
$dsa = PublicKeyLoader::load($xml);
$pkcs8 = $dsa->toString('PKCS8');
$this->assertSame(
strtolower(preg_replace('#\s#', '', $pkcs8)),

View File

@ -7,6 +7,7 @@
*/
use phpseclib\Crypt\DSA;
use phpseclib\Crypt\PublicKeyLoader;
class Unit_Crypt_DSA_SignatureTest extends PhpseclibTestCase
{
@ -14,9 +15,7 @@ class Unit_Crypt_DSA_SignatureTest extends PhpseclibTestCase
{
$message = 'hello, world!';
$dsa = new DSA();
$dsa->load('-----BEGIN DSA PRIVATE KEY-----
$dsa = PublicKeyLoader::load('-----BEGIN DSA PRIVATE KEY-----
MIIBvAIBAAKBgQDsGAHAM16bsPlwl7jaec4QMynYa0YLiLiOZC4mvH4UW/tRJxTz
aV7eH1EtnP9D9J78x/07wKYs8zJEWCXmuq0UluQfjA47+pb68b/ucQTNeZHboNN9
5oEi+8BCSK0y8G3uf3Y89qHvqa9Si6rP374MinEMrbVFm+UpsGflFcd83wIVALtJ
@ -27,10 +26,11 @@ CCBGBQJRAoGBALnHTAZlpoLJZuSBVtnMuRM3cSX43IkE9w9FveDV1jX5mmfK7yBV
pQFV8eVJfk91ERQ4Dn6ePLUv2dRIt4a0S0qHqadgzyoFyqkmmUi1kNLyixtRqh+m
2gXx0t63HEpZDbEPppdpnlppZquVQh7TyrKSXW9MTzUkQjFI9UY7kZeKAhQXiJgI
kBniZHdFBAZBTE14YJUBkw==
-----END DSA PRIVATE KEY-----');
$signature = $dsa->sign($message, 'ASN1');
-----END DSA PRIVATE KEY-----')
->withSignatureFormat('ASN1');
$signature = $dsa->sign($message);
$dsa->load('-----BEGIN PUBLIC KEY-----
$dsa = PublicKeyLoader::load('-----BEGIN PUBLIC KEY-----
MIIBuDCCASwGByqGSM44BAEwggEfAoGBAOwYAcAzXpuw+XCXuNp5zhAzKdhrRguI
uI5kLia8fhRb+1EnFPNpXt4fUS2c/0P0nvzH/TvApizzMkRYJea6rRSW5B+MDjv6
lvrxv+5xBM15kdug033mgSL7wEJIrTLwbe5/djz2oe+pr1KLqs/fvgyKcQyttUWb
@ -41,37 +41,36 @@ jhGOrO+kJcZBxUSxINgIIEYFAlEDgYUAAoGBALnHTAZlpoLJZuSBVtnMuRM3cSX4
3IkE9w9FveDV1jX5mmfK7yBVpQFV8eVJfk91ERQ4Dn6ePLUv2dRIt4a0S0qHqadg
zyoFyqkmmUi1kNLyixtRqh+m2gXx0t63HEpZDbEPppdpnlppZquVQh7TyrKSXW9M
TzUkQjFI9UY7kZeK
-----END PUBLIC KEY-----');
-----END PUBLIC KEY-----')
->withSignatureFormat('ASN1');
$this->assertTrue($dsa->verify($message, $signature, 'ASN1'));
$this->assertFalse($dsa->verify('foozbar', $signature, 'ASN1'));
$this->assertTrue($dsa->verify($message, $signature));
$this->assertFalse($dsa->verify('foozbar', $signature));
// openssl dgst -dss1 -sign dsa_priv.pem foo.txt > sigfile.bin
$signature = '302c021456d7e7da10d1538a6cd45dcb2b0ce15c28bac03402147e973a4de1e92e8a87ed5218c797952a3f854df5';
$signature = pack('H*', $signature);
$dsa->setHash('sha1');
$dsa = $dsa->withHash('sha1');
$this->assertTrue($dsa->verify("foobar\n", $signature, 'ASN1'));
$this->assertFalse($dsa->verify('foozbar', $signature, 'ASN1'));
$this->assertTrue($dsa->verify("foobar\n", $signature));
$this->assertFalse($dsa->verify('foozbar', $signature));
// openssl dgst -sha256 -sign dsa_priv.pem foo.txt > sigfile.bin
$signature = '302e021500b131ec2682c4c0be13e6558ba3d64929ebc0ac420215009946300a03561cef50c0a51d0cd0a2c835e798fc';
$signature = pack('H*', $signature);
$dsa->setHash('sha256');
$dsa = $dsa->withHash('sha256');
$this->assertTrue($dsa->verify('abcdefghijklmnopqrstuvwxyz', $signature, 'ASN1'));
$this->assertFalse($dsa->verify('zzzz', $signature, 'ASN1'));
$this->assertTrue($dsa->verify('abcdefghijklmnopqrstuvwxyz', $signature));
$this->assertFalse($dsa->verify('zzzz', $signature));
}
public function testRandomSignature()
{
$message = 'hello, world!';
$dsa = new DSA();
$dsa->load('-----BEGIN DSA PRIVATE KEY-----
$dsa = PublicKeyLoader::load('-----BEGIN DSA PRIVATE KEY-----
MIIBvAIBAAKBgQDsGAHAM16bsPlwl7jaec4QMynYa0YLiLiOZC4mvH4UW/tRJxTz
aV7eH1EtnP9D9J78x/07wKYs8zJEWCXmuq0UluQfjA47+pb68b/ucQTNeZHboNN9
5oEi+8BCSK0y8G3uf3Y89qHvqa9Si6rP374MinEMrbVFm+UpsGflFcd83wIVALtJ
@ -82,9 +81,11 @@ CCBGBQJRAoGBALnHTAZlpoLJZuSBVtnMuRM3cSX43IkE9w9FveDV1jX5mmfK7yBV
pQFV8eVJfk91ERQ4Dn6ePLUv2dRIt4a0S0qHqadgzyoFyqkmmUi1kNLyixtRqh+m
2gXx0t63HEpZDbEPppdpnlppZquVQh7TyrKSXW9MTzUkQjFI9UY7kZeKAhQXiJgI
kBniZHdFBAZBTE14YJUBkw==
-----END DSA PRIVATE KEY-----');
$signature1 = $dsa->sign($message, 'ASN1');
$signature2 = $dsa->sign($message, 'ASN1');
-----END DSA PRIVATE KEY-----')
->withSignatureFormat('ASN1');
$public = $dsa->getPublicKey();
$signature1 = $dsa->sign($message);
$signature2 = $dsa->sign($message);
// phpseclib's DSA implementation uses a CSPRNG to generate the k parameter.
// used correctly this should result in different signatures every time.
@ -93,31 +94,31 @@ kBniZHdFBAZBTE14YJUBkw==
// unit test would need to be updated
$this->assertNotEquals($signature1, $signature2);
$this->assertTrue($dsa->verify($message, $signature1, 'ASN1'));
$this->assertTrue($dsa->verify($message, $signature2, 'ASN1'));
$this->assertTrue($public->verify($message, $signature1));
$this->assertTrue($public->verify($message, $signature2));
$signature = $dsa->sign($message, 'SSH2');
$dsa = $dsa->withSignatureFormat('SSH2');
$public = $public->withSignatureFormat('SSH2');
$pubKey = $dsa->getPublicKey();
$signature = $dsa->sign($message);
$dsa = new DSA();
$dsa->load($pubKey);
$this->assertTrue($dsa->verify($message, $signature, 'SSH2'));
$this->assertTrue($public->verify($message, $signature));
}
public function testSSHSignature()
{
$dsa = new DSA();
$dsa->setHash('sha1');
$dsa->load('AAAAB3NzaC1kc3MAAACBAPyzZzm4oqmY12lxmHwNcfYDNyXr38M1lU6xy9I792U1YSKgX27nUW9eXdJ8Mrn63Le5rrBRfg2Niycx' .
$dsa = PublicKeyLoader::load('AAAAB3NzaC1kc3MAAACBAPyzZzm4oqmY12lxmHwNcfYDNyXr38M1lU6xy9I792U1YSKgX27nUW9eXdJ8Mrn63Le5rrBRfg2Niycx' .
'JF2IwDpwCi7YpIv79uwT3RtA0chQDS4vx8qi8BWBzy7PZC9hmqY62+mgfj8ooga1sr+JpMh+8r4j3KjPM+wE37khkgkvAAAAFQDn' .
'19pBng6TajI/vdg7GPnxsitCqQAAAIEA6Pl1Z/TVdkc+HpfkAvcg2Q+yNtnVq7+26RCbRDO3b9Ocr+tZA9u23qnO3KDYeygzaLnI' .
'gpErp61Bj70iIUldhXy2LFGZFEC9XiKmt/tQxSDKiBbj3bS3wKfHrAlElgjhqxiRh+GixgSsmCj96eJFXcsxPjQU81HR+WJ0ALV1' .
'UnMAAACABRdNuqqe1Y68es8TIflV71P0J7Ci2BbbqAXRwYYKc9/7DrygwaN2UIbMXyOLuojeZgQPPoM9nkzd6QZo8M9apawVKKwD' .
'GAUj2of+F9WVRxhE0ohTQBzD/3HqT80pQsX+rYcxuSx1cCtdMp4oLrrfKO2J4EiWUkaoSB7SdCaj+vU=');
$dsa = $dsa
->withHash('sha1')
->withSignatureFormat('SSH2');
$message = pack('H*', '8bfc69a222c12ddf6bc6bf33c9cadc106af04feb');
$signature = pack('H*', '000000077373682d64737300000028a7a2e55dc43e5e6145aa94daa0552ea479d1139d6d6ba50650b489e24e976593e73f76557813d6bc');
$this->assertTrue($dsa->verify($message, $signature, 'SSH2'));
$this->assertTrue($dsa->verify($message, $signature));
}
}

View File

@ -10,6 +10,7 @@ use phpseclib\Crypt\ECDSA;
use phpseclib\File\ASN1;
use phpseclib\Crypt\ECDSA\Curves\Ed448;
use phpseclib\Math\BigInteger;
use phpseclib\Crypt\PublicKeyLoader;
class Ed448PublicKey
{
@ -167,7 +168,8 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$plaintext = 'zzz';
ECDSA::useInternalEngine();
extract(ECDSA::createKey($name));
$privatekey = ECDSA::createKey($name);
$publickey = $privatekey->getPublicKey();
$sig = $privatekey->sign($plaintext);
ECDSA::useBestEngine();
@ -189,7 +191,8 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$plaintext = 'zzz';
ECDSA::useBestEngine();
extract(ECDSA::createKey($name));
$privatekey = ECDSA::createKey($name);
$publickey = $privatekey->getPublicKey();
$sig = $privatekey->sign($plaintext);
ECDSA::useInternalEngine();
@ -207,11 +210,8 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', '6c82a562cb808d10d632be89c8513ebf6c929f34ddfa8c9f63c9960ef6e348a3528c8a3fcc2f044e39a3fc5b94492f8f032e7549a20098f95b');
$public = pack('H*', '5fd7449b59b461fd2ce787ec616ad46a1da1342485a70e1f8a0ea75d80e96778edf124769b46c7061bd6783df1e50f6cd1fa1abeafe8256180');
$privateKey = new ECDSA();
$privateKey->load($private);
$publicKey = new ECDSA();
$publicKey->load($public);
$privateKey = PublicKeyLoader::load($private);
$publicKey = PublicKeyLoader::load($public);
$expected = '533a37f6bbe457251f023c0d88f976ae2dfb504a843e34d2074fd823d41a591f2b233f034f628281f2fd7a22ddd47d7828c59bd0a21bfd3980' .
'ff0d2028d4b18a9df63e006c5d1c2d345b925d8dc00b4104852db99ac5c7cdda8530a113a0f4dbb61149f05a7363268c71d95808ff2e652600';
@ -221,19 +221,16 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', 'c4eab05d357007c632f3dbb48489924d552b08fe0c353a0d4a1f00acda2c463afbea67c5e8d2877c5e3bc397a659949ef8021e954e0a12274e');
$public = pack('H*', '43ba28f430cdff456ae531545f7ecd0ac834a55d9358c0372bfa0c6c6798c0866aea01eb00742802b8438ea4cb82169c235160627b4c3a9480');
$privateKey = new ECDSA();
$privateKey->load($private);
$publicKey = new ECDSA();
$publicKey->load($public);
$privateKey = PublicKeyLoader::load($private);
$publicKey = PublicKeyLoader::load($public);
$expected = '26b8f91727bd62897af15e41eb43c377efb9c610d48f2335cb0bd0087810f4352541b143c4b981b7e18f62de8ccdf633fc1bf037ab7cd77980' .
'5e0dbcc0aae1cbcee1afb2e027df36bc04dcecbf154336c19f0af7e0a6472905e799f1953d2a0ff3348ab21aa4adafd1d234441cf807c03a00';
$this->assertSame($expected, bin2hex($sig = $privateKey->sign("\x03")));
$this->assertTrue($publicKey->verify("\x03", $sig));
$publicKey->setContext(pack('H*', '666f6f'));
$privateKey->setContext(pack('H*', '666f6f'));
$publicKey = $publicKey->withContext(pack('H*', '666f6f'));
$privateKey = $privateKey->withContext(pack('H*', '666f6f'));
$expected = 'd4f8f6131770dd46f40867d6fd5d5055de43541f8c5e35abbcd001b32a89f7d2151f7647f11d8ca2ae279fb842d607217fce6e042f6815ea00' .
'0c85741de5c8da1144a6a1aba7f96de42505d7a7298524fda538fccbbb754f578c1cad10d54d0d5428407e85dcbc98a49155c13764e66c3c00';
@ -243,11 +240,8 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', '258cdd4ada32ed9c9ff54e63756ae582fb8fab2ac721f2c8e676a72768513d939f63dddb55609133f29adf86ec9929dccb52c1c5fd2ff7e21b');
$public = pack('H*', '3ba16da0c6f2cc1f30187740756f5e798d6bc5fc015d7c63cc9510ee3fd44adc24d8e968b6e46e6f94d19b945361726bd75e149ef09817f580');
$privateKey = new ECDSA();
$privateKey->load($private);
$publicKey = new ECDSA();
$publicKey->load($public);
$privateKey = PublicKeyLoader::load($private);
$publicKey = PublicKeyLoader::load($public);
$message = pack('H*', '64a65f3cdedcdd66811e2915');
@ -259,11 +253,8 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', '7ef4e84544236752fbb56b8f31a23a10e42814f5f55ca037cdcc11c64c9a3b2949c1bb60700314611732a6c2fea98eebc0266a11a93970100e');
$public = pack('H*', 'b3da079b0aa493a5772029f0467baebee5a8112d9d3a22532361da294f7bb3815c5dc59e176b4d9f381ca0938e13c6c07b174be65dfa578e80');
$privateKey = new ECDSA();
$privateKey->load($private);
$publicKey = new ECDSA();
$publicKey->load($public);
$privateKey = PublicKeyLoader::load($private);
$publicKey = PublicKeyLoader::load($public);
$message = pack('H*', '64a65f3cdedcdd66811e2915e7');
@ -275,11 +266,8 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', 'd65df341ad13e008567688baedda8e9dcdc17dc024974ea5b4227b6530e339bff21f99e68ca6968f3cca6dfe0fb9f4fab4fa135d5542ea3f01');
$public = pack('H*', 'df9705f58edbab802c7f8363cfe5560ab1c6132c20a9f1dd163483a26f8ac53a39d6808bf4a1dfbd261b099bb03b3fb50906cb28bd8a081f00');
$privateKey = new ECDSA();
$privateKey->load($private);
$publicKey = new ECDSA();
$publicKey->load($public);
$privateKey = PublicKeyLoader::load($private);
$publicKey = PublicKeyLoader::load($public);
$message = 'bd0f6a3747cd561bdddf4640a332461a4a30a12a434cd0bf40d766d9c6d458e5512204a30c17d1f50b5079631f64eb3112182da3005835461113718d1a5ef944';
$message = pack('H*', $message);
@ -292,11 +280,8 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', '2ec5fe3c17045abdb136a5e6a913e32ab75ae68b53d2fc149b77e504132d37569b7e766ba74a19bd6162343a21c8590aa9cebca9014c636df5');
$public = pack('H*', '79756f014dcfe2079f5dd9e718be4171e2ef2486a08f25186f6bff43a9936b9bfe12402b08ae65798a3d81e22e9ec80e7690862ef3d4ed3a00');
$privateKey = new ECDSA();
$privateKey->load($private);
$publicKey = new ECDSA();
$publicKey->load($public);
$privateKey = PublicKeyLoader::load($private);
$publicKey = PublicKeyLoader::load($public);
$message = '15777532b0bdd0d1389f636c5f6b9ba734c90af572877e2d272dd078aa1e567cfa80e12928bb542330e8409f3174504107ecd5efac61ae7504dabe2a602ede89e5cca6257a7c77e27a702b3ae39fc769fc54f2395ae6a1178cab4738e543072fc1c177fe71e92e25bf03e4ecb72f47b64d0465aaea4c7fad372536c8ba516a60' .
'39c3c2a39f0e4d832be432dfa9a706a6e5c7e19f397964ca4258002f7c0541b590316dbc5622b6b2a6fe7a4abffd96105eca76ea7b98816af0748c10df048ce012d901015a51f189f3888145c03650aa23ce894c3bd889e030d565071c59f409a9981b51878fd6fc110624dcbcde0bf7a69ccce38fabdf86f3bef6044819de11';
@ -310,11 +295,8 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', '872d093780f5d3730df7c212664b37b8a0f24f56810daa8382cd4fa3f77634ec44dc54f1c2ed9bea86fafb7632d8be199ea165f5ad55dd9ce8');
$public = pack('H*', 'a81b2e8a70a5ac94ffdbcc9badfc3feb0801f258578bb114ad44ece1ec0e799da08effb81c5d685c0c56f64eecaef8cdf11cc38737838cf400');
$privateKey = new ECDSA();
$privateKey->load($private);
$publicKey = new ECDSA();
$publicKey->load($public);
$privateKey = PublicKeyLoader::load($private);
$publicKey = PublicKeyLoader::load($public);
$message = '6ddf802e1aae4986935f7f981ba3f0351d6273c0a0c22c9c0e8339168e675412a3debfaf435ed651558007db4384b650fcc07e3b586a27a4f7a00ac8a6fec2cd86ae4bf1570c41e6a40c931db27b2faa15a8cedd52cff7362c4e6e23daec0fbc3a79b6806e316efcc7b68119bf46bc76a26067a53f296dafdbdc11c77f7777e9' .
'72660cf4b6a9b369a6665f02e0cc9b6edfad136b4fabe723d2813db3136cfde9b6d044322fee2947952e031b73ab5c603349b307bdc27bc6cb8b8bbd7bd323219b8033a581b59eadebb09b3c4f3d2277d4f0343624acc817804728b25ab797172b4c5c21a22f9c7839d64300232eb66e53f31c723fa37fe387c7d3e50bdf9813' .
@ -342,12 +324,8 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', '9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60');
$public = pack('H*', 'd75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a');
$privateKey = new ECDSA();
// libsodium format
$privateKey->load($private . $public);
$publicKey = new ECDSA();
$publicKey->load($public);
$privateKey = PublicKeyLoader::load($private . $public); // libsodium format
$publicKey = PublicKeyLoader::load($public);
$expected = 'e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e06522490155' .
'5fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b';
@ -357,11 +335,8 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', '4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb');
$public = pack('H*', '3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c');
$privateKey = new ECDSA();
$privateKey->load($private . $public);
$publicKey = new ECDSA();
$publicKey->load($public);
$privateKey = PublicKeyLoader::load($private . $public);
$publicKey = PublicKeyLoader::load($public);
$expected = '92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da' .
'085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00';
@ -371,11 +346,8 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', 'c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7');
$public = pack('H*', 'fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025');
$privateKey = new ECDSA();
$privateKey->load($private . $public);
$publicKey = new ECDSA();
$publicKey->load($public);
$privateKey = PublicKeyLoader::load($private . $public); // libsodium format
$publicKey = PublicKeyLoader::load($public);
$expected = '6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac' .
'18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a';
@ -385,11 +357,8 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', 'f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5');
$public = pack('H*', '278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e');
$privateKey = new ECDSA();
$privateKey->load($private . $public);
$publicKey = new ECDSA();
$publicKey->load($public);
$privateKey = PublicKeyLoader::load($private . $public); // libsodium format
$publicKey = PublicKeyLoader::load($public);
$message = '08b8b2b733424243760fe426a4b54908632110a66c2f6591eabd3345e3e4eb98' .
'fa6e264bf09efe12ee50f8f54e9f77b1e355f6c50544e23fb1433ddf73be84d8' .
@ -433,11 +402,8 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', '833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42');
$public = pack('H*', 'ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf');
$privateKey = new ECDSA();
$privateKey->load($private . $public);
$publicKey = new ECDSA();
$publicKey->load($public);
$privateKey = PublicKeyLoader::load($private . $public);
$publicKey = PublicKeyLoader::load($public);
$message = 'ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a' .
'2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f';
@ -451,14 +417,11 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', '0305334e381af78f141cb666f6199f57bc3495335a256a95bd2a55bf546663f6');
$public = pack('H*', 'dfc9425e4f968f7f0c29f0259cf5f9aed6851c2bb4ad8bfb860cfee0ab248292');
$privateKey = new ECDSA();
$privateKey->load($private . $public);
$privateKey = PublicKeyLoader::load($private . $public);
$publicKey = PublicKeyLoader::load($public);
$publicKey = new ECDSA();
$publicKey->load($public);
$privateKey->setContext("\x62\x61\x72");
$publicKey->setContext("\x62\x61\x72");
$privateKey = $privateKey->withContext("\x62\x61\x72");
$publicKey = $publicKey->withContext("\x62\x61\x72");
$message = 'f726936d19c800494e3fdaff20b276a8';
$message = pack('H*', $message);
@ -471,14 +434,11 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', '0305334e381af78f141cb666f6199f57bc3495335a256a95bd2a55bf546663f6');
$public = pack('H*', 'dfc9425e4f968f7f0c29f0259cf5f9aed6851c2bb4ad8bfb860cfee0ab248292');
$privateKey = new ECDSA();
$privateKey->load($private . $public);
$privateKey = PublicKeyLoader::load($private . $public);
$publicKey = PublicKeyLoader::load($public);
$publicKey = new ECDSA();
$publicKey->load($public);
$privateKey->setContext("\x66\x6f\x6f");
$publicKey->setContext("\x66\x6f\x6f");
$privateKey = $privateKey->withContext("\x66\x6f\x6f");
$publicKey = $publicKey->withContext("\x66\x6f\x6f");
$message = '508e9e6882b979fea900f62adceaca35';
$message = pack('H*', $message);
@ -491,14 +451,11 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', 'ab9c2853ce297ddab85c993b3ae14bcad39b2c682beabc27d6d4eb20711d6560');
$public = pack('H*', '0f1d1274943b91415889152e893d80e93275a1fc0b65fd71b4b0dda10ad7d772');
$privateKey = new ECDSA();
$privateKey->load($private . $public);
$privateKey = PublicKeyLoader::load($private . $public);
$publicKey = PublicKeyLoader::load($public);
$publicKey = new ECDSA();
$publicKey->load($public);
$privateKey->setContext("\x66\x6f\x6f");
$publicKey->setContext("\x66\x6f\x6f");
$privateKey = $privateKey->withContext("\x66\x6f\x6f");
$publicKey = $publicKey->withContext("\x66\x6f\x6f");
$message = 'f726936d19c800494e3fdaff20b276a8';
$message = pack('H*', $message);
@ -512,8 +469,7 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
public function testRandomSignature()
{
$message = 'hello, world!';
$private = new ECDSA();
$private->load('PuTTY-User-Key-File-2: ecdsa-sha2-nistp256
$private = PublicKeyLoader::load('PuTTY-User-Key-File-2: ecdsa-sha2-nistp256
Encryption: none
Comment: ecdsa-key-20181105
Public-Lines: 3

View File

@ -11,28 +11,27 @@ use phpseclib\Crypt\ECDSA\Keys\PKCS8;
use phpseclib\Crypt\ECDSA\Keys\PuTTY;
use phpseclib\Crypt\ECDSA\Keys\OpenSSH;
use phpseclib\Crypt\ECDSA\Keys\XML;
use phpseclib\Crypt\PublicKeyLoader;
class Unit_Crypt_ECDSA_LoadKeyTest extends PhpseclibTestCase
{
// openssl ecparam -name secp256k1 -genkey -noout -out secp256k1.pem
public function testPKCS1PrivateKey()
{
$key = new ECDSA;
$key->load($expected = '-----BEGIN EC PRIVATE KEY-----
$key = PublicKeyLoader::load($expected = '-----BEGIN EC PRIVATE KEY-----
MHQCAQEEIEzUawcXqUsQhaEQ51JLeOIY0ddzlO2nNgwDk32ETqwkoAcGBSuBBAAK
oUQDQgAEFuVcVb9iCUhg2cknHPE+BouHGhQ39ORjMaMI3T4RfRxr6dj5HAXdEqVZ
1W94KMe30ndmTndcJ8BPeT1Dd15FdQ==
-----END EC PRIVATE KEY-----');
$this->assertSame('secp256k1', $key->getCurve());
//PKCS1::useNamedCurve();
$this->assertSame($expected, $key->getPrivateKey('PKCS1'));
$this->assertSame($expected, $key->toString('PKCS1'));
}
// openssl ecparam -name secp256k1 -genkey -noout -out secp256k1.pem -param_enc explicit
public function testPKCS1PrivateKeySpecifiedCurve()
{
$key = new ECDSA;
$key->load('-----BEGIN EC PRIVATE KEY-----
$key = PublicKeyLoader::load('-----BEGIN EC PRIVATE KEY-----
MIIBEwIBAQQgFr6TF5meGfgCXDqVxoSEltGI+T94G42PPbA6/ibq+ouggaUwgaIC
AQEwLAYHKoZIzj0BAQIhAP////////////////////////////////////7///wv
MAYEAQAEAQcEQQR5vmZ++dy7rFWgYpXOhwsHApv82y3OKNlZ8oFbFvgXmEg62ncm
@ -61,29 +60,27 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAABwRBBHm+Zn753LusVaBilc6HCwcCm/zbLc4o
E5w=
-----END EC PRIVATE KEY-----';
PKCS1::useSpecifiedCurve();
$this->assertSame($expected, $key->getPrivateKey('PKCS1'));
$this->assertSame($expected, $key->toString('PKCS1'));
}
// openssl ecparam -name secp256k1 -genkey -noout -out secp256k1.pem
// openssl pkcs8 -topk8 -nocrypt -in secp256k1.pem -out secp256k1-2.pem
public function testPKCS8PrivateKey()
{
$key = new ECDSA;
$key->load($expected = '-----BEGIN PRIVATE KEY-----
$key = PublicKeyLoader::load($expected = '-----BEGIN PRIVATE KEY-----
MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgAYCXwnhqMT6fCIKIkQ0w
cac7QqHrn4TCQMF9a+im74WhRANCAATwCjyGuP8xQbvVjznqazL36oeAnD32I+X2
+wscW3OmyTDpk41HaWYPh+j+BoufsSkCwf8dBRGEQbCieZbbZogy
-----END PRIVATE KEY-----');
$this->assertSame('secp256k1', $key->getCurve());
$this->assertSame($expected, $key->getPrivateKey('PKCS8'));
$this->assertSame($expected, $key->toString('PKCS8'));
}
// openssl ecparam -name secp256k1 -genkey -noout -out secp256k1.pem -param_enc explicit
// openssl pkcs8 -topk8 -nocrypt -in secp256k1.pem -out secp256k1-2.pem
public function testPKCS8PrivateKeySpecifiedCurve()
{
$key = new ECDSA;
$key->load('-----BEGIN PRIVATE KEY-----
$key = PublicKeyLoader::load('-----BEGIN PRIVATE KEY-----
MIIBIwIBADCBrgYHKoZIzj0CATCBogIBATAsBgcqhkjOPQEBAiEA////////////
/////////////////////////v///C8wBgQBAAQBBwRBBHm+Zn753LusVaBilc6H
CwcCm/zbLc4o2VnygVsW+BeYSDradyajxGVdpPv8DhEIqP0XtEimhVQZnEfQj/sQ
@ -107,28 +104,26 @@ AASdfrr5QLNRbdP9+QsYgh9mMmblsgzABXzkukOibaEjjjUlHH79bhaq0a5b4H8s
AFLpken6rN6lOEIeyNLdD097
-----END PRIVATE KEY-----';
PKCS8::useSpecifiedCurve();
$this->assertSame($expected, $key->getPrivateKey('PKCS8'));
$this->assertSame($expected, $key->toString('PKCS8'));
}
// openssl ecparam -name sect113r1 -genkey -noout -out sect113r1.pem
public function testBinaryPKCS1PrivateKey()
{
$key = new ECDSA;
$key->load($expected = '-----BEGIN EC PRIVATE KEY-----
$key = PublicKeyLoader::load($expected = '-----BEGIN EC PRIVATE KEY-----
MEECAQEEDwBZdP4eSzKk/uQa6jdtfKAHBgUrgQQABKEiAyAABAHqCoNb++mK5qvE
c4rCzQEuI19czqvXpEPcAWSXew==
-----END EC PRIVATE KEY-----');
$this->assertSame('sect113r1', $key->getCurve());
PKCS1::useNamedCurve();
$this->assertSame($expected, $key->getPrivateKey('PKCS1'));
$this->assertSame($expected, $key->toString('PKCS1'));
}
// openssl ecparam -name sect113r1 -genkey -noout -out sect113r1.pem -param_enc explicit
public function testBinaryPKCS1PrivateKeySpecifiedCurve()
{
$key = new ECDSA;
$key->load('-----BEGIN EC PRIVATE KEY-----
$key = PublicKeyLoader::load('-----BEGIN EC PRIVATE KEY-----
MIHNAgEBBA8AuSc4BeeyYTq9rbSDuL2ggZIwgY8CAQEwHAYHKoZIzj0BAjARAgFx
BgkqhkjOPQECAwICAQkwNwQOMIglDKbnx/5knOhYIPcEDui+5NPiJgdEGIvg6ccj
AxUAEOcjqxTWluZ2h1YVF1b+v4/LSakEHwQAnXNhbzX0qxQH1zViwQ8ApSgwJ3lY
@ -149,7 +144,7 @@ BACdc2FvNfSrFAfXNWLBDwClKDAneVjuhNExXtMYhgIPAQAAAAAAAADZzOyKOeVv
oSIDIAAEAULtznTLu7D6K4d4wK1bAKko0FRxV6IeZ7rT0O/+
-----END EC PRIVATE KEY-----';
PKCS1::useSpecifiedCurve();
$this->assertSame($expected, $key->getPrivateKey('PKCS1'));
$this->assertSame($expected, $key->toString('PKCS1'));
}
// openssl ecparam -name sect113r1 -genkey -noout -out sect113r1.pem
@ -157,23 +152,21 @@ oSIDIAAEAULtznTLu7D6K4d4wK1bAKko0FRxV6IeZ7rT0O/+
// sect113r1's reduction polynomial is a trinomial
public function testBinaryPKCS8PrivateKey()
{
$key = new ECDSA;
$key->load($expected = '-----BEGIN PRIVATE KEY-----
$key = PublicKeyLoader::load($expected = '-----BEGIN PRIVATE KEY-----
MFECAQAwEAYHKoZIzj0CAQYFK4EEAAQEOjA4AgEBBA8A5OuqAY8HYoFOaz9mE6mh
IgMgAAQASF3rOTPXvH0QdRBvsrMBdLMf27yd8AWABrZTxvI=
-----END PRIVATE KEY-----');
$this->assertSame('sect113r1', $key->getCurve());
PKCS8::useNamedCurve();
$this->assertSame($expected, $key->getPrivateKey('PKCS8'));
$this->assertSame($expected, $key->toString('PKCS8'));
}
// openssl ecparam -name sect113r1 -genkey -noout -out sect113r1.pem -param_enc explicit
// openssl pkcs8 -topk8 -nocrypt -in sect113r1.pem -out sect113r1-2.pem
public function testBinaryPKCS8PrivateKeySpecifiedCurve()
{
$key = new ECDSA;
$key->load('-----BEGIN PRIVATE KEY-----
$key = PublicKeyLoader::load('-----BEGIN PRIVATE KEY-----
MIHdAgEAMIGbBgcqhkjOPQIBMIGPAgEBMBwGByqGSM49AQIwEQIBcQYJKoZIzj0B
AgMCAgEJMDcEDjCIJQym58f+ZJzoWCD3BA7ovuTT4iYHRBiL4OnHIwMVABDnI6sU
1pbmdodWFRdW/r+Py0mpBB8EAJ1zYW819KsUB9c1YsEPAKUoMCd5WO6E0TFe0xiG
@ -193,15 +186,14 @@ BA8AXtfDMRsRTx8snPbWHquhIgMgAAQA9xdWGJ6vV23+vkdq0C8BLJVg5E3amMyf
/5keGa4=
-----END PRIVATE KEY-----';
PKCS8::useSpecifiedCurve();
$this->assertSame($expected, $key->getPrivateKey('PKCS8'));
$this->assertSame($expected, $key->toString('PKCS8'));
}
// openssl ecparam -name sect131r1 -genkey -noout -out sect131r1.pem -param_enc explicit
// sect131r1's reduction polynomial is a pentanomial
public function testBinaryPentanomialPKCS1PrivateKey()
{
$key = new ECDSA;
$key->load('-----BEGIN EC PRIVATE KEY-----
$key = PublicKeyLoader::load('-----BEGIN EC PRIVATE KEY-----
MIHoAgEBBBECPEK9NCISWf2riBsORoTM+6CBpzCBpAIBATAlBgcqhkjOPQECMBoC
AgCDBgkqhkjOPQECAwMwCQIBAgIBAwIBCDA9BBEHoRsJp2tWIURBj/P/jCVwuAQR
AhfAVhCIS2O5xscpFnj500EDFQBNaW5naHVhUXWYW9OtutohtDqX4gQjBACBuvkf
@ -221,14 +213,13 @@ SxtO+eFQAhEEAAAAAAAAAAIxI5U6lGS1TaEmAyQABARCKJRo6OZZ7GKjWoKmDzmh
BjoJZJZQztmlj7Qep/sf1l8=
-----END EC PRIVATE KEY-----';
PKCS1::useSpecifiedCurve();
$this->assertSame($expected, $key->getPrivateKey('PKCS1'));
$this->assertSame($expected, $key->toString('PKCS1'));
}
// from https://tools.ietf.org/html/draft-ietf-curdle-pkix-07#section-10.1
public function testEd25519PublicKey()
{
$key = new ECDSA;
$key->load('-----BEGIN PUBLIC KEY-----
$key = PublicKeyLoader::load('-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE=
-----END PUBLIC KEY-----');
$this->assertSame('Ed25519', $key->getCurve());
@ -240,23 +231,21 @@ MCowBQYDK2VwAyEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE=
$expected = '-----BEGIN PUBLIC KEY-----
MCwwBwYDK2VwBQADIQAZv0QJaYTN/oVBusFn3DuWyFCGqjC2tssMXDitcDFm4Q==
-----END PUBLIC KEY-----';
$this->assertSame($expected, $key->getPublicKey('PKCS8'));
$this->assertSame($expected, $key->toString('PKCS8'));
}
// from https://tools.ietf.org/html/draft-ietf-curdle-pkix-07#section-10.3
public function testEd25519PrivateKey()
{
// without public key (public key should be derived)
$key = new ECDSA;
$key->load('-----BEGIN PRIVATE KEY-----
$key = PublicKeyLoader::load('-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC
-----END PRIVATE KEY-----');
$this->assertSame('Ed25519', $key->getCurve());
$this->assertSame('Ed25519', $key->getPublicKey()->getCurve());
// with public key
$key = new ECDSA;
$key->load('-----BEGIN PRIVATE KEY-----
$key = PublicKeyLoader::load('-----BEGIN PRIVATE KEY-----
MHICAQEwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC
oB8wHQYKKoZIhvcNAQkJFDEPDA1DdXJkbGUgQ2hhaXJzgSEAGb9ECWmEzf6FQbrB
Z9w7lshQhqowtrbLDFw4rXAxZuE=
@ -271,13 +260,12 @@ Z9w7lshQhqowtrbLDFw4rXAxZuE=
MFICAQEwBwYDK2VwBQAEIgQg1O5y2/kTWErVttjx92n4rTr+fCjL8dT74Jeoj0R1
WEKBIBm/RAlphM3+hUG6wWfcO5bIUIaqMLa2ywxcOK1wMWbh
-----END PRIVATE KEY-----';
$this->assertSame($expected, $key->getPrivateKey('PKCS8'));
$this->assertSame($expected, $key->toString('PKCS8'));
}
public function testPuTTYnistp256()
{
$key = new ECDSA;
$key->load($expected = 'PuTTY-User-Key-File-2: ecdsa-sha2-nistp256
$key = PublicKeyLoader::load($expected = 'PuTTY-User-Key-File-2: ecdsa-sha2-nistp256
Encryption: none
Comment: ecdsa-key-20181105
Public-Lines: 3
@ -291,20 +279,18 @@ Private-MAC: b85ca0eb7c612df5d18af85128821bd53faaa3ef
$this->assertSame('nistp256', $key->getCurve());
PuTTY::setComment('ecdsa-key-20181105');
$this->assertSame($expected, $key->getPrivateKey('PuTTY'));
$this->assertSame($expected, $key->toString('PuTTY'));
$key = new ECDSA;
$key->load($expected = 'ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJEXCsWA8s18m25MJlVE1urbXPYFi4q8oMbb2H0kE2f5WPxizsKXRmb1J68paXQizryL9fC4FTqICJ1+UnaPfk0= ecdsa-key-20181105');
$key = PublicKeyLoader::load($expected = 'ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJEXCsWA8s18m25MJlVE1urbXPYFi4q8oMbb2H0kE2f5WPxizsKXRmb1J68paXQizryL9fC4FTqICJ1+UnaPfk0= ecdsa-key-20181105');
$this->assertSame('nistp256', $key->getCurve());
OpenSSH::setComment('ecdsa-key-20181105');
$this->assertSame($expected, $key->getPublicKey('OpenSSH'));
$this->assertSame($expected, $key->toString('OpenSSH'));
}
public function testPuTTYnistp384()
{
$key = new ECDSA;
$key->load($expected = 'PuTTY-User-Key-File-2: ecdsa-sha2-nistp384
$key = PublicKeyLoader::load($expected = 'PuTTY-User-Key-File-2: ecdsa-sha2-nistp384
Encryption: none
Comment: ecdsa-key-20181105
Public-Lines: 3
@ -319,21 +305,19 @@ Private-MAC: 97a990a3d5f6b8f268d4be9c4ab9ebfd8fa79849
$this->assertSame('nistp384', $key->getCurve());
PuTTY::setComment('ecdsa-key-20181105');
$this->assertSame($expected, $key->getPrivateKey('PuTTY'));
$this->assertSame($expected, $key->toString('PuTTY'));
$key = new ECDSA;
$key->load($expected = 'ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBOI53wHG3CdcAJZq5PXWZAEAxxsNVFQlQgOX9toWEOgqQF5LbK2nWLKRvaHMzocUXaTYZDccSS0ATZFPT3j1Er1LU9cu4PHpyS07v262jdzkxIvKCPcAeISuV80MC7rHog== ecdsa-key-20181105');
$key = PublicKeyLoader::load($expected = 'ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBOI53wHG3CdcAJZq5PXWZAEAxxsNVFQlQgOX9toWEOgqQF5LbK2nWLKRvaHMzocUXaTYZDccSS0ATZFPT3j1Er1LU9cu4PHpyS07v262jdzkxIvKCPcAeISuV80MC7rHog== ecdsa-key-20181105');
$this->assertSame('nistp384', $key->getCurve());
OpenSSH::setComment('ecdsa-key-20181105');
$this->assertSame($expected, $key->getPublicKey('OpenSSH'));
$this->assertSame($expected, $key->toString('OpenSSH'));
}
public function testPuTTYnistp521()
{
$key = new ECDSA;
$key->load($expected = 'PuTTY-User-Key-File-2: ecdsa-sha2-nistp521
$key = PublicKeyLoader::load($expected = 'PuTTY-User-Key-File-2: ecdsa-sha2-nistp521
Encryption: none
Comment: ecdsa-key-20181105
Public-Lines: 4
@ -349,20 +333,18 @@ Private-MAC: 6d49ce289b85549a43d74422dd8bb3ba8798c72c
$this->assertSame('nistp521', $key->getCurve());
PuTTY::setComment('ecdsa-key-20181105');
$this->assertSame($expected, $key->getPrivateKey('PuTTY'));
$this->assertSame($expected, $key->toString('PuTTY'));
$key = new ECDSA;
$key->load($expected = 'ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAF1Eg0MjaJwooFj6HCNh4RWbvmQRY+sdczJyBdT3EaTc/6IUcCfW7w7rAeRp2CDdE9RlAVD8IuLqW7DJH06Xeov8wBO5G6jUqXu0rlHsOSiC6VcCxBJuWVNB1IorHnS7PX0f6HdLlIEme73P77drqpn5YY0XLtP6hFrF7H5XfCxpNyaJA== ecdsa-key-20181105');
$key = PublicKeyLoader::load($expected = 'ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAF1Eg0MjaJwooFj6HCNh4RWbvmQRY+sdczJyBdT3EaTc/6IUcCfW7w7rAeRp2CDdE9RlAVD8IuLqW7DJH06Xeov8wBO5G6jUqXu0rlHsOSiC6VcCxBJuWVNB1IorHnS7PX0f6HdLlIEme73P77drqpn5YY0XLtP6hFrF7H5XfCxpNyaJA== ecdsa-key-20181105');
$this->assertSame('nistp521', $key->getCurve());
OpenSSH::setComment('ecdsa-key-20181105');
$this->assertSame($expected, $key->getPublicKey('OpenSSH'));
$this->assertSame($expected, $key->toString('OpenSSH'));
}
public function testPuTTYed25519()
{
$key = new ECDSA;
$key->load($expected = 'PuTTY-User-Key-File-2: ssh-ed25519
$key = PublicKeyLoader::load($expected = 'PuTTY-User-Key-File-2: ssh-ed25519
Encryption: none
Comment: ed25519-key-20181105
Public-Lines: 2
@ -375,14 +357,13 @@ Private-MAC: 8a06821a1c8b8b40fc40f876e543c4ea3fb81bb9
$this->assertSame('Ed25519', $key->getCurve());
PuTTY::setComment('ed25519-key-20181105');
$this->assertSame($expected, $key->getPrivateKey('PuTTY'));
$this->assertSame($expected, $key->toString('PuTTY'));
$key = new ECDSA;
$key->load($expected = 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC6I6RyYAqtBcWXws9EDqGbhFtc5rKG4NMn/G7temQtu ed25519-key-20181105');
$key = PublicKeyLoader::load($expected = 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC6I6RyYAqtBcWXws9EDqGbhFtc5rKG4NMn/G7temQtu ed25519-key-20181105');
$this->assertSame('Ed25519', $key->getCurve());
OpenSSH::setComment('ed25519-key-20181105');
$this->assertSame($expected, $key->getPublicKey('OpenSSH'));
$this->assertSame($expected, $key->toString('OpenSSH'));
}
public function testlibsodium()
@ -393,22 +374,19 @@ Private-MAC: 8a06821a1c8b8b40fc40f876e543c4ea3fb81bb9
$kp = sodium_crypto_sign_keypair();
$key = new ECDSA;
$key->load($expected = sodium_crypto_sign_secretkey($kp));
$key = PublicKeyLoader::load($expected = sodium_crypto_sign_secretkey($kp));
$this->assertSame('Ed25519', $key->getCurve());
$this->assertSame($expected, $key->getPrivateKey('libsodium'));
$this->assertSame($expected, $key->toString('libsodium'));
$key = new ECDSA;
$key->load($expected = sodium_crypto_sign_publickey($kp));
$key = PublicKeyLoader::load($expected = sodium_crypto_sign_publickey($kp));
$this->assertSame('Ed25519', $key->getCurve());
$this->assertSame($expected, $key->getPublicKey('libsodium'));
$this->assertSame($expected, $key->toString('libsodium'));
}
// ssh-keygen -t ed25519
public function testOpenSSHPrivateKey()
{
$key = new ECDSA;
$key->load('-----BEGIN OPENSSH PRIVATE KEY-----
$key = PublicKeyLoader::load('-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACCpm7dS1/WDTW+uuhp2+aFLPKaJle6+oJqDGLXhlQAX4AAAAJg8TmN5PE5j
eQAAAAtzc2gtZWQyNTUxOQAAACCpm7dS1/WDTW+uuhp2+aFLPKaJle6+oJqDGLXhlQAX4A
@ -424,16 +402,14 @@ pomV7r6gmoMYteGVABfgAAAAD3ZhZ3JhbnRAdmFncmFudAECAwQFBg==
// support encrypted keys
// none-the-less, because of the randomized component we can't easily
// see if the key string is equal to another known string
$key2 = new ECDSA;
$key2->load($key->getPrivateKey('OpenSSH'));
$key2 = PublicKeyLoader::load($key->toString('OpenSSH'));
$this->assertSame('Ed25519', $key2->getCurve());
}
// from https://www.w3.org/TR/xmldsig-core/#sec-RFC4050Compat
public function testXMLKey()
{
$key = new ECDSA;
$key->load($orig = '<ECDSAKeyValue xmlns="http://www.w3.org/2001/04/xmldsig-more#">
$key = PublicKeyLoader::load($orig = '<ECDSAKeyValue xmlns="http://www.w3.org/2001/04/xmldsig-more#">
<DomainParameters>
<NamedCurve URN="urn:oid:1.2.840.10045.3.1.7" />
</DomainParameters>
@ -453,22 +429,12 @@ pomV7r6gmoMYteGVABfgAAAAD3ZhZ3JhbnRAdmFncmFudAECAwQFBg==
//$dom = new DOMDocument();
//$dom->preserveWhiteSpace = false;
$dom->loadXML($key->getPublicKey('XML'));
$dom->loadXML($key->toString('XML'));
$actual = $dom->C14N();
$this->assertSame($expected, $actual);
}
public function testToPublicKey()
{
$key = new ECDSA;
$key->load('-----BEGIN PRIVATE KEY-----
MFICAQEwBwYDK2VwBQAEIgQgS5tTLrcNRaml4g5CgGeMvptuXuSrcrFbl+zVSxHD
H76BIDXmiVv2hLjr5MhZENlKIuz0ak1hUO8MdZ2vgY/nGcUV
-----END PRIVATE KEY-----');
$this->assertInternalType('string', (string) $key->getPublicKey());
}
public static function assertSame($expected, $actual, $message = '')
{
$expected = str_replace("\r\n", "\n", $expected);

View File

@ -8,14 +8,17 @@
use phpseclib\Crypt\RSA;
use phpseclib\Crypt\RSA\Keys\PKCS1;
use phpseclib\Crypt\RSA\PrivateKey;
use phpseclib\Crypt\RSA\PublicKey;
class Unit_Crypt_RSA_CreateKeyTest extends PhpseclibTestCase
{
public function testCreateKey()
{
extract(RSA::createKey(768));
$this->assertInstanceOf('\phpseclib\Crypt\RSA', $privatekey);
$this->assertInstanceOf('\phpseclib\Crypt\RSA', $publickey);
$privatekey = RSA::createKey(768);
$publickey = $privatekey->getPublicKey();
$this->assertInstanceOf(PrivateKey::class, $privatekey);
$this->assertInstanceOf(PublicKey::class, $publickey);
$this->assertNotEmpty("$privatekey");
$this->assertNotEmpty("$publickey");
$this->assertSame($privatekey->getLength(), 768);
@ -40,15 +43,15 @@ class Unit_Crypt_RSA_CreateKeyTest extends PhpseclibTestCase
{
RSA::useInternalEngine();
RSA::setSmallestPrime(256);
extract(RSA::createKey(1024));
$this->assertInstanceOf('\phpseclib\Crypt\RSA', $privatekey);
$this->assertInstanceOf('\phpseclib\Crypt\RSA', $publickey);
$privatekey->setPrivateKeyFormat('PKCS1');
$this->assertNotEmpty("$privatekey");
$this->assertNotEmpty("$publickey");
$privatekey = RSA::createKey(1024);
$publickey = $privatekey->getPublicKey();
$this->assertInstanceOf(PrivateKey::class, $privatekey);
$this->assertInstanceOf(PublicKey::class, $publickey);
$this->assertNotEmpty($privatekey->toString('PKCS1'));
$this->assertNotEmpty($publickey->toString('PKCS1'));
$this->assertSame($privatekey->getLength(), 1024);
$this->assertSame($publickey->getLength(), 1024);
$r = PKCS1::load("$privatekey");
$r = PKCS1::load($privatekey->toString('PKCS1'));
$this->assertCount(4, $r['primes']);
// the last prime number could be slightly over. eg. 99 * 99 == 9801 but 10 * 10 = 100. the more numbers you're
// multiplying the less certain you are to have each of them multiply to an n-bit number
@ -56,10 +59,9 @@ class Unit_Crypt_RSA_CreateKeyTest extends PhpseclibTestCase
$this->assertSame($prime->getLength(), 256);
}
$rsa = new RSA();
$rsa->load($privatekey->getPrivateKey());
$rsa = RSA::load($privatekey->toString('PKCS1'));
$signature = $rsa->sign('zzz');
$rsa->load($rsa->getPublicKey());
$rsa = RSA::load($rsa->getPublicKey()->toString('PKCS1'));
$this->assertTrue($rsa->verify('zzz', $signature));
RSA::useBestEngine();

View File

@ -6,6 +6,9 @@
*/
use phpseclib\Crypt\RSA;
use phpseclib\Crypt\PublicKeyLoader;
use phpseclib\Crypt\RSA\PrivateKey;
use phpseclib\Crypt\RSA\PublicKey;
use phpseclib\Crypt\RSA\Keys\PKCS1;
use phpseclib\Crypt\RSA\Keys\PKCS8;
use phpseclib\Crypt\RSA\Keys\PuTTY;
@ -20,19 +23,17 @@ class Unit_Crypt_RSA_LoadKeyTest extends PhpseclibTestCase
OpenSSH::setComment('phpseclib-generated-key');
}
/**
* @expectedException \phpseclib\Exception\NoKeyLoadedException
*/
public function testBadKey()
{
$rsa = new RSA();
$key = 'zzzzzzzzzzzzzz';
$this->assertFalse($rsa->load($key));
PublicKeyLoader::load($key);
}
public function testPKCS1Key()
{
$rsa = new RSA();
$key = '-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp
wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5
@ -47,14 +48,14 @@ U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ
37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0=
-----END RSA PRIVATE KEY-----';
$this->assertTrue($rsa->load($key));
$this->assertInternalType('string', $rsa->getPrivateKey());
$rsa = PublicKeyLoader::load($key);
$this->assertInstanceOf(PrivateKey::class, $rsa);
$this->assertInternalType('string', "$rsa");
}
public function testPKCS1SpacesKey()
{
$rsa = new RSA();
$key = '-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp
wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5
@ -70,14 +71,14 @@ U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ
-----END RSA PRIVATE KEY-----';
$key = str_replace(["\r", "\n", "\r\n"], ' ', $key);
$this->assertTrue($rsa->load($key));
$this->assertInternalType('string', $rsa->getPrivateKey());
$rsa = PublicKeyLoader::load($key);
$this->assertInstanceOf(PrivateKey::class, $rsa);
$this->assertInternalType('string', "$rsa");
}
public function testPKCS1NoHeaderKey()
{
$rsa = new RSA();
$key = 'MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp
wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5
1s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQABAoGAFijko56+qGyN8M0RVyaRAXz++xTqHBLh
@ -90,14 +91,14 @@ X6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2epl
U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ
37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0=';
$this->assertTrue($rsa->load($key));
$this->assertInternalType('string', $rsa->getPrivateKey());
$rsa = PublicKeyLoader::load($key);
$this->assertInstanceOf(PrivateKey::class, $rsa);
$this->assertInternalType('string', "$rsa");
}
public function testPKCS1NoWhitespaceNoHeaderKey()
{
$rsa = new RSA();
$key = 'MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp' .
'wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5' .
'1s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQABAoGAFijko56+qGyN8M0RVyaRAXz++xTqHBLh' .
@ -110,14 +111,14 @@ U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ
'U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ' .
'37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0=';
$this->assertTrue($rsa->load($key));
$this->assertInternalType('string', $rsa->getPrivateKey());
$rsa = PublicKeyLoader::load($key);
$this->assertInstanceOf(PrivateKey::class, $rsa);
$this->assertInternalType('string', "$rsa");
}
public function testRawPKCS1Key()
{
$rsa = new RSA();
$key = 'MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp' .
'wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5' .
'1s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQABAoGAFijko56+qGyN8M0RVyaRAXz++xTqHBLh' .
@ -131,15 +132,14 @@ U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ
'37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0=';
$key = base64_decode($key);
$this->assertTrue($rsa->load($key));
$this->assertInternalType('string', $rsa->getPrivateKey());
$rsa = PublicKeyLoader::load($key);
$this->assertInstanceOf(PrivateKey::class, $rsa);
$this->assertInternalType('string', "$rsa");
}
public function testLoadPKCS8PrivateKey()
{
$rsa = new RSA();
$rsa->setPassword('password');
$key = '-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIE6TAbBgkqhkiG9w0BBQMwDgQIcWWgZeQYPTcCAggABIIEyLoa5b3ktcPmy4VB
hHkpHzVSEsKJPmQTUaQvUwIp6+hYZeuOk78EPehrYJ/QezwJRdyBoD51oOxqWCE2
@ -170,14 +170,14 @@ GF/qoZyC1mbqdtyyeWgHtVbJVUORmpbNnXOII9duEqBUNDiO9VSZNn/8h/VsYeAB
xryZaRDVmtMuf/OZBQ==
-----END ENCRYPTED PRIVATE KEY-----';
$this->assertTrue($rsa->load($key));
$this->assertInternalType('string', $rsa->getPrivateKey());
$rsa = PublicKeyLoader::load($key, 'password');
$this->assertInstanceOf(PrivateKey::class, $rsa);
$this->assertInternalType('string', "$rsa");
}
public function testSavePKCS8PrivateKey()
{
$rsa = new RSA();
$key = '-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp
wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5
@ -191,20 +191,19 @@ X6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2epl
U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ
37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0=
-----END RSA PRIVATE KEY-----';
$rsa->setPassword('password');
$this->assertTrue($rsa->load($key));
$rsa = PublicKeyLoader::load($key, 'password');
$key = $rsa->getPrivateKey('PKCS8');
$this->assertInternalType('string', $key);
$this->assertInstanceOf(PrivateKey::class, $rsa);
$this->assertTrue($rsa->load($key));
$key = (string) $rsa->withPassword('password');
$rsa = PublicKeyLoader::load($key, 'password');
$this->assertInstanceOf(PrivateKey::class, $rsa);
}
public function testPubKey1()
{
$rsa = new RSA();
$key = '-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEA61BjmfXGEvWmegnBGSuS+rU9soUg2FnODva32D1AqhwdziwHINFa
D1MVlcrYG6XRKfkcxnaXGfFDWHLEvNBSEVCgJjtHAGZIm5GL/KA86KDp/CwDFMSw
@ -214,15 +213,12 @@ gPiUWOPatVkt7+Bs3h5Ramxh7XjBOXeulmCpGSynXNcpZ/06+vofGi/2MlpQZNhH
Ao8eayMp6FcvNucIpUndo1X8dKMv3Y26ZQIDAQAB
-----END RSA PUBLIC KEY-----';
$this->assertTrue($rsa->load($key));
$this->assertInstanceOf(RSA::class, $rsa->getPublicKey());
$this->assertFalse($rsa->getPrivateKey());
$rsa = PublicKeyLoader::load($key);
$this->assertInstanceOf(PublicKey::class, $rsa);
}
public function testPubKey2()
{
$rsa = new RSA();
$key = '-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA61BjmfXGEvWmegnBGSuS
+rU9soUg2FnODva32D1AqhwdziwHINFaD1MVlcrYG6XRKfkcxnaXGfFDWHLEvNBS
@ -233,29 +229,23 @@ lmCpGSynXNcpZ/06+vofGi/2MlpQZNhHAo8eayMp6FcvNucIpUndo1X8dKMv3Y26
ZQIDAQAB
-----END PUBLIC KEY-----';
$this->assertTrue($rsa->load($key));
$this->assertInstanceOf(RSA::class, $rsa->getPublicKey());
$this->assertFalse($rsa->getPrivateKey());
$rsa = PublicKeyLoader::load($key);
$this->assertInstanceOf(PublicKey::class, $rsa);
}
public function testSSHPubKey()
{
$rsa = new RSA();
$key = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4e' .
'CZ0FPqri0cb2JZfXJ/DgYSF6vUpwmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMS' .
'GkVb1/3j+skZ6UtW+5u09lHNsj6tQ51s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZw== ' .
'phpseclib-generated-key';
$this->assertTrue($rsa->load($key));
$this->assertInstanceOf(RSA::class, $rsa->getPublicKey());
$this->assertFalse($rsa->getPrivateKey());
$rsa = PublicKeyLoader::load($key);
$this->assertInstanceOf(PublicKey::class, $rsa);
}
public function testSSHPubKeyFingerprint()
{
$rsa = new RSA();
$key = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD9K+ebJRMN10kGanhi6kDz6EYFqZttZWZh0'.
'YoEbIbbere9N2Yvfc7oIoCTHYowhXND9WSJaIs1E4bx0085CZnofWaqf4NbZTzAh18iZup08ec'.
'COB5gJVS1efpgVSviDF2L7jxMsBVoOBfqsmA8m0RwDDVezyWvw4y+STSuVzu2jI8EfwN7ZFGC6'.
@ -263,15 +253,14 @@ ZQIDAQAB
'b6wYtY/q/WtUFr3nK+x0lgOtokhnJfRR/6fnmC1CztPnIT4BWK81VGKWONAxuhMyQ5XChyu6S9'.
'mWG5tUlUI/5';
$this->assertTrue($rsa->load($key));
$this->assertSame($rsa->getPublicKeyFingerprint('md5'), 'bd:2c:2f:31:b9:ef:b8:f8:ad:fc:40:a6:94:4f:28:82');
$this->assertSame($rsa->getPublicKeyFingerprint('sha256'), 'N9sV2uSNZEe8TITODku0pRI27l+Zk0IY0TrRTw3ozwM');
$rsa = PublicKeyLoader::load($key, 'password');
$this->assertInstanceOf(PublicKey::class, $rsa);
$this->assertSame($rsa->getFingerprint('md5'), 'bd:2c:2f:31:b9:ef:b8:f8:ad:fc:40:a6:94:4f:28:82');
$this->assertSame($rsa->getFingerprint('sha256'), 'N9sV2uSNZEe8TITODku0pRI27l+Zk0IY0TrRTw3ozwM');
}
public function testSetPrivate()
{
$rsa = new RSA();
$key = '-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEA61BjmfXGEvWmegnBGSuS+rU9soUg2FnODva32D1AqhwdziwHINFa
D1MVlcrYG6XRKfkcxnaXGfFDWHLEvNBSEVCgJjtHAGZIm5GL/KA86KDp/CwDFMSw
@ -281,29 +270,28 @@ gPiUWOPatVkt7+Bs3h5Ramxh7XjBOXeulmCpGSynXNcpZ/06+vofGi/2MlpQZNhH
Ao8eayMp6FcvNucIpUndo1X8dKMv3Y26ZQIDAQAB
-----END RSA PUBLIC KEY-----';
$this->assertTrue($rsa->load($key));
$this->assertTrue($rsa->setPrivateKey());
$rsa = PublicKeyLoader::load($key);
$this->assertInstanceOf(PublicKey::class, $rsa);
$rsa = $rsa->asPrivateKey();
$this->assertInstanceOf(PrivateKey::class, $rsa);
$this->assertGreaterThanOrEqual(1, strlen("$rsa"));
$this->assertFalse($rsa->getPublicKey());
}
/**
* make phpseclib generated XML keys be unsigned. this may need to be reverted
* if it is later learned that XML keys are, in fact, supposed to be signed
*
* @group github468
*/
public function testUnsignedXML()
{
$rsa = new RSA();
$key = '<RSAKeyValue>
<Modulus>v5OxcEgxPUfa701NpxnScCmlRkbwSGBiTWobHkIWZEB+AlRTHaVoZg/D8l6YzR7VdQidG6gF+nuUMjY75dBXgY/XcyVq0Hccf1jTfgARuNuq4GGG3hnCJVi2QsOgcf9R7TeXn+p1RKIhjQoWCiEQeEBTotNbJhcabNcPGSEJw+s=</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>';
$rsa->load($key);
$rsa->setPublicKey();
$newkey = $rsa->getPublicKey('XML');
$rsa = PublicKeyLoader::load($key);
$newkey = $rsa->toString('XML');
$this->assertSame(strtolower(preg_replace('#\s#', '', $key)), strtolower(preg_replace('#\s#', '', $newkey)));
}
@ -313,8 +301,6 @@ Ao8eayMp6FcvNucIpUndo1X8dKMv3Y26ZQIDAQAB
*/
public function testSignedPKCS1()
{
$rsa = new RSA();
$key = '-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/k7FwSDE9R9rvTU2nGdJwKaVG
RvBIYGJNahseQhZkQH4CVFMdpWhmD8PyXpjNHtV1CJ0bqAX6e5QyNjvl0FeBj9dz
@ -322,9 +308,8 @@ JWrQdxx/WNN+ABG426rgYYbeGcIlWLZCw6Bx/1HtN5ef6nVEoiGNChYKIRB4QFOi
01smFxps1w8ZIQnD6wIDAQAB
-----END PUBLIC KEY-----';
$rsa->load($key);
$rsa->setPublicKey();
$newkey = $rsa->getPublicKey();
$rsa = PublicKeyLoader::load($key);
$newkey = "$rsa";
$this->assertSame(preg_replace('#\s#', '', $key), preg_replace('#\s#', '', $newkey));
}
@ -334,8 +319,6 @@ JWrQdxx/WNN+ABG426rgYYbeGcIlWLZCw6Bx/1HtN5ef6nVEoiGNChYKIRB4QFOi
*/
public function testPKCS8Only()
{
$rsa = new RSA();
$key = '-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAKB0yPMAbUHKqJxP
5sjG9AOrQSAYNDc34NsnZ1tsi7fZ9lHlBaKZ6gjm2U9q+/qCKv2BuGINxWo2CMJp
@ -353,15 +336,13 @@ qMnD/pkHR/NFcYSYShUJS0cHyryVl7/eCclsQlZTRdnVTtKF9xPGTQC8fK0G7BDN
Z2sKniRCcDT1ZP4=
-----END PRIVATE KEY-----';
$result = $rsa->load($key, 'PKCS8');
$rsa = RSA::load($key, false, 'PKCS8');
$this->assertTrue($result);
$this->assertInstanceOf(PrivateKey::class, $rsa);
}
public function testPKCS1EncryptionChange()
{
$rsa = new RSA();
$key = 'PuTTY-User-Key-File-2: ssh-rsa
Encryption: none
Comment: phpseclib-generated-key
@ -382,44 +363,23 @@ Gpb88h5NBYZzWXGZ37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ
Private-MAC: 03e2cb74e1d67652fbad063d2ed0478f31bdf256
';
$key = preg_replace('#(?<!\r)\n#', "\r\n", $key);
$this->assertTrue($rsa->load($key));
$rsa->setPrivateKeyFormat('PKCS1');
$rsa = PublicKeyLoader::load($key);
$this->assertInstanceOf(PrivateKey::class, $rsa);
PKCS1::setEncryptionAlgorithm('AES-256-CBC');
$rsa->setPassword('demo');
$encryptedKey = (string) $rsa;
$encryptedKey = $rsa->withPassword('demo')->toString('PKCS1');
$this->assertRegExp('#AES-256-CBC#', $encryptedKey);
$rsa = new RSA();
$rsa->setPassword('demo');
$this->assertTrue($rsa->load($encryptedKey));
$rsa->setPassword();
$rsa->setPrivateKeyFormat('PuTTY');
$key2 = (string) $rsa;
$rsa = PublicKeyLoader::load($key, 'demo');
$this->assertInstanceOf(PrivateKey::class, $rsa);
OpenSSH::setComment('ecdsa-key-20181105');
$key2 = $rsa->withPassword()->toString('PuTTY');
$this->assertSame($key, $key2);
}
public function testRawKey()
{
$rsa = new RSA();
$key = [
'e' => new BigInteger('10001', 16),
'n' => new BigInteger('aa18aba43b50deef38598faf87d2ab634e4571c130a9bca7b878267414faab8b471bd8965f5c9fc3' .
'818485eaf529c26246f3055064a8de19c8c338be5496cbaeb059dc0b358143b44a35449eb2641131' .
'21a455bd7fde3fac919e94b56fb9bb4f651cdb23ead439d6cd523eb08191e75b35fd13a7419b3090' .
'f24787bd4f4e1967', 16)
];
$this->assertTrue($rsa->load($key));
$rsa->setPublicKeyFormat('raw');
$this->assertEmpty("$rsa");
}
public function testRawComment()
{
$key = 'PuTTY-User-Key-File-2: ssh-rsa
@ -440,13 +400,10 @@ fM8VzC3ukvzzRh0pujUVTr/yQdmciASVFnZlt4xQy+ZEOVUAOfwjd//AFfXTvk6x
EOpSeghXSs7IilJu8I6/sB1w5dakdeBSFkIynrlFXkO0uUw+QJJWjxY8SypzgIuP
DzduF6XsQrCyo6dnIpGQCQ==
Private-MAC: 35134b7434bf828b21404099861d455e660e8740';
$raw = PuTTY::load($key, 'password');
$this->assertArrayHasKey('comment', $raw);
$this->assertEquals($raw['comment'], 'phpseclib-generated-key');
$rsa = new RSA();
$rsa->load($raw);
$this->assertGreaterThanOrEqual(1, strlen("$rsa"));
}
public function testPrivateMSBlob()
@ -465,16 +422,13 @@ Private-MAC: 35134b7434bf828b21404099861d455e660e8740';
$plaintext = 'zzz';
$privKey = new RSA();
$privKey->load($key);
$privKey = PublicKeyLoader::load($key);
$this->assertInstanceOf(PrivateKey::class, $privKey);
$this->assertSame($privKey->getLoadedFormat(), 'MSBLOB');
$this->assertGreaterThanOrEqual(1, strlen("$privKey"));
$pubKey = new RSA();
$pubKey->load($privKey->getPublicKey('msblob'));
$pubKey = PublicKeyLoader::load($privKey->getPublicKey()->toString('msblob'));
$this->assertInstanceOf(PublicKey::class, $pubKey);
$this->assertGreaterThanOrEqual(1, strlen("$pubKey"));
$ciphertext = $pubKey->encrypt($plaintext);
@ -486,9 +440,8 @@ Private-MAC: 35134b7434bf828b21404099861d455e660e8740';
{
$key = 'AAAAB3NzaC1yc2EAAAABIwAAAIEA/NcGSQFZ0ZgN1EbDusV6LLwLnQjs05ljKcVVP7Z6aKIJUyhUDHE30uJa5XfwPPBsZ3L3Q7S0yycVcuuHjdauugmpn9xx+gyoYs7UiV5G5rvxNcA/Tc+MofGhAMiTmNicorNAs5mv6fRoVbkpIONRXPz6WK0kjx/X04EV42Vm9Qk=';
$rsa = new RSA();
$rsa->load($key);
$rsa = PublicKeyLoader::load($key);
$this->assertInstanceOf(PublicKey::class, $rsa);
$this->assertSame($rsa->getLoadedFormat(), 'OpenSSH');
$this->assertGreaterThanOrEqual(1, strlen("$rsa"));
@ -504,48 +457,13 @@ C/EwUYl8b0fAwEsEF3myb+ryzgA9ihY08Zs9NZdmt1Maa+I7lQcLX9F/65YdcAch
ILaEujU=
---- END SSH2 PUBLIC KEY ----';
$rsa = new RSA();
$rsa->load($key);
$rsa = PublicKeyLoader::load($key);
$this->assertInstanceOf(PublicKey::class, $rsa);
$this->assertSame($rsa->getLoadedFormat(), 'PuTTY');
$this->assertGreaterThanOrEqual(1, strlen("$rsa"));
}
/**
* @group github960
*/
public function testSetLoad()
{
$key = 'PuTTY-User-Key-File-2: ssh-rsa
Encryption: aes256-cbc
Comment: phpseclib-generated-key
Public-Lines: 4
AAAAB3NzaC1yc2EAAAADAQABAAAAgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4
eCZ0FPqri0cb2JZfXJ/DgYSF6vUpwmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RK
NUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ51s1SPrCBkedbNf0Tp0GbMJDy
R4e9T04ZZw==
Private-Lines: 8
llx04QMegql0/nE5RvcJSrGrodxt6ytuv/JX2caeZBUyQwQc2WBNYagLHyHPM9jI
9OUWz59FLhjFXZMDNMoUXxVmjwQpOAaVPYNxxFM9AF6/NXFji64K7huD9n4A+kLn
sHwMLWPR5a/tZA0r05DZNz9ULA3mQu7Hz4EQ8ifu3uTPJuTmL51x6RmudYKysb20
fM8VzC3ukvzzRh0pujUVTr/yQdmciASVFnZlt4xQy+ZEOVUAOfwjd//AFfXTvk6x
7A45rNlU/uicHwLgoY1APvRHCFxw7F+uVW5L4mSX7NNzqBKkZ+1qpQTAfQvIfEIb
444+CXsgIyOpqt6VxJH2u6elAtE1wau3YaFR8Alm8m97rFYzRi3oDP5NZYkTCWSV
EOpSeghXSs7IilJu8I6/sB1w5dakdeBSFkIynrlFXkO0uUw+QJJWjxY8SypzgIuP
DzduF6XsQrCyo6dnIpGQCQ==
Private-MAC: 35134b7434bf828b21404099861d455e660e8740';
$rsa = new RSA();
$rsa->setPrivateKey($key);
$rsa->load($key);
$rsa = new RSA();
$rsa->load($key);
$rsa->setPrivateKey();
$rsa->load($rsa);
}
/**
* @group github980
*/
@ -558,19 +476,17 @@ NNj0BDlf38hOtkhDzz/hkYb+EBYLLvldhgsD0OvRNy8yhz7EjaUqLCB0juIN4QIB
AAIBAAIBAAIBAAIBAA==
-----END RSA PRIVATE KEY-----';
$rsa = new RSA();
$rsa->load($key);
$rsa->setHash('md5');
$rsa->setMGFHash('md5');
$rsa = PublicKeyLoader::load($key)
->withHash('md5')
->withMGFHash('md5')
->withPadding(RSA::SIGNATURE_PKCS1);
$rsa->sign('zzzz', RSA::PADDING_PKCS1);
$rsa->sign('zzzz');
}
public function pkcs8tester($key, $pass)
{
$rsa = new RSA();
$rsa->setPassword($pass);
$rsa->load($key);
$rsa = PublicKeyLoader::load($key, $pass);
$r = PKCS8::load($key, $pass);
PKCS8::setEncryptionAlgorithm($r['meta']['algorithm']);
if (isset($r['meta']['cipher'])) {
@ -590,15 +506,13 @@ AAIBAAIBAAIBAAIBAA==
$this->assertSame($r['meta']['prf'], $r2['meta']['prf']);
}
$rsa2 = new RSA();
$rsa2->setPassword($pass);
$rsa2->load($newkey);
$rsa2 = PublicKeyLoader::load($newkey, $pass);
// comparing $key to $newkey won't work since phpseclib randomly generates IV's and salt's
// so we'll strip the encryption
$rsa->setPassword();
$rsa2->setPassword();
$rsa = $rsa->withPassword();
$rsa2 = $rsa2->withPassword();
$this->assertSame("$rsa", "$rsa2");
}
@ -930,29 +844,8 @@ OFLPBrLe4Hw=
$this->pkcs8tester($key, $pass);
}
public function testGoodBad()
{
$rsa = new RSA();
$key = '-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEA61BjmfXGEvWmegnBGSuS+rU9soUg2FnODva32D1AqhwdziwHINFa
D1MVlcrYG6XRKfkcxnaXGfFDWHLEvNBSEVCgJjtHAGZIm5GL/KA86KDp/CwDFMSw
luowcXwDwoyinmeOY9eKyh6aY72xJh7noLBBq1N0bWi1e2i+83txOCg4yV2oVXhB
o8pYEJ8LT3el6Smxol3C1oFMVdwPgc0vTl25XucMcG/ALE/KNY6pqC2AQ6R2ERlV
gPiUWOPatVkt7+Bs3h5Ramxh7XjBOXeulmCpGSynXNcpZ/06+vofGi/2MlpQZNhH
Ao8eayMp6FcvNucIpUndo1X8dKMv3Y26ZQIDAQAB
-----END RSA PUBLIC KEY-----';
$this->assertTrue($rsa->load($key));
$this->assertInstanceOf(RSA::class, $rsa->getPublicKey());
$this->assertFalse($rsa->load('zzz'));
$this->assertFalse($rsa->getPublicKey());
}
public function testXMLDeclaration()
{
$rsa = new RSA();
$key = '<?xml version="1.0" encoding="utf-8"?>
<RSAKeyValue>
<Modulus>AKoYq6Q7UN7vOFmPr4fSq2NORXHBMKm8p7h4JnQU+quLRxvYll9cn8OBhIXq9SnCYkbzBVBkqN4ZyMM4vlSWy66wWdwLNYFDtEo1RJ6yZBExIaRVvX/eP6yRnpS1b7m7T2Uc2yPq1DnWzVI+sIGR51s1/ROnQZswkPJHh71PThln</Modulus>
@ -965,7 +858,8 @@ Ao8eayMp6FcvNucIpUndo1X8dKMv3Y26ZQIDAQAB
<D>Fijko56+qGyN8M0RVyaRAXz++xTqHBLh3tx4VgMtrQ+WEgCjhoTwo23KMBAuJGSYnRmoBZM3lMfTKevIkAidPExvYCdm5dYq3XToLkkLv5L2pIIVOFMDG+KESnAFV7l2c+cnzRMW0+b6f8mR1CJzZuxVLL6Q02fvLi55/mbSYxE=</D>
</RSAKeyValue>';
$this->assertTrue($rsa->load($key));
$this->assertInstanceOf(RSA::class, $rsa->getPublicKey());
$rsa = PublicKeyLoader::load($key);
$this->assertInstanceOf(PrivateKey::class, $rsa);
$this->assertInstanceOf(PublicKey::class, $rsa->getPublicKey());
}
}

View File

@ -7,6 +7,8 @@
use phpseclib\Crypt\RSA;
use phpseclib\Math\BigInteger;
use phpseclib\Crypt\PublicKeyLoader;
use phpseclib\Crypt\RSA\Keys\PKCS8;
class Unit_Crypt_RSA_ModeTest extends PhpseclibTestCase
{
@ -14,8 +16,6 @@ class Unit_Crypt_RSA_ModeTest extends PhpseclibTestCase
{
$plaintext = 'a';
$rsa = new RSA();
$privatekey = '-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp
wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5
@ -29,19 +29,21 @@ X6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2epl
U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ
37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0=
-----END RSA PRIVATE KEY-----';
$rsa->load($privatekey);
$rsa->load($rsa->getPublicKey());
$rsa = PublicKeyLoader::load($privatekey);
$rsa = $rsa->getPublicKey()
->withPadding(RSA::ENCRYPTION_NONE);
$expected = '105b92f59a87a8ad4da52c128b8c99491790ef5a54770119e0819060032fb9e772ed6772828329567f3d7e9472154c1530f8156ba7fd732f52ca1c06' .
'5a3f5ed8a96c442e4662e0464c97f133aed31262170201993085a589565d67cc9e727e0d087e3b225c8965203b271e38a499c92fc0d6502297eca712' .
'4d04bd467f6f1e7c';
$expected = pack('H*', $expected);
$result = $rsa->encrypt($plaintext, RSA::PADDING_NONE);
$result = $rsa->encrypt($plaintext);
$this->assertEquals($result, $expected);
$rsa->load($privatekey);
$this->assertEquals(trim($rsa->decrypt($result, RSA::PADDING_NONE), "\0"), $plaintext);
$rsa = PublicKeyLoader::load($privatekey)
->withPadding(RSA::ENCRYPTION_NONE);
$this->assertEquals(trim($rsa->decrypt($result), "\0"), $plaintext);
}
/**
@ -49,15 +51,14 @@ U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ
*/
public function testPSSSigs()
{
$rsa = new RSA();
$rsa->setHash('sha1');
$rsa->setMGFHash('sha1');
$rsa->load('-----BEGIN PUBLIC KEY-----
$rsa = PublicKeyLoader::load('-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqGKukO1De7zhZj6+H0qtjTkVx
wTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUpwmJG8wVQZKjeGcjDOL5UlsuusFnc
CzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ51s1SPrCBkedbNf0T
p0GbMJDyR4e9T04ZZwIDAQAB
-----END PUBLIC KEY-----');
-----END PUBLIC KEY-----')
->withHash('sha1')
->withMGFHash('sha1');
$sig = pack('H*', '1bd29a1d704a906cd7f726370ce1c63d8fb7b9a620871a05f3141a311c0d6e75fefb5d36dfb50d3ea2d37cd67992471419bfadd35da6e13b494' .
'058ddc9b568d4cfea13ddc3c62b86a6256f5f296980d1131d3eaec6089069a3de79983f73eae20198a18721338b4a66e9cfe80e4f8e4fcef7a5bead5cbb' .
@ -72,22 +73,24 @@ p0GbMJDyR4e9T04ZZwIDAQAB
public function testSmallModulo()
{
$plaintext = 'x';
$n = new BigInteger(base64_decode('272435F22706FA96DE26E980D22DFF67'), 256);
$e = new BigInteger(base64_decode('158753FF2AF4D1E5BBAB574D5AE6B54D'), 256);
$rsa = new RSA();
$rsa->load(['n' => $n, 'e' => $e]);
$key = PKCS8::savePublicKey(
new BigInteger(base64_decode('272435F22706FA96DE26E980D22DFF67'), 256), // n
new BigInteger(base64_decode('158753FF2AF4D1E5BBAB574D5AE6B54D'), 256) // e
);
$rsa = PublicKeyLoader::load($key);
$rsa->encrypt($plaintext);
}
public function testPKCS1LooseVerify()
{
$rsa = new RSA();
$rsa->load('-----BEGIN RSA PUBLIC KEY-----
$rsa = PublicKeyLoader::load('-----BEGIN RSA PUBLIC KEY-----
MIGJAoGBAMuqkz8ij+ESAaNvgocVGmapjlrIldmhRo4h2NX4e6IXiCLTSxASQtY4
iqRnmyxqQSfaan2okTfQ6sP95bl8Qz8lgneW3ClC6RXG/wpJgsx7TXQ2kodlcKBF
m4k72G75QXhZ+I40ZG7cjBf1/9egakR0a0X0MpeOrKCzMBLv9+mpAgMBAAE=
-----END RSA PUBLIC KEY-----');
-----END RSA PUBLIC KEY-----')
->withPadding(RSA::SIGNATURE_RELAXED_PKCS1);
$message = base64_decode('MYIBLjAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xNDA1MTUxNDM4MzRaMC8GCSqGSIb3DQEJBDEiBCBLzLIBGdOf0L2WRrIY' .
'9KTwiHnReBW48S9C7LNRaPp5mDCBwgYLKoZIhvcNAQkQAi8xgbIwga8wgawwgakEIJDB9ZGwihf+TaiwrHQNkNHkqbN8Nuws0e77QNObkvFZMIGEMHCkbjBs' .
@ -97,16 +100,14 @@ m4k72G75QXhZ+I40ZG7cjBf1/9egakR0a0X0MpeOrKCzMBLv9+mpAgMBAAE=
$sig = base64_decode('XDSZWw6IcUj8ICxRJf04HzF8stzoiFAZSR2a0Rw3ziZxTOT0/NVUYJO5+9TaaREXEgxuCLpgmA+6W2SWrrGoxbbNfaI90ZoKeOAws4IX+9RfiWuooibjKcvt' .
'GJYVVOCcjvQYxUUNbQ4EjCUonk3h7ECXfCCmWqbeq2LsyXeeYGE=');
$this->assertTrue($rsa->verify($message, $sig, RSA::PADDING_RELAXED_PKCS1));
$this->assertTrue($rsa->verify($message, $sig));
}
public function testZeroLengthSalt()
{
$plaintext = 'a';
$rsa = new RSA();
$privatekey = '-----BEGIN RSA PRIVATE KEY-----
$rsa = PublicKeyLoader::load('-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp
wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5
1s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQABAoGAFijko56+qGyN8M0RVyaRAXz++xTqHBLh
@ -118,96 +119,17 @@ L0NDt4SkosjgGwJAFklyR1uZ/wPJjj611cdBcztlPdqoxssQGnh85BzCj/u3WqBpE2vjvyyvyI5k
X6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2epl
U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ
37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0=
-----END RSA PRIVATE KEY-----';
$rsa->load($privatekey);
$rsa->setSaltLength(0);
$rsa->setHash('sha1');
$rsa->setMGFHash('sha1');
-----END RSA PRIVATE KEY-----')
->withSaltLength(0)
->withHash('sha1')
->withMGFHash('sha1');
// Check we generate the correct signature.
$sig = pack('H*', '0ddfc93548e21d015c0a289a640b3b79aecfdfae045f583c5925b91cc5c399bba181616ad6ae20d9662d966f0eb2fddb550f4733268e34d640f4c9dadcaf25b3c82c42130a5081c6ebad7883331c65b25b6a37ffa7c4233a468dae56180787e2718ed87c48d8d50b72f5850e4a40963b4f36710be250ecef6fe0bb91249261a3');
$this->assertEquals($sig, $rsa->sign($plaintext));
// Check we can verify the signature correctly.
$rsa->load($rsa->getPublicKey());
$rsa = $rsa->getPublicKey();
$this->assertTrue($rsa->verify($plaintext, $sig));
}
/**
* @expectedException \phpseclib\Exception\UnsupportedOperationException
*/
public function testPrivateEncrypt()
{
$rsa = new RSA();
$privatekey = '-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp
wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5
1s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQABAoGAFijko56+qGyN8M0RVyaRAXz++xTqHBLh
3tx4VgMtrQ+WEgCjhoTwo23KMBAuJGSYnRmoBZM3lMfTKevIkAidPExvYCdm5dYq3XToLkkLv5L2
pIIVOFMDG+KESnAFV7l2c+cnzRMW0+b6f8mR1CJzZuxVLL6Q02fvLi55/mbSYxECQQDeAw6fiIQX
GukBI4eMZZt4nscy2o12KyYner3VpoeE+Np2q+Z3pvAMd/aNzQ/W9WaI+NRfcxUJrmfPwIGm63il
AkEAxCL5HQb2bQr4ByorcMWm/hEP2MZzROV73yF41hPsRC9m66KrheO9HPTJuo3/9s5p+sqGxOlF
L0NDt4SkosjgGwJAFklyR1uZ/wPJjj611cdBcztlPdqoxssQGnh85BzCj/u3WqBpE2vjvyyvyI5k
X6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2epl
U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ
37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0=
-----END RSA PRIVATE KEY-----';
$rsa->load($privatekey);
$rsa->encrypt('hello, world!');
}
/**
* @expectedException \phpseclib\Exception\UnsupportedOperationException
*/
public function testPublicSign()
{
$rsa = new RSA();
$rsa->load('-----BEGIN RSA PUBLIC KEY-----
MIGJAoGBAMuqkz8ij+ESAaNvgocVGmapjlrIldmhRo4h2NX4e6IXiCLTSxASQtY4
iqRnmyxqQSfaan2okTfQ6sP95bl8Qz8lgneW3ClC6RXG/wpJgsx7TXQ2kodlcKBF
m4k72G75QXhZ+I40ZG7cjBf1/9egakR0a0X0MpeOrKCzMBLv9+mpAgMBAAE=
-----END RSA PUBLIC KEY-----');
$rsa->sign('hello, world!');
}
/**
* @expectedException \phpseclib\Exception\UnsupportedOperationException
*/
public function testPublicDecrypt()
{
$rsa = new RSA();
$rsa->load('-----BEGIN RSA PUBLIC KEY-----
MIGJAoGBAMuqkz8ij+ESAaNvgocVGmapjlrIldmhRo4h2NX4e6IXiCLTSxASQtY4
iqRnmyxqQSfaan2okTfQ6sP95bl8Qz8lgneW3ClC6RXG/wpJgsx7TXQ2kodlcKBF
m4k72G75QXhZ+I40ZG7cjBf1/9egakR0a0X0MpeOrKCzMBLv9+mpAgMBAAE=
-----END RSA PUBLIC KEY-----');
$rsa->decrypt('zzz');
}
/**
* @expectedException \phpseclib\Exception\UnsupportedOperationException
*/
public function testPrivateVerify()
{
$rsa = new RSA();
$privatekey = '-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp
wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5
1s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQABAoGAFijko56+qGyN8M0RVyaRAXz++xTqHBLh
3tx4VgMtrQ+WEgCjhoTwo23KMBAuJGSYnRmoBZM3lMfTKevIkAidPExvYCdm5dYq3XToLkkLv5L2
pIIVOFMDG+KESnAFV7l2c+cnzRMW0+b6f8mR1CJzZuxVLL6Q02fvLi55/mbSYxECQQDeAw6fiIQX
GukBI4eMZZt4nscy2o12KyYner3VpoeE+Np2q+Z3pvAMd/aNzQ/W9WaI+NRfcxUJrmfPwIGm63il
AkEAxCL5HQb2bQr4ByorcMWm/hEP2MZzROV73yF41hPsRC9m66KrheO9HPTJuo3/9s5p+sqGxOlF
L0NDt4SkosjgGwJAFklyR1uZ/wPJjj611cdBcztlPdqoxssQGnh85BzCj/u3WqBpE2vjvyyvyI5k
X6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2epl
U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ
37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0=
-----END RSA PRIVATE KEY-----';
$rsa->load($privatekey);
$rsa->verify('hello, world!', 'dummysignature');
}
}

View File

@ -7,6 +7,7 @@
use phpseclib\File\X509;
use phpseclib\Crypt\RSA;
use phpseclib\Crypt\PublicKeyLoader;
class Unit_File_X509_CSRTest extends PhpseclibTestCase
{
@ -98,10 +99,9 @@ draiRBZruwMPwPIP
// on PHP 7.1, with older versions of phpseclib, this would produce a "A non-numeric value encountered" warning
public function testNewCSR()
{
$rsa = new RSA();
$x509 = new X509();
$rsa->load('-----BEGIN RSA PRIVATE KEY-----
$rsa = PublicKeyLoader::load('-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp
wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5
1s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQABAoGAFijko56+qGyN8M0RVyaRAXz++xTqHBLh

View File

@ -46,7 +46,7 @@ class Unit_File_X509_SPKACTest extends PhpseclibTestCase
public function testSaveSPKAC()
{
extract(RSA::createKey());
$privatekey = RSA::createKey();
$x509 = new X509();
$x509->setPrivateKey($privatekey);

View File

@ -9,6 +9,7 @@ use phpseclib\File\ASN1;
use phpseclib\File\ASN1\Element;
use phpseclib\File\X509;
use phpseclib\Crypt\RSA;
use phpseclib\Crypt\PublicKeyLoader;
class Unit_File_X509_X509Test extends PhpseclibTestCase
{
@ -133,7 +134,7 @@ ulvKGQSy068Bsn5fFNum21K5mvMSf3yinDtvmX3qUA12IxL/92ZzKbeVCq3Yi7Le
IOkKcGQRCMha8X2e7GmlpdWC1ycenlbN0nbVeSv3JUMcafC4+Q==
-----END CERTIFICATE-----');
$value = $this->_encodeOID('1.2.3.4');
$value = ASN1::encodeOID('1.2.3.4');
$ext = chr(ASN1::TYPE_OBJECT_IDENTIFIER) . ASN1::encodeLength(strlen($value)) . $value;
$value = 'zzzzzzzzz';
$ext.= chr(ASN1::TYPE_OCTET_STRING) . ASN1::encodeLength(strlen($value)) . $value;
@ -151,8 +152,7 @@ IOkKcGQRCMha8X2e7GmlpdWC1ycenlbN0nbVeSv3JUMcafC4+Q==
*/
public function testSaveNullRSAParam()
{
$privKey = new RSA();
$privKey->load('-----BEGIN RSA PRIVATE KEY-----
$privKey = PublicKeyLoader::load('-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDMswfEpAgnUDWA74zZw5XcPsWh1ly1Vk99tsqwoFDkLF7jvXy1
dDLHYfuquvfxCgcp8k/4fQhx4ubR8bbGgEq9B05YRnViK0R0iBB5Ui4IaxWYYhKE
8xqAEH2fL+/7nsqqNFKkEN9KeFwc7WbMY49U2adlMrpBdRjk1DqIEW3QTwIDAQAB
@ -168,9 +168,7 @@ aBtsWpliLSex/HHhtRW9AkBGcq67zKmEpJ9kXcYLEjJii3flFS+Ct/rNm+Hhm1l7
4vca9v/F2hGVJuHIMJ8mguwYlNYzh2NqoIDJTtgOkBmt
-----END RSA PRIVATE KEY-----');
$pubKey = new RSA();
$pubKey->load($privKey->getPublicKey());
$pubKey->setPublicKey();
$pubKey = $privKey->getPublicKey();
$subject = new X509();
$subject->setDNProp('id-at-organizationName', 'phpseclib demo cert');
@ -192,37 +190,12 @@ aBtsWpliLSex/HHhtRW9AkBGcq67zKmEpJ9kXcYLEjJii3flFS+Ct/rNm+Hhm1l7
$this->assertArrayHasKey('parameters', $cert['tbsCertificate']['signature']);
}
private function _encodeOID($oid)
{
if ($oid === false) {
user_error('Invalid OID');
return false;
}
$value = '';
$parts = explode('.', $oid);
$value = chr(40 * $parts[0] + $parts[1]);
for ($i = 2; $i < count($parts); $i++) {
$temp = '';
if (!$parts[$i]) {
$temp = "\0";
} else {
while ($parts[$i]) {
$temp = chr(0x80 | ($parts[$i] & 0x7F)) . $temp;
$parts[$i] >>= 7;
}
$temp[strlen($temp) - 1] = $temp[strlen($temp) - 1] & chr(0x7F);
}
$value.= $temp;
}
return $value;
}
public function testGetOID()
{
// load the OIDs
new X509();
$this->assertEquals(ASN1::getOID('2.16.840.1.101.3.4.2.1'), '2.16.840.1.101.3.4.2.1');
$this->assertEquals(ASN1::getOID('id-sha256'), '2.16.840.1.101.3.4.2.1');
$this->assertEquals(ASN1::getOID('1.2.840.113549.1.1.5'), '1.2.840.113549.1.1.5');
$this->assertEquals(ASN1::getOID('sha1WithRSAEncryption'), '1.2.840.113549.1.1.5');
$this->assertEquals(ASN1::getOID('zzz'), 'zzz');
}
@ -383,7 +356,8 @@ Mj93S
// fixed by #1104
public function testMultipleDomainNames()
{
extract(RSA::createKey(512));
$privatekey = RSA::createKey(512);
$publickey = $privatekey->getPublicKey();
$subject = new X509();
$subject->setDomain('example.com', 'example.net');
@ -578,8 +552,7 @@ keSg3sfr4VWT545guJlTe+6vvelxbPFIXCXnyVLoePBYZtEe8FQhIBxd3EQHsxuJ
iSoMCxKCa8r5P1DrxKaJAkBBP87OdahRq0CBQjTFg0wmPs66PoTXA4hZvSxV77CO
tMPj6Pas7Muejogm6JkmxXC/uT6Tzfknd0B3XSmtDzGL
-----END RSA PRIVATE KEY-----';
$cakey = new RSA();
$cakey->load($pemcakey);
$cakey = PublicKeyLoader::load($pemcakey);
$pemca = '-----BEGIN CERTIFICATE-----
MIICADCCAWmgAwIBAgIUJXQulcz5xkTam8UGC/yn6iVaiWwwDQYJKoZIhvcNAQEF
BQAwHDEaMBgGA1UECgwRcGhwc2VjbGliIGRlbW8gQ0EwHhcNMTgwMTIxMTc0NzM0