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; namespace phpseclib\Crypt\Common;
use phpseclib\Exception\UnsupportedFormatException;
use phpseclib\Exception\NoKeyLoadedException;
use phpseclib\Math\BigInteger; use phpseclib\Math\BigInteger;
use phpseclib\Crypt\Hash; use phpseclib\Crypt\Hash;
use ParagonIE\ConstantTime\Base64; use phpseclib\Crypt\RSA;
use phpseclib\Exception\UnsupportedOperationException; use phpseclib\Crypt\DSA;
use phpseclib\Exception\FileNotFoundException; use phpseclib\Crypt\ECDSA;
/** /**
* Base Class for all stream cipher classes * Base Class for all stream cipher classes
@ -46,15 +48,36 @@ abstract class AsymmetricKey
protected static $one; protected static $one;
/** /**
* OpenSSL configuration file name. * Format of the loaded key
* *
* Set to null to use system configuration file. * @var string
* * @access private
* @see self::createKey()
* @var mixed
* @access public
*/ */
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) * Supported plugins (lower case)
@ -92,73 +115,6 @@ abstract class AsymmetricKey
*/ */
private static $signatureFileFormats = []; 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 * Available Engines
* *
@ -169,10 +125,8 @@ abstract class AsymmetricKey
/** /**
* The constructor * The constructor
*
* @access public
*/ */
public function __construct() protected function __construct()
{ {
self::initialize_static_variables(); self::initialize_static_variables();
@ -180,51 +134,14 @@ abstract class AsymmetricKey
$this->hmac = new Hash('sha256'); $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 * Initialize static variables
*
* @access private
*/ */
protected static function initialize_static_variables() protected static function initialize_static_variables()
{ {
if (!isset(self::$zero)) { if (!isset(self::$zero)) {
self::$zero= new BigInteger(0); self::$zero= new BigInteger(0);
self::$one = new BigInteger(1); self::$one = new BigInteger(1);
self::$configFile = __DIR__ . '/../../openssl.cnf';
} }
self::loadPlugins('Keys'); 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 * 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. * 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. * 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 * @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 * @access public
* @param string $hash * @param string $hash
*/ */
public function setHash($hash) public function withHash($hash)
{ {
$this->hash = new Hash($hash); $new = clone $this;
$this->hmac = new Hash($hash);
$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 (isset($private['publicKey'])) {
if ($private['publicKey'][0] != "\0") { 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); $private['publicKey'] = substr($private['publicKey'], 1);
} }
@ -494,7 +494,7 @@ abstract class PKCS8 extends PKCS
if (is_array($public)) { if (is_array($public)) {
if ($public['publicKey'][0] != "\0") { 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 (is_array(static::OID_NAME)) {
if (!in_array($public['publicKeyAlgorithm']['algorithm'], 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 * <?php
* include 'vendor/autoload.php'; * include 'vendor/autoload.php';
* *
* extract(\phpseclib\Crypt\DSA::createKey()); * $private = \phpseclib\Crypt\DSA::createKey();
* $public = $private->getPublicKey();
* *
* $plaintext = 'terrafrost'; * $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> * </code>
* *
@ -30,14 +31,11 @@
namespace phpseclib\Crypt; namespace phpseclib\Crypt;
use ParagonIE\ConstantTime\Base64;
use phpseclib\File\ASN1;
use phpseclib\Math\BigInteger;
use phpseclib\Crypt\Common\AsymmetricKey; use phpseclib\Crypt\Common\AsymmetricKey;
use phpseclib\Math\PrimeField; use phpseclib\Crypt\DSA\PrivateKey;
use phpseclib\Crypt\ECDSA\Signature\ASN1 as ASN1Signature; use phpseclib\Crypt\DSA\PublicKey;
use phpseclib\Exception\UnsupportedOperationException; use phpseclib\Crypt\DSA\Parameters;
use phpseclib\Exception\NoKeyLoadedException; use phpseclib\Math\BigInteger;
use phpseclib\Exception\InsufficientSetupException; use phpseclib\Exception\InsufficientSetupException;
/** /**
@ -47,7 +45,7 @@ use phpseclib\Exception\InsufficientSetupException;
* @author Jim Wigginton <terrafrost@php.net> * @author Jim Wigginton <terrafrost@php.net>
* @access public * @access public
*/ */
class DSA extends AsymmetricKey abstract class DSA extends AsymmetricKey
{ {
/** /**
* Algorithm Name * Algorithm Name
@ -63,7 +61,7 @@ class DSA extends AsymmetricKey
* @var \phpseclib\Math\BigInteger * @var \phpseclib\Math\BigInteger
* @access private * @access private
*/ */
private $p; protected $p;
/** /**
* DSA Group Order q * DSA Group Order q
@ -81,15 +79,7 @@ class DSA extends AsymmetricKey
* @var \phpseclib\Math\BigInteger * @var \phpseclib\Math\BigInteger
* @access private * @access private
*/ */
private $g; protected $g;
/**
* DSA secret exponent x
*
* @var \phpseclib\Math\BigInteger
* @access private
*/
protected $x;
/** /**
* DSA public key value y * DSA public key value y
@ -97,7 +87,23 @@ class DSA extends AsymmetricKey
* @var \phpseclib\Math\BigInteger * @var \phpseclib\Math\BigInteger
* @access private * @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 * Create DSA parameters
@ -133,7 +139,7 @@ class DSA extends AsymmetricKey
case $L == 3072 && $N == 256: case $L == 3072 && $N == 256:
break; break;
default: default:
return false; throw new \InvalidArgumentException('Invalid values for N and L');
} }
$two = new BigInteger(2); $two = new BigInteger(2);
@ -163,7 +169,7 @@ class DSA extends AsymmetricKey
$h = $h->add(self::$one); $h = $h->add(self::$one);
} }
$dsa = new DSA(); $dsa = new Parameters;
$dsa->p = $p; $dsa->p = $p;
$dsa->q = $q; $dsa->q = $q;
$dsa->g = $g; $dsa->g = $g;
@ -174,17 +180,14 @@ class DSA extends AsymmetricKey
/** /**
* Create public / private key pair. * Create public / private key pair.
* *
* This method is a bit polymorphic. It can take a DSA object (eg. pre-loaded with parameters), * This method is a bit polymorphic. It can take a DSA/Parameters object, L / N as two distinct parameters or
* L / N as two distinct parameters or no parameters (at which point L and N will be generated * no parameters (at which point L and N will be generated with this method)
* with this method)
* *
* Returns an array with the following two elements: * Returns the private key, from which the publickey can be extracted
* - 'privatekey': The private key.
* - 'publickey': The public key.
* *
* @param $args[] * @param $args[]
* @access public * @access public
* @return array|DSA * @return DSA\PrivateKey
*/ */
public static function createKey(...$args) 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])) { if (count($args) == 2 && is_int($args[0]) && is_int($args[1])) {
$private = self::createParameters($args[0], $args[1]); $params = self::createParameters($args[0], $args[1]);
} else if (count($args) == 1 && $args[0] instanceof DSA) { } else if (count($args) == 1 && $args[0] instanceof Parameters) {
$private = clone $args[0]; $params = $args[0];
} else if (!count($args)) { } else if (!count($args)) {
$private = self::createParameters(); $params = self::createParameters();
} else { } else {
throw new InsufficientSetupException('Valid parameters are either two integers (L and N), a single DSA object or no parameters at all.'); 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->x = BigInteger::randomRange(self::$one, $private->q->subtract(self::$one));
$private->y = $private->g->powMod($private->x, $private->p); $private->y = $private->g->powMod($private->x, $private->p);
$public = clone $private; //$public = clone $private;
unset($public->x); //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 * @return bool
* @access public * @access public
* @param string $key * @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(); self::initialize_static_variables();
@ -230,55 +241,38 @@ class DSA extends AsymmetricKey
self::useBestEngine(); self::useBestEngine();
} }
if ($key instanceof DSA) { $components = parent::load($key, $type, $password);
$this->privateKeyFormat = $key->privateKeyFormat; if (!isset($components['x']) && !isset($components['y'])) {
$this->publicKeyFormat = $key->publicKeyFormat; $new = new Parameters;
$this->format = $key->format; } else if (isset($components['x'])) {
$this->p = $key->p; $new = new PrivateKey;
$this->q = $key->q; $new->x = $components['x'];
$this->g = $key->g; } else {
$this->x = $key->x; $new = new PublicKey;
$this->y = $key->y;
$this->parametersFormat = $key->parametersFormat;
return true;
} }
$components = parent::load($key, $type); $new->p = $components['p'];
if ($components === false) { $new->q = $components['q'];
$this->format = null; $new->g = $components['g'];
$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;
if (isset($components['y'])) { 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() public function getLength()
{ {
return isset($this->p) ? return ['L' => $this->p->getLength(), 'N' => $this->q->getLength()];
['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);
} }
/** /**
@ -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 * @access public
* @param string $message * @param string $type optional
* @param string $format optional
* @return mixed * @return mixed
*/ */
public function sign($message, $format = 'ASN1') public function getParameters()
{ {
$shortFormat = $format; $type = self::validatePlugin('Keys', 'PKCS1', 'saveParameters');
$format = self::validatePlugin('Signature', $format);
if ($format === false) {
return false;
}
if (empty($this->x)) { $key = $type::saveParameters($this->p, $this->q, $this->g);
if (empty($this->y)) { return DSA::load($key, 'PKCS1')
throw new NoKeyLoadedException('No key has been loaded'); ->withHash($this->hash->getHash())
} ->withSignatureFormat($this->shortFormat);
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);
} }
/** /**
* Verify a signature * Determines the signature padding mode
*
* Valid values are: ASN1, SSH2, Raw
* *
* @see self::verify()
* @access public * @access public
* @param string $message * @param string $padding
* @param string $signature
* @param string $format optional
* @return mixed
*/ */
public function verify($message, $signature, $format = 'ASN1') public function withSignatureFormat($format)
{ {
$format = self::validatePlugin('Signature', $format); $new = clone $this;
if ($format === false) { $new->shortFormat = $format;
return false; $new->format = self::validatePlugin('Signature', $format);
} return $new;
$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);
} }
} }

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 * <?php
* include 'vendor/autoload.php'; * include 'vendor/autoload.php';
* *
* extract(\phpseclib\Crypt\ECDSA::createKey()); * $private = \phpseclib\Crypt\ECDSA::createKey('secp256k1');
* $public = $private->getPublicKey();
* *
* $plaintext = 'terrafrost'; * $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> * </code>
* *
@ -30,21 +31,18 @@
namespace phpseclib\Crypt; namespace phpseclib\Crypt;
use phpseclib\Math\BigInteger;
use phpseclib\Crypt\Common\AsymmetricKey; use phpseclib\Crypt\Common\AsymmetricKey;
use phpseclib\Exception\UnsupportedCurveException; use phpseclib\Crypt\ECDSA\PrivateKey;
use phpseclib\Exception\UnsupportedOperationException; use phpseclib\Crypt\ECDSA\PublicKey;
use phpseclib\Exception\UnsupportedAlgorithmException; use phpseclib\Crypt\ECDSA\Parameters;
use phpseclib\Exception\NoKeyLoadedException;
use phpseclib\Exception\InsufficientSetupException;
use phpseclib\File\ASN1;
use phpseclib\File\ASN1\Maps\ECParameters;
use phpseclib\Crypt\ECDSA\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; use phpseclib\Crypt\ECDSA\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
use phpseclib\Crypt\ECDSA\Curves\Ed25519; use phpseclib\Crypt\ECDSA\Curves\Ed25519;
use phpseclib\Crypt\ECDSA\Curves\Ed448; use phpseclib\Crypt\ECDSA\Curves\Ed448;
use phpseclib\Crypt\ECDSA\Keys\PKCS1; use phpseclib\Crypt\ECDSA\Keys\PKCS1;
use phpseclib\Crypt\ECDSA\Keys\PKCS8; use phpseclib\File\ASN1\Maps\ECParameters;
use phpseclib\Crypt\ECDSA\Signature\ASN1 as ASN1Signature; use phpseclib\File\ASN1;
use phpseclib\Exception\UnsupportedCurveException;
use phpseclib\Exception\UnsupportedAlgorithmException;
/** /**
* Pure-PHP implementation of ECDSA. * Pure-PHP implementation of ECDSA.
@ -53,7 +51,7 @@ use phpseclib\Crypt\ECDSA\Signature\ASN1 as ASN1Signature;
* @author Jim Wigginton <terrafrost@php.net> * @author Jim Wigginton <terrafrost@php.net>
* @access public * @access public
*/ */
class ECDSA extends AsymmetricKey abstract class ECDSA extends AsymmetricKey
{ {
/** /**
* Algorithm Name * Algorithm Name
@ -63,30 +61,35 @@ class ECDSA extends AsymmetricKey
*/ */
const ALGORITHM = 'ECDSA'; 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 * Public Key QA
* *
* @var object[] * @var object[]
*/ */
private $QA; protected $QA;
/** /**
* Curve * Curve
* *
* @var \phpseclib\Crypt\ECDSA\BaseCurves\Base * @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 * Curve Name
@ -96,44 +99,18 @@ class ECDSA extends AsymmetricKey
private $curveName; private $curveName;
/** /**
* Curve Order * Context
* *
* Used for deterministic ECDSA * @var string
*
* @var \phpseclib\Math\BigInteger
*/ */
protected $q; protected $context;
/**
* 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;
/** /**
* Create public / private key pair. * Create public / private key pair.
* *
* @access public * @access public
* @param string $curve * @param string $curve
* @return \phpseclib\Crypt\ECDSA[] * @return \phpseclib\Crypt\ECDSA\PrivateKey
*/ */
public static function createKey($curve) public static function createKey($curve)
{ {
@ -143,51 +120,62 @@ class ECDSA extends AsymmetricKey
self::useBestEngine(); 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(); $kp = sodium_crypto_sign_keypair();
$privatekey = new static(); $privatekey = ECDSA::load(sodium_crypto_sign_secretkey($kp), 'libsodium');
$privatekey->load(sodium_crypto_sign_secretkey($kp)); //$publickey = ECDSA::load(sodium_crypto_sign_publickey($kp), 'libsodium');
$publickey = new static(); $privatekey->curveName = 'Ed25519';
$publickey->load(sodium_crypto_sign_publickey($kp)); //$publickey->curveName = $curve;
$publickey->curveName = $privatekey->curveName = $curve; return $privatekey;
return compact('privatekey', 'publickey');
} }
$privatekey = new static(); $privatekey = new PrivateKey;
$curveName = $curve; $curveName = $curve;
$curve = '\phpseclib\Crypt\ECDSA\Curves\\' . $curve; $curve = '\phpseclib\Crypt\ECDSA\Curves\\' . $curve;
if (!class_exists($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(); $curve = new $curve();
$privatekey->dA = $dA = $curve->createRandomMultiplier(); $privatekey->dA = $dA = $curve->createRandomMultiplier();
$privatekey->QA = $curve->multiplyPoint($curve->getBasePoint(), $dA); $privatekey->QA = $curve->multiplyPoint($curve->getBasePoint(), $dA);
$privatekey->curve = $curve; $privatekey->curve = $curve;
$publickey = clone $privatekey; //$publickey = clone $privatekey;
unset($publickey->dA); //unset($publickey->dA);
unset($publickey->x); //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 * 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) * Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed)
* * @return bool
* @access public * @access public
* @param string $key * @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(); self::initialize_static_variables();
@ -195,54 +183,42 @@ class ECDSA extends AsymmetricKey
self::useBestEngine(); self::useBestEngine();
} }
if ($key instanceof ECDSA) { $components = parent::load($key, $type, $password);
$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;
parent::load($key, false); if (!isset($components['dA']) && !isset($components['QA'])) {
$new = new Parameters;
return true; $new->curve = $components['curve'];
return $new;
} }
$components = parent::load($key, $type); $new = isset($components['dA']) ?
if ($components === false) { new PrivateKey :
$this->clearKey(); new PublicKey;
return false; $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') { if ($new->curve instanceof TwistedEdwardsCurve) {
$this->clearKey(); return $new->withHash($components['curve']::HASH);
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');
} }
$this->curve = $components['curve']; return $new;
$this->QA = $components['QA'];
$this->dA = isset($components['dA']) ? $components['dA'] : null;
return true;
} }
/** /**
* 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->format = self::validatePlugin('Signature', 'ASN1');
$this->dA = null; $this->shortFormat = 'ASN1';
$this->QA = null;
$this->curve = null; parent::__construct();
} }
/** /**
@ -306,117 +282,9 @@ class ECDSA extends AsymmetricKey
*/ */
public function getLength() public function getLength()
{ {
if (!isset($this->QA)) {
return 0;
}
return $this->curve->getLength(); 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 * Returns the current engine being used
* *
@ -427,10 +295,6 @@ class ECDSA extends AsymmetricKey
*/ */
public function getEngine() 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) { if ($this->curve instanceof TwistedEdwardsCurve) {
return $this->curve instanceof Ed25519 && self::$engines['libsodium'] && !isset($this->context) ? return $this->curve instanceof Ed25519 && self::$engines['libsodium'] && !isset($this->context) ?
'libsodium' : 'PHP'; 'libsodium' : 'PHP';
@ -440,6 +304,41 @@ class ECDSA extends AsymmetricKey
'OpenSSL' : 'PHP'; '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 * Sets the context
* *
@ -450,11 +349,16 @@ class ECDSA extends AsymmetricKey
* @access public * @access public
* @param string $context optional * @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)) { if (!isset($context)) {
$this->context = null; $new->context = null;
return; return $new;
} }
if (!is_string($context)) { if (!is_string($context)) {
throw new \InvalidArgumentException('setContext expects a string'); throw new \InvalidArgumentException('setContext expects a string');
@ -462,7 +366,8 @@ class ECDSA extends AsymmetricKey
if (strlen($context) > 255) { if (strlen($context) > 255) {
throw new \LengthException('The context is supposed to be, at most, 255 bytes long'); 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 * @access public
* @param string $hash * @param string $hash
*/ */
public function setHash($hash) public function withHash($hash)
{ {
if ($this->curve instanceof Ed25519 && $hash != 'sha512') { if ($this->curve instanceof Ed25519 && $hash != 'sha512') {
throw new UnsupportedAlgorithmException('Ed25519 only supports sha512 as a hash'); 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'); throw new UnsupportedAlgorithmException('Ed448 only supports shake256 with a length of 114 bytes');
} }
parent::setHash($hash); return parent::withHash($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);
} }
} }

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); 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 (isset($components['modulus']) && isset($components['publicExponent'])) {
if (count($components) == 3) {
$components['isPublicKey'] = true;
}
return $components; 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\Hash;
use phpseclib\Crypt\Random; use phpseclib\Crypt\Random;
use phpseclib\Crypt\RSA; 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\Exception\UnsupportedAlgorithmException;
use phpseclib\File\ASN1\Element; use phpseclib\File\ASN1\Element;
use phpseclib\Math\BigInteger; use phpseclib\Math\BigInteger;
@ -273,18 +277,18 @@ class X509
if (!self::$oidsLoaded) { if (!self::$oidsLoaded) {
// OIDs from RFC5280 and those RFCs mentioned in RFC5280#section-4.1.1.2 // OIDs from RFC5280 and those RFCs mentioned in RFC5280#section-4.1.1.2
ASN1::loadOIDs([ ASN1::loadOIDs([
'id-pkix' => '1.3.6.1.5.5.7', //'id-pkix' => '1.3.6.1.5.5.7',
'id-pe' => '1.3.6.1.5.5.7.1', //'id-pe' => '1.3.6.1.5.5.7.1',
'id-qt' => '1.3.6.1.5.5.7.2', //'id-qt' => '1.3.6.1.5.5.7.2',
'id-kp' => '1.3.6.1.5.5.7.3', //'id-kp' => '1.3.6.1.5.5.7.3',
'id-ad' => '1.3.6.1.5.5.7.48', //'id-ad' => '1.3.6.1.5.5.7.48',
'id-qt-cps' => '1.3.6.1.5.5.7.2.1', '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-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-ocsp' =>'1.3.6.1.5.5.7.48.1',
'id-ad-caIssuers' => '1.3.6.1.5.5.7.48.2', '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-timeStamping' => '1.3.6.1.5.5.7.48.3',
'id-ad-caRepository' => '1.3.6.1.5.5.7.48.5', '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-name' => '2.5.4.41',
'id-at-surname' => '2.5.4.4', 'id-at-surname' => '2.5.4.4',
'id-at-givenName' => '2.5.4.42', 'id-at-givenName' => '2.5.4.42',
@ -307,18 +311,19 @@ class X509
'id-at-role' => '2.5.4.72', 'id-at-role' => '2.5.4.72',
'id-at-postalAddress' => '2.5.4.16', 'id-at-postalAddress' => '2.5.4.16',
'id-domainComponent' => '0.9.2342.19200300.100.1.25', //'id-domainComponent' => '0.9.2342.19200300.100.1.25',
'pkcs-9' => '1.2.840.113549.1.9', //'pkcs-9' => '1.2.840.113549.1.9',
'pkcs-9-at-emailAddress' => '1.2.840.113549.1.9.1', '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-authorityKeyIdentifier' => '2.5.29.35',
'id-ce-subjectKeyIdentifier' => '2.5.29.14', 'id-ce-subjectKeyIdentifier' => '2.5.29.14',
'id-ce-keyUsage' => '2.5.29.15', 'id-ce-keyUsage' => '2.5.29.15',
'id-ce-privateKeyUsagePeriod' => '2.5.29.16', 'id-ce-privateKeyUsagePeriod' => '2.5.29.16',
'id-ce-certificatePolicies' => '2.5.29.32', '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-policyMappings' => '2.5.29.33',
'id-ce-subjectAltName' => '2.5.29.17', 'id-ce-subjectAltName' => '2.5.29.17',
'id-ce-issuerAltName' => '2.5.29.18', 'id-ce-issuerAltName' => '2.5.29.18',
'id-ce-subjectDirectoryAttributes' => '2.5.29.9', 'id-ce-subjectDirectoryAttributes' => '2.5.29.9',
@ -327,7 +332,7 @@ class X509
'id-ce-policyConstraints' => '2.5.29.36', 'id-ce-policyConstraints' => '2.5.29.36',
'id-ce-cRLDistributionPoints' => '2.5.29.31', 'id-ce-cRLDistributionPoints' => '2.5.29.31',
'id-ce-extKeyUsage' => '2.5.29.37', '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-serverAuth' => '1.3.6.1.5.5.7.3.1',
'id-kp-clientAuth' => '1.3.6.1.5.5.7.3.2', 'id-kp-clientAuth' => '1.3.6.1.5.5.7.3.2',
'id-kp-codeSigning' => '1.3.6.1.5.5.7.3.3', '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-cRLReasons' => '2.5.29.21',
'id-ce-certificateIssuer' => '2.5.29.29', 'id-ce-certificateIssuer' => '2.5.29.29',
'id-ce-holdInstructionCode' => '2.5.29.23', '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-none' => '1.2.840.10040.2.1',
'id-holdinstruction-callissuer' => '1.2.840.10040.2.2', 'id-holdinstruction-callissuer' => '1.2.840.10040.2.2',
'id-holdinstruction-reject' => '1.2.840.10040.2.3', 'id-holdinstruction-reject' => '1.2.840.10040.2.3',
'id-ce-invalidityDate' => '2.5.29.24', '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', 'rsaEncryption' => '1.2.840.113549.1.1.1',
'md2WithRSAEncryption' => '1.2.840.113549.1.1.2', 'md2WithRSAEncryption' => '1.2.840.113549.1.1.2',
'md5WithRSAEncryption' => '1.2.840.113549.1.1.4', 'md5WithRSAEncryption' => '1.2.840.113549.1.1.4',
'sha1WithRSAEncryption' => '1.2.840.113549.1.1.5', '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', 'sha224WithRSAEncryption' => '1.2.840.113549.1.1.14',
'sha256WithRSAEncryption' => '1.2.840.113549.1.1.11', 'sha256WithRSAEncryption' => '1.2.840.113549.1.1.11',
'sha384WithRSAEncryption' => '1.2.840.113549.1.1.12', 'sha384WithRSAEncryption' => '1.2.840.113549.1.1.12',
'sha512WithRSAEncryption' => '1.2.840.113549.1.1.13', '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-ecPublicKey' => '1.2.840.10045.2.1',
'id-sha384' => '2.16.840.1.101.3.4.2.2', 'ecdsa-with-SHA1' => '1.2.840.10045.4.1',
'id-sha512' => '2.16.840.1.101.3.4.2.3', // from https://tools.ietf.org/html/rfc5758#section-3.2
'id-GostR3411-94-with-GostR3410-94' => '1.2.643.2.2.4', 'ecdsa-with-SHA224' => '1.2.840.10045.4.3.1',
'id-GostR3411-94-with-GostR3410-2001' => '1.2.643.2.2.3', 'ecdsa-with-SHA256' => '1.2.840.10045.4.3.2',
'id-GostR3410-2001' => '1.2.643.2.2.20', 'ecdsa-with-SHA384' => '1.2.840.10045.4.3.3',
'id-GostR3410-94' => '1.2.643.2.2.19', '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 Object Identifiers from "Netscape Certificate Extensions"
'netscape' => '2.16.840.1.113730', 'netscape' => '2.16.840.1.113730',
'netscape-cert-extension' => '2.16.840.1.113730.1', '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/issuer/rdnSequence');
$this->mapInDNs($x509, 'tbsCertificate/subject/rdnSequence'); $this->mapInDNs($x509, 'tbsCertificate/subject/rdnSequence');
$key = &$x509['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']; $key = $x509['tbsCertificate']['subjectPublicKeyInfo'];
$key = $this->reformatKey($x509['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $key); $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->currentCert = $x509;
$this->dn = $x509['tbsCertificate']['subject']; $this->dn = $x509['tbsCertificate']['subject'];
@ -531,21 +505,14 @@ class X509
case is_object($cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']): case is_object($cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']):
break; break;
default: default:
switch ($algorithm) { $cert['tbsCertificate']['subjectPublicKeyInfo'] = new Element(
case 'rsaEncryption': base64_decode(preg_replace('#-.+-|[\r\n]#', '', $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']))
$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
given that and the fact that RSA keys appear to be the only key type for which the parameters field can be blank, if ($algorithm == 'rsaEncryption') {
it seems like perhaps the ASN.1 description ought not say the parameters field is OPTIONAL, but whatever. $cert['signatureAlgorithm']['parameters'] = null;
*/ $cert['tbsCertificate']['signature']['parameters'] = null;
$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;
}
} }
$filters = []; $filters = [];
@ -1389,9 +1356,7 @@ class X509
{ {
switch ($publicKeyAlgorithm) { switch ($publicKeyAlgorithm) {
case 'rsaEncryption': case 'rsaEncryption':
$rsa = new RSA(); $key = RSA::load($publicKey, 'PKCS8');
$rsa->load($publicKey);
switch ($signatureAlgorithm) { switch ($signatureAlgorithm) {
case 'md2WithRSAEncryption': case 'md2WithRSAEncryption':
case 'md5WithRSAEncryption': case 'md5WithRSAEncryption':
@ -1400,10 +1365,41 @@ class X509
case 'sha256WithRSAEncryption': case 'sha256WithRSAEncryption':
case 'sha384WithRSAEncryption': case 'sha384WithRSAEncryption':
case 'sha512WithRSAEncryption': case 'sha512WithRSAEncryption':
$rsa->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm)); $key = $key
if (!@$rsa->verify($signatureSubject, $signature, RSA::PADDING_PKCS1)) { ->withHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm))
return false; ->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; break;
default: default:
throw new UnsupportedAlgorithmException('Signature algorithm unsupported'); throw new UnsupportedAlgorithmException('Signature algorithm unsupported');
@ -1413,7 +1409,7 @@ class X509
throw new UnsupportedAlgorithmException('Public key algorithm unsupported'); 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; 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 * Decodes an IP address
* *
@ -2053,9 +2023,8 @@ class X509
* @access public * @access public
* @return bool * @return bool
*/ */
public function setPublicKey($key) public function setPublicKey(PublicKey $key)
{ {
$key->setPublicKey();
$this->publicKey = $key; $this->publicKey = $key;
} }
@ -2067,7 +2036,7 @@ class X509
* @param object $key * @param object $key
* @access public * @access public
*/ */
public function setPrivateKey($key) public function setPrivateKey(PrivateKey $key)
{ {
$this->privateKey = $key; $this->privateKey = $key;
} }
@ -2115,15 +2084,16 @@ class X509
switch ($keyinfo['algorithm']['algorithm']) { switch ($keyinfo['algorithm']['algorithm']) {
case 'rsaEncryption': case 'rsaEncryption':
$publicKey = new RSA(); return RSA::load($key, 'PKCS8');
$publicKey->load($key); case 'id-ecPublicKey':
$publicKey->setPublicKey(); case 'id-Ed25519':
break; case 'id-Ed448':
default: return ECDSA::load($key, 'PKCS8');
return false; 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']); $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);
$algorithm = &$csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm']; $key = $csr['certificationRequestInfo']['subjectPKInfo'];
$key = &$csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']; $key = ASN1::encodeDER($key, Maps\SubjectPublicKeyInfo::MAP);
$key = $this->reformatKey($algorithm, $key); $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'] =
"-----BEGIN PUBLIC KEY-----\r\n" .
chunk_split(base64_encode($key), 64) .
"-----END PUBLIC KEY-----";
switch ($algorithm) { $this->publicKey = null;
case 'rsaEncryption': $this->publicKey = $this->getPublicKey();
$this->publicKey = new RSA();
$this->publicKey->load($key);
$this->publicKey->setPublicKey();
break;
default:
$this->publicKey = null;
}
$this->currentKeyIdentifier = null; $this->currentKeyIdentifier = null;
$this->currentCert = $csr; $this->currentCert = $csr;
@ -2224,14 +2190,9 @@ class X509
case is_object($csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']): case is_object($csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']):
break; break;
default: default:
switch ($algorithm) { $csr['certificationRequestInfo']['subjectPKInfo'] = new Element(
case 'rsaEncryption': base64_decode(preg_replace('#-.+-|[\r\n]#', '', $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']))
$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;
}
} }
$filters = []; $filters = [];
@ -2305,18 +2266,15 @@ class X509
$this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);
$algorithm = &$spkac['publicKeyAndChallenge']['spki']['algorithm']['algorithm']; $key = $spkac['publicKeyAndChallenge']['spki'];
$key = &$spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']; $key = ASN1::encodeDER($key, Maps\SubjectPublicKeyInfo::MAP);
$key = $this->reformatKey($algorithm, $key); $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey'] =
"-----BEGIN PUBLIC KEY-----\r\n" .
chunk_split(base64_encode($key), 64) .
"-----END PUBLIC KEY-----";
switch ($algorithm) { $this->publicKey = null;
case 'rsaEncryption': $this->publicKey = $this->getPublicKey();
$this->publicKey = new RSA();
$this->publicKey->load($key);
break;
default:
$this->publicKey = null;
}
$this->currentKeyIdentifier = null; $this->currentKeyIdentifier = null;
$this->currentCert = $spkac; $this->currentCert = $spkac;
@ -2344,11 +2302,9 @@ class X509
case is_object($spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']): case is_object($spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']):
break; break;
default: default:
switch ($algorithm) { $spkac['publicKeyAndChallenge']['spki'] = new Element(
case 'rsaEncryption': base64_decode(preg_replace('#-.+-|[\r\n]#', '', $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']))
$spkac['publicKeyAndChallenge']['spki']['subjectPublicKey'] );
= "\0" . Base64::decode(preg_replace('#-.+-|[\r\n]#', '', $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']));
}
} }
$spkac = ASN1::encodeDER($spkac, Maps\SignedPublicKeyAndChallenge::MAP); $spkac = ASN1::encodeDER($spkac, Maps\SignedPublicKeyAndChallenge::MAP);
@ -2535,7 +2491,7 @@ class X509
} }
$currentCert = isset($this->currentCert) ? $this->currentCert : null; $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'])) { if (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertificate'])) {
$this->currentCert = $subject->currentCert; $this->currentCert = $subject->currentCert;
@ -2713,17 +2669,12 @@ class X509
} }
$origPublicKey = $this->publicKey; $origPublicKey = $this->publicKey;
$class = get_class($this->privateKey); $this->publicKey = $this->privateKey->getPublicKey();
$this->publicKey = new $class(); $publicKey = $this->formatSubjectPublicKey();
$this->publicKey->load($this->privateKey->getPublicKey());
$this->publicKey->setPublicKey();
if (!($publicKey = $this->formatSubjectPublicKey())) {
return false;
}
$this->publicKey = $origPublicKey; $this->publicKey = $origPublicKey;
$currentCert = isset($this->currentCert) ? $this->currentCert : null; $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'])) { if (isset($this->currentCert) && is_array($this->currentCert) && isset($this->currentCert['certificationRequestInfo'])) {
$this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm; $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm;
@ -2772,18 +2723,12 @@ class X509
} }
$origPublicKey = $this->publicKey; $origPublicKey = $this->publicKey;
$class = get_class($this->privateKey); $this->publicKey = $this->privateKey->getPublicKey();
$this->publicKey = new $class();
$this->publicKey->load($this->privateKey->getPublicKey());
$this->publicKey->setPublicKey();
$publicKey = $this->formatSubjectPublicKey(); $publicKey = $this->formatSubjectPublicKey();
if (!$publicKey) {
return false;
}
$this->publicKey = $origPublicKey; $this->publicKey = $origPublicKey;
$currentCert = isset($this->currentCert) ? $this->currentCert : null; $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? // 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'])) { 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 * @throws \phpseclib\Exception\UnsupportedAlgorithmException if the algorithm is unsupported
* @return mixed * @return mixed
*/ */
private function signHelper($key, $signatureAlgorithm) private function signHelper(PrivateKey $key, $signatureAlgorithm)
{ {
if ($key instanceof RSA) { if ($key instanceof RSA) {
switch ($signatureAlgorithm) { switch ($signatureAlgorithm) {
@ -2977,9 +2922,52 @@ class X509
case 'sha256WithRSAEncryption': case 'sha256WithRSAEncryption':
case 'sha384WithRSAEncryption': case 'sha384WithRSAEncryption':
case 'sha512WithRSAEncryption': 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; return $this->currentCert;
default: default:
throw new UnsupportedAlgorithmException('Signature algorithm unsupported'); throw new UnsupportedAlgorithmException('Signature algorithm unsupported');
@ -3669,17 +3657,14 @@ class X509
*/ */
private function formatSubjectPublicKey() private function formatSubjectPublicKey()
{ {
if ($this->publicKey instanceof RSA) { $publicKey = base64_decode(preg_replace('#-.+-|[\r\n]#', '', $this->publicKey));
// 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')
];
}
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; namespace phpseclib\Math\BigInteger\Engines;
use phpseclib\Crypt\RSA; use phpseclib\Crypt\RSA;
use phpseclib\Crypt\RSA\Keys\PKCS8;
use phpseclib\Math\BigInteger; use phpseclib\Math\BigInteger;
/** /**
@ -51,11 +52,11 @@ abstract class OpenSSL
throw new \OutOfRangeException('Only modulo between 31 and 16384 bits are accepted'); throw new \OutOfRangeException('Only modulo between 31 and 16384 bits are accepted');
} }
$rsa = new RSA(); $key = PKCS8::savePublicKey(
$rsa->load([ new BigInteger($n),
'e' => new BigInteger($e), new BigInteger($e)
'n' => new BigInteger($n) );
]); $rsa = RSA::load($key);
//$rsa->setPublicKeyFormat('PKCS1'); //$rsa->setPublicKeyFormat('PKCS1');
$plaintext = str_pad($x->toBytes(), strlen($n->toBytes(true)) - 1, "\0", STR_PAD_LEFT); $plaintext = str_pad($x->toBytes(), strlen($n->toBytes(true)) - 1, "\0", STR_PAD_LEFT);

View File

@ -24,7 +24,7 @@
* <?php * <?php
* include 'vendor/autoload.php'; * include 'vendor/autoload.php';
* *
* $key = new \phpseclib\Crypt\RSA(); * \phpseclib\Crypt\PublicKeyLoader::load('...');
* //$key->setPassword('whatever'); * //$key->setPassword('whatever');
* $key->load(file_get_contents('privatekey')); * $key->load(file_get_contents('privatekey'));
* *
@ -49,12 +49,12 @@
namespace phpseclib\Net; namespace phpseclib\Net;
use ParagonIE\ConstantTime\Base64;
use phpseclib\Crypt\Blowfish; use phpseclib\Crypt\Blowfish;
use phpseclib\Crypt\Hash; use phpseclib\Crypt\Hash;
use phpseclib\Crypt\Random; use phpseclib\Crypt\Random;
use phpseclib\Crypt\RC4; use phpseclib\Crypt\RC4;
use phpseclib\Crypt\Rijndael; use phpseclib\Crypt\Rijndael;
use phpseclib\Crypt\Common\PrivateKey;
use phpseclib\Crypt\RSA; use phpseclib\Crypt\RSA;
use phpseclib\Crypt\DSA; use phpseclib\Crypt\DSA;
use phpseclib\Crypt\ECDSA; use phpseclib\Crypt\ECDSA;
@ -66,6 +66,7 @@ use phpseclib\System\SSH\Agent;
use phpseclib\System\SSH\Agent\Identity as AgentIdentity; use phpseclib\System\SSH\Agent\Identity as AgentIdentity;
use phpseclib\Exception\NoSupportedAlgorithmsException; use phpseclib\Exception\NoSupportedAlgorithmsException;
use phpseclib\Exception\UnsupportedAlgorithmException; use phpseclib\Exception\UnsupportedAlgorithmException;
use phpseclib\Exception\UnsupportedCurveException;
use phpseclib\Common\Functions\Strings; use phpseclib\Common\Functions\Strings;
/** /**
@ -2112,9 +2113,11 @@ class SSH2
return !is_string($password) && !is_array($password) ? false : $this->keyboard_interactive_process($password); 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); return $this->privatekey_login($username, $password);
} elseif ($password instanceof Agent) { }
if ($password instanceof Agent) {
return $this->ssh_agent_login($username, $password); return $this->ssh_agent_login($username, $password);
} }
@ -2126,6 +2129,10 @@ class SSH2
return false; 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)) { if (!isset($password)) {
$packet = Strings::packSSH2( $packet = Strings::packSSH2(
'Cs3', 'Cs3',
@ -2361,7 +2368,7 @@ class SSH2
* @return bool * @return bool
* @access private * @access private
*/ */
private function ssh_agent_login($username, $agent) private function ssh_agent_login($username, Agent $agent)
{ {
$this->agent = $agent; $this->agent = $agent;
$keys = $agent->requestIdentities(); $keys = $agent->requestIdentities();
@ -2378,42 +2385,69 @@ class SSH2
* Login with an RSA private key * Login with an RSA private key
* *
* @param string $username * @param string $username
* @param \phpseclib\Crypt\RSA $password * @param \phpseclib\Crypt\Common\PrivateKey $privatekey
* @return bool * @return bool
* @throws \RuntimeException on connection error * @throws \RuntimeException on connection error
* @access private * @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} * @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. * 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();
$publickey = $privatekey->getPublicKey('Raw');
if ($publickey === false) { if ($publickey instanceof RSA) {
return false; $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( $publickeyStr = $publickey->toString('OpenSSH');
'sii', $publickeyStr = base64_decode(preg_replace('#(^.*? )|( .*?)$#', '', $publickeyStr));
'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';
}
$part1 = Strings::packSSH2( $part1 = Strings::packSSH2(
'Csss', 'Csss',
@ -2422,7 +2456,7 @@ class SSH2
'ssh-connection', 'ssh-connection',
'publickey' 'publickey'
); );
$part2 = Strings::packSSH2('ss', $signatureType, $publickey); $part2 = Strings::packSSH2('ss', $signatureType, $publickeyStr);
$packet = $part1 . chr(0) . $part2; $packet = $part1 . chr(0) . $part2;
$this->send_binary_packet($packet); $this->send_binary_packet($packet);
@ -2438,7 +2472,6 @@ class SSH2
case NET_SSH2_MSG_USERAUTH_FAILURE: case NET_SSH2_MSG_USERAUTH_FAILURE:
list($message) = Strings::unpackSSH2('s', $response); list($message) = Strings::unpackSSH2('s', $response);
$this->errors[] = 'SSH_MSG_USERAUTH_FAILURE: ' . $message; $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE: ' . $message;
return false; return false;
case NET_SSH2_MSG_USERAUTH_PK_OK: 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 // 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; $packet = $part1 . chr(1) . $part2;
$privatekey->setHash($hash); $privatekey = $privatekey->withHash($hash);
$signature = $privatekey->sign(Strings::packSSH2('s', $this->session_id) . $packet, RSA::PADDING_PKCS1); $signature = $privatekey->sign(Strings::packSSH2('s', $this->session_id) . $packet);
$signature = Strings::packSSH2('ss', $signatureType, $signature); if ($publickey instanceof RSA) {
$signature = Strings::packSSH2('ss', $signatureType, $signature);
}
$packet.= Strings::packSSH2('s', $signature); $packet.= Strings::packSSH2('s', $signature);
$this->send_binary_packet($packet); $this->send_binary_packet($packet);
@ -4258,6 +4293,8 @@ class SSH2
return [ return [
'ssh-ed25519', // https://tools.ietf.org/html/draft-ietf-curdle-ssh-ed25519-02 'ssh-ed25519', // https://tools.ietf.org/html/draft-ietf-curdle-ssh-ed25519-02
'ecdsa-sha2-nistp256', // RFC 5656 'ecdsa-sha2-nistp256', // RFC 5656
'ecdsa-sha2-nistp384', // RFC 5656
'ecdsa-sha2-nistp521', // RFC 5656
'rsa-sha2-256', // RFC 8332 'rsa-sha2-256', // RFC 8332
'rsa-sha2-512', // RFC 8332 'rsa-sha2-512', // RFC 8332
'ssh-rsa', // RECOMMENDED sign Raw RSA Key 'ssh-rsa', // RECOMMENDED sign Raw RSA Key
@ -4553,7 +4590,7 @@ class SSH2
if ($this->signature_validated) { if ($this->signature_validated) {
return $this->bitmap ? return $this->bitmap ?
$this->signature_format . ' ' . Base64::encode($this->server_public_host_key) : $this->signature_format . ' ' . $server_public_host_key :
false; false;
} }
@ -4562,27 +4599,30 @@ class SSH2
switch ($this->signature_format) { switch ($this->signature_format) {
case 'ssh-ed25519': case 'ssh-ed25519':
case 'ecdsa-sha2-nistp256': case 'ecdsa-sha2-nistp256':
$ec = new ECDSA(); case 'ecdsa-sha2-nistp384':
$ec->load($server_public_host_key, 'OpenSSH'); case 'ecdsa-sha2-nistp521':
$key = ECDSA::load($server_public_host_key, 'OpenSSH')
->withSignatureFormat('SSH2');
switch ($this->signature_format) { switch ($this->signature_format) {
case 'ssh-ed25519': case 'ssh-ed25519':
//$ec->setHash('sha512');
Strings::shift($signature, 4 + strlen('ssh-ed25519') + 4); Strings::shift($signature, 4 + strlen('ssh-ed25519') + 4);
$hash = 'sha512';
break; break;
case 'ecdsa-sha2-nistp256': 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')) { $key = $key->withHash($hash);
return $this->disconnect_helper(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
};
break; break;
case 'ssh-dss': case 'ssh-dss':
$dsa = new DSA(); $key = DSA::load($server_public_host_key, 'OpenSSH')
$dsa->load($server_public_host_key, 'OpenSSH'); ->withSignatureFormat('SSH2')
$dsa->setHash('sha1'); ->withHash('sha1');
if (!$dsa->verify($this->exchange_hash, $signature, 'SSH2')) {
return $this->disconnect_helper(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
};
break; break;
case 'ssh-rsa': case 'ssh-rsa':
case 'rsa-sha2-256': case 'rsa-sha2-256':
@ -4594,8 +4634,8 @@ class SSH2
$temp = unpack('Nlength', Strings::shift($signature, 4)); $temp = unpack('Nlength', Strings::shift($signature, 4));
$signature = Strings::shift($signature, $temp['length']); $signature = Strings::shift($signature, $temp['length']);
$rsa = new RSA(); $key = RSA::load($server_public_host_key, 'OpenSSH')
$rsa->load($server_public_host_key, 'OpenSSH'); ->withPadding(RSA::SIGNATURE_PKCS1);
switch ($this->signature_format) { switch ($this->signature_format) {
case 'rsa-sha2-512': case 'rsa-sha2-512':
$hash = 'sha512'; $hash = 'sha512';
@ -4607,17 +4647,18 @@ class SSH2
default: default:
$hash = 'sha1'; $hash = 'sha1';
} }
$rsa->setHash($hash); $key = $key->withHash($hash);
if (!$rsa->verify($this->exchange_hash, $signature, RSA::PADDING_PKCS1)) {
return $this->disconnect_helper(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
}
break; break;
default: default:
$this->disconnect_helper(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); $this->disconnect_helper(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
throw new NoSupportedAlgorithmsException('Unsupported signature format'); 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\Exception\BadConfigurationException;
use phpseclib\System\SSH\Agent\Identity; use phpseclib\System\SSH\Agent\Identity;
use phpseclib\Common\Functions\Strings; use phpseclib\Common\Functions\Strings;
use phpseclib\Crypt\PublicKeyLoader;
/** /**
* Pure-PHP ssh-agent client identity factory * Pure-PHP ssh-agent client identity factory
@ -198,9 +199,8 @@ class Agent
$temp = $key_blob; $temp = $key_blob;
list($key_type) = Strings::unpackSSH2('s', $temp); list($key_type) = Strings::unpackSSH2('s', $temp);
switch ($key_type) { switch ($key_type) {
case 'ssh-rsa': case 'ssh-rsa':
$key = new RSA(); $key = PublicKeyLoader::load(base64_encode($key_blob));
$key->load($key_str);
break; break;
case 'ssh-dss': case 'ssh-dss':
// not currently supported // not currently supported

View File

@ -20,6 +20,8 @@ use phpseclib\Crypt\RSA;
use phpseclib\Exception\UnsupportedAlgorithmException; use phpseclib\Exception\UnsupportedAlgorithmException;
use phpseclib\System\SSH\Agent; use phpseclib\System\SSH\Agent;
use phpseclib\Common\Functions\Strings; use phpseclib\Common\Functions\Strings;
use phpseclib\Crypt\Common\PrivateKey;
/** /**
* Pure-PHP ssh-agent client identity object * Pure-PHP ssh-agent client identity object
@ -34,7 +36,7 @@ use phpseclib\Common\Functions\Strings;
* @author Jim Wigginton <terrafrost@php.net> * @author Jim Wigginton <terrafrost@php.net>
* @access internal * @access internal
*/ */
class Identity class Identity implements PrivateKey
{ {
/**@+ /**@+
* Signature Flags * Signature Flags
@ -107,7 +109,6 @@ class Identity
public function setPublicKey($key) public function setPublicKey($key)
{ {
$this->key = $key; $this->key = $key;
$this->key->setPublicKey();
} }
/** /**
@ -135,32 +136,48 @@ class Identity
*/ */
public function getPublicKey($type = 'PKCS8') public function getPublicKey($type = 'PKCS8')
{ {
return $this->key->getPublicKey($type); return $this->key;
} }
/** /**
* Sets the hash * Sets the hash
* *
* ssh-agent only supports signatures with sha1 hashes but to maintain BC with RSA.php this function exists * @param string $hash
*
* @param string $hash optional
* @access public * @access public
*/ */
public function setHash($hash) public function withHash($hash)
{ {
$this->flags = 0; $new = clone $this;
$new->flags = 0;
switch ($hash) { switch ($hash) {
case 'sha1': case 'sha1':
break; break;
case 'sha256': case 'sha256':
$this->flags = self::SSH_AGENT_RSA2_256; $new->flags = self::SSH_AGENT_RSA2_256;
break; break;
case 'sha512': case 'sha512':
$this->flags = self::SSH_AGENT_RSA2_512; $new->flags = self::SSH_AGENT_RSA2_512;
break; break;
default: default:
throw new UnsupportedAlgorithmException('The only supported hashes for RSA are sha1, sha256 and sha512'); 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 * @throws \phpseclib\Exception\UnsupportedAlgorithmException if the algorithm is unsupported
* @access public * @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 // 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( $packet = Strings::packSSH2(
'CssN', 'CssN',
@ -206,4 +219,26 @@ class Identity
return $signature_blob; 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;
use phpseclib\Crypt\DSA\Parameters;
use phpseclib\Crypt\DSA\PublicKey;
use phpseclib\Crypt\DSA\PrivateKey;
/** /**
* @requires PHP 7.0 * @requires PHP 7.0
@ -16,14 +19,17 @@ class Unit_Crypt_DSA_CreateKeyTest extends PhpseclibTestCase
public function testCreateParameters() public function testCreateParameters()
{ {
$dsa = DSA::createParameters(); $dsa = DSA::createParameters();
$this->assertInstanceOf('\phpseclib\Crypt\DSA', $dsa); $this->assertInstanceOf(Parameters::class, $dsa);
$this->assertRegexp('#BEGIN DSA PARAMETERS#', "$dsa"); $this->assertRegexp('#BEGIN DSA PARAMETERS#', "$dsa");
$dsa = DSA::createParameters(100, 100); try {
$this->assertFalse($dsa); $dsa = DSA::createParameters(100, 100);
} catch (Exception $e) {
$this->assertInstanceOf(Exception::class, $e);
}
$dsa = DSA::createParameters(512, 160); $dsa = DSA::createParameters(512, 160);
$this->assertInstanceOf('\phpseclib\Crypt\DSA', $dsa); $this->assertInstanceOf(Parameters::class, $dsa);
$this->assertRegexp('#BEGIN DSA PARAMETERS#', "$dsa"); $this->assertRegexp('#BEGIN DSA PARAMETERS#', "$dsa");
return $dsa; return $dsa;
@ -34,17 +40,17 @@ class Unit_Crypt_DSA_CreateKeyTest extends PhpseclibTestCase
*/ */
public function testCreateKey($params) public function testCreateKey($params)
{ {
extract(DSA::createKey()); $privatekey = DSA::createKey();
$this->assertInstanceOf('\phpseclib\Crypt\DSA', $privatekey); $this->assertInstanceOf(PrivateKey::class, $privatekey);
$this->assertInstanceOf('\phpseclib\Crypt\DSA', $publickey); $this->assertInstanceOf(PublicKey::class, $privatekey->getPublicKey());
extract(DSA::createKey($params)); $privatekey = DSA::createKey($params);
$this->assertInstanceOf('\phpseclib\Crypt\DSA', $privatekey); $this->assertInstanceOf(PrivateKey::class, $privatekey);
$this->assertInstanceOf('\phpseclib\Crypt\DSA', $publickey); $this->assertInstanceOf(PublicKey::class, $privatekey->getPublicKey());
extract(DSA::createKey(512, 160)); $privatekey = DSA::createKey(512, 160);
$this->assertInstanceOf('\phpseclib\Crypt\DSA', $privatekey); $this->assertInstanceOf(PrivateKey::class, $privatekey);
$this->assertInstanceOf('\phpseclib\Crypt\DSA', $publickey); $this->assertInstanceOf(PublicKey::class, $privatekey->getPublicKey());
} }
} }

View File

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

View File

@ -7,6 +7,7 @@
*/ */
use phpseclib\Crypt\DSA; use phpseclib\Crypt\DSA;
use phpseclib\Crypt\PublicKeyLoader;
class Unit_Crypt_DSA_SignatureTest extends PhpseclibTestCase class Unit_Crypt_DSA_SignatureTest extends PhpseclibTestCase
{ {
@ -14,9 +15,7 @@ class Unit_Crypt_DSA_SignatureTest extends PhpseclibTestCase
{ {
$message = 'hello, world!'; $message = 'hello, world!';
$dsa = new DSA(); $dsa = PublicKeyLoader::load('-----BEGIN DSA PRIVATE KEY-----
$dsa->load('-----BEGIN DSA PRIVATE KEY-----
MIIBvAIBAAKBgQDsGAHAM16bsPlwl7jaec4QMynYa0YLiLiOZC4mvH4UW/tRJxTz MIIBvAIBAAKBgQDsGAHAM16bsPlwl7jaec4QMynYa0YLiLiOZC4mvH4UW/tRJxTz
aV7eH1EtnP9D9J78x/07wKYs8zJEWCXmuq0UluQfjA47+pb68b/ucQTNeZHboNN9 aV7eH1EtnP9D9J78x/07wKYs8zJEWCXmuq0UluQfjA47+pb68b/ucQTNeZHboNN9
5oEi+8BCSK0y8G3uf3Y89qHvqa9Si6rP374MinEMrbVFm+UpsGflFcd83wIVALtJ 5oEi+8BCSK0y8G3uf3Y89qHvqa9Si6rP374MinEMrbVFm+UpsGflFcd83wIVALtJ
@ -27,10 +26,11 @@ CCBGBQJRAoGBALnHTAZlpoLJZuSBVtnMuRM3cSX43IkE9w9FveDV1jX5mmfK7yBV
pQFV8eVJfk91ERQ4Dn6ePLUv2dRIt4a0S0qHqadgzyoFyqkmmUi1kNLyixtRqh+m pQFV8eVJfk91ERQ4Dn6ePLUv2dRIt4a0S0qHqadgzyoFyqkmmUi1kNLyixtRqh+m
2gXx0t63HEpZDbEPppdpnlppZquVQh7TyrKSXW9MTzUkQjFI9UY7kZeKAhQXiJgI 2gXx0t63HEpZDbEPppdpnlppZquVQh7TyrKSXW9MTzUkQjFI9UY7kZeKAhQXiJgI
kBniZHdFBAZBTE14YJUBkw== kBniZHdFBAZBTE14YJUBkw==
-----END DSA PRIVATE KEY-----'); -----END DSA PRIVATE KEY-----')
$signature = $dsa->sign($message, 'ASN1'); ->withSignatureFormat('ASN1');
$signature = $dsa->sign($message);
$dsa->load('-----BEGIN PUBLIC KEY----- $dsa = PublicKeyLoader::load('-----BEGIN PUBLIC KEY-----
MIIBuDCCASwGByqGSM44BAEwggEfAoGBAOwYAcAzXpuw+XCXuNp5zhAzKdhrRguI MIIBuDCCASwGByqGSM44BAEwggEfAoGBAOwYAcAzXpuw+XCXuNp5zhAzKdhrRguI
uI5kLia8fhRb+1EnFPNpXt4fUS2c/0P0nvzH/TvApizzMkRYJea6rRSW5B+MDjv6 uI5kLia8fhRb+1EnFPNpXt4fUS2c/0P0nvzH/TvApizzMkRYJea6rRSW5B+MDjv6
lvrxv+5xBM15kdug033mgSL7wEJIrTLwbe5/djz2oe+pr1KLqs/fvgyKcQyttUWb lvrxv+5xBM15kdug033mgSL7wEJIrTLwbe5/djz2oe+pr1KLqs/fvgyKcQyttUWb
@ -41,37 +41,36 @@ jhGOrO+kJcZBxUSxINgIIEYFAlEDgYUAAoGBALnHTAZlpoLJZuSBVtnMuRM3cSX4
3IkE9w9FveDV1jX5mmfK7yBVpQFV8eVJfk91ERQ4Dn6ePLUv2dRIt4a0S0qHqadg 3IkE9w9FveDV1jX5mmfK7yBVpQFV8eVJfk91ERQ4Dn6ePLUv2dRIt4a0S0qHqadg
zyoFyqkmmUi1kNLyixtRqh+m2gXx0t63HEpZDbEPppdpnlppZquVQh7TyrKSXW9M zyoFyqkmmUi1kNLyixtRqh+m2gXx0t63HEpZDbEPppdpnlppZquVQh7TyrKSXW9M
TzUkQjFI9UY7kZeK TzUkQjFI9UY7kZeK
-----END PUBLIC KEY-----'); -----END PUBLIC KEY-----')
->withSignatureFormat('ASN1');
$this->assertTrue($dsa->verify($message, $signature, 'ASN1')); $this->assertTrue($dsa->verify($message, $signature));
$this->assertFalse($dsa->verify('foozbar', $signature, 'ASN1')); $this->assertFalse($dsa->verify('foozbar', $signature));
// openssl dgst -dss1 -sign dsa_priv.pem foo.txt > sigfile.bin // openssl dgst -dss1 -sign dsa_priv.pem foo.txt > sigfile.bin
$signature = '302c021456d7e7da10d1538a6cd45dcb2b0ce15c28bac03402147e973a4de1e92e8a87ed5218c797952a3f854df5'; $signature = '302c021456d7e7da10d1538a6cd45dcb2b0ce15c28bac03402147e973a4de1e92e8a87ed5218c797952a3f854df5';
$signature = pack('H*', $signature); $signature = pack('H*', $signature);
$dsa->setHash('sha1'); $dsa = $dsa->withHash('sha1');
$this->assertTrue($dsa->verify("foobar\n", $signature, 'ASN1')); $this->assertTrue($dsa->verify("foobar\n", $signature));
$this->assertFalse($dsa->verify('foozbar', $signature, 'ASN1')); $this->assertFalse($dsa->verify('foozbar', $signature));
// openssl dgst -sha256 -sign dsa_priv.pem foo.txt > sigfile.bin // openssl dgst -sha256 -sign dsa_priv.pem foo.txt > sigfile.bin
$signature = '302e021500b131ec2682c4c0be13e6558ba3d64929ebc0ac420215009946300a03561cef50c0a51d0cd0a2c835e798fc'; $signature = '302e021500b131ec2682c4c0be13e6558ba3d64929ebc0ac420215009946300a03561cef50c0a51d0cd0a2c835e798fc';
$signature = pack('H*', $signature); $signature = pack('H*', $signature);
$dsa->setHash('sha256'); $dsa = $dsa->withHash('sha256');
$this->assertTrue($dsa->verify('abcdefghijklmnopqrstuvwxyz', $signature, 'ASN1')); $this->assertTrue($dsa->verify('abcdefghijklmnopqrstuvwxyz', $signature));
$this->assertFalse($dsa->verify('zzzz', $signature, 'ASN1')); $this->assertFalse($dsa->verify('zzzz', $signature));
} }
public function testRandomSignature() public function testRandomSignature()
{ {
$message = 'hello, world!'; $message = 'hello, world!';
$dsa = new DSA(); $dsa = PublicKeyLoader::load('-----BEGIN DSA PRIVATE KEY-----
$dsa->load('-----BEGIN DSA PRIVATE KEY-----
MIIBvAIBAAKBgQDsGAHAM16bsPlwl7jaec4QMynYa0YLiLiOZC4mvH4UW/tRJxTz MIIBvAIBAAKBgQDsGAHAM16bsPlwl7jaec4QMynYa0YLiLiOZC4mvH4UW/tRJxTz
aV7eH1EtnP9D9J78x/07wKYs8zJEWCXmuq0UluQfjA47+pb68b/ucQTNeZHboNN9 aV7eH1EtnP9D9J78x/07wKYs8zJEWCXmuq0UluQfjA47+pb68b/ucQTNeZHboNN9
5oEi+8BCSK0y8G3uf3Y89qHvqa9Si6rP374MinEMrbVFm+UpsGflFcd83wIVALtJ 5oEi+8BCSK0y8G3uf3Y89qHvqa9Si6rP374MinEMrbVFm+UpsGflFcd83wIVALtJ
@ -82,9 +81,11 @@ CCBGBQJRAoGBALnHTAZlpoLJZuSBVtnMuRM3cSX43IkE9w9FveDV1jX5mmfK7yBV
pQFV8eVJfk91ERQ4Dn6ePLUv2dRIt4a0S0qHqadgzyoFyqkmmUi1kNLyixtRqh+m pQFV8eVJfk91ERQ4Dn6ePLUv2dRIt4a0S0qHqadgzyoFyqkmmUi1kNLyixtRqh+m
2gXx0t63HEpZDbEPppdpnlppZquVQh7TyrKSXW9MTzUkQjFI9UY7kZeKAhQXiJgI 2gXx0t63HEpZDbEPppdpnlppZquVQh7TyrKSXW9MTzUkQjFI9UY7kZeKAhQXiJgI
kBniZHdFBAZBTE14YJUBkw== kBniZHdFBAZBTE14YJUBkw==
-----END DSA PRIVATE KEY-----'); -----END DSA PRIVATE KEY-----')
$signature1 = $dsa->sign($message, 'ASN1'); ->withSignatureFormat('ASN1');
$signature2 = $dsa->sign($message, 'ASN1'); $public = $dsa->getPublicKey();
$signature1 = $dsa->sign($message);
$signature2 = $dsa->sign($message);
// phpseclib's DSA implementation uses a CSPRNG to generate the k parameter. // phpseclib's DSA implementation uses a CSPRNG to generate the k parameter.
// used correctly this should result in different signatures every time. // used correctly this should result in different signatures every time.
@ -93,31 +94,31 @@ kBniZHdFBAZBTE14YJUBkw==
// unit test would need to be updated // unit test would need to be updated
$this->assertNotEquals($signature1, $signature2); $this->assertNotEquals($signature1, $signature2);
$this->assertTrue($dsa->verify($message, $signature1, 'ASN1')); $this->assertTrue($public->verify($message, $signature1));
$this->assertTrue($dsa->verify($message, $signature2, 'ASN1')); $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(); $this->assertTrue($public->verify($message, $signature));
$dsa->load($pubKey);
$this->assertTrue($dsa->verify($message, $signature, 'SSH2'));
} }
public function testSSHSignature() public function testSSHSignature()
{ {
$dsa = new DSA(); $dsa = PublicKeyLoader::load('AAAAB3NzaC1kc3MAAACBAPyzZzm4oqmY12lxmHwNcfYDNyXr38M1lU6xy9I792U1YSKgX27nUW9eXdJ8Mrn63Le5rrBRfg2Niycx' .
$dsa->setHash('sha1');
$dsa->load('AAAAB3NzaC1kc3MAAACBAPyzZzm4oqmY12lxmHwNcfYDNyXr38M1lU6xy9I792U1YSKgX27nUW9eXdJ8Mrn63Le5rrBRfg2Niycx' .
'JF2IwDpwCi7YpIv79uwT3RtA0chQDS4vx8qi8BWBzy7PZC9hmqY62+mgfj8ooga1sr+JpMh+8r4j3KjPM+wE37khkgkvAAAAFQDn' . 'JF2IwDpwCi7YpIv79uwT3RtA0chQDS4vx8qi8BWBzy7PZC9hmqY62+mgfj8ooga1sr+JpMh+8r4j3KjPM+wE37khkgkvAAAAFQDn' .
'19pBng6TajI/vdg7GPnxsitCqQAAAIEA6Pl1Z/TVdkc+HpfkAvcg2Q+yNtnVq7+26RCbRDO3b9Ocr+tZA9u23qnO3KDYeygzaLnI' . '19pBng6TajI/vdg7GPnxsitCqQAAAIEA6Pl1Z/TVdkc+HpfkAvcg2Q+yNtnVq7+26RCbRDO3b9Ocr+tZA9u23qnO3KDYeygzaLnI' .
'gpErp61Bj70iIUldhXy2LFGZFEC9XiKmt/tQxSDKiBbj3bS3wKfHrAlElgjhqxiRh+GixgSsmCj96eJFXcsxPjQU81HR+WJ0ALV1' . 'gpErp61Bj70iIUldhXy2LFGZFEC9XiKmt/tQxSDKiBbj3bS3wKfHrAlElgjhqxiRh+GixgSsmCj96eJFXcsxPjQU81HR+WJ0ALV1' .
'UnMAAACABRdNuqqe1Y68es8TIflV71P0J7Ci2BbbqAXRwYYKc9/7DrygwaN2UIbMXyOLuojeZgQPPoM9nkzd6QZo8M9apawVKKwD' . 'UnMAAACABRdNuqqe1Y68es8TIflV71P0J7Ci2BbbqAXRwYYKc9/7DrygwaN2UIbMXyOLuojeZgQPPoM9nkzd6QZo8M9apawVKKwD' .
'GAUj2of+F9WVRxhE0ohTQBzD/3HqT80pQsX+rYcxuSx1cCtdMp4oLrrfKO2J4EiWUkaoSB7SdCaj+vU='); 'GAUj2of+F9WVRxhE0ohTQBzD/3HqT80pQsX+rYcxuSx1cCtdMp4oLrrfKO2J4EiWUkaoSB7SdCaj+vU=');
$dsa = $dsa
->withHash('sha1')
->withSignatureFormat('SSH2');
$message = pack('H*', '8bfc69a222c12ddf6bc6bf33c9cadc106af04feb'); $message = pack('H*', '8bfc69a222c12ddf6bc6bf33c9cadc106af04feb');
$signature = pack('H*', '000000077373682d64737300000028a7a2e55dc43e5e6145aa94daa0552ea479d1139d6d6ba50650b489e24e976593e73f76557813d6bc'); $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\File\ASN1;
use phpseclib\Crypt\ECDSA\Curves\Ed448; use phpseclib\Crypt\ECDSA\Curves\Ed448;
use phpseclib\Math\BigInteger; use phpseclib\Math\BigInteger;
use phpseclib\Crypt\PublicKeyLoader;
class Ed448PublicKey class Ed448PublicKey
{ {
@ -167,7 +168,8 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$plaintext = 'zzz'; $plaintext = 'zzz';
ECDSA::useInternalEngine(); ECDSA::useInternalEngine();
extract(ECDSA::createKey($name)); $privatekey = ECDSA::createKey($name);
$publickey = $privatekey->getPublicKey();
$sig = $privatekey->sign($plaintext); $sig = $privatekey->sign($plaintext);
ECDSA::useBestEngine(); ECDSA::useBestEngine();
@ -189,7 +191,8 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$plaintext = 'zzz'; $plaintext = 'zzz';
ECDSA::useBestEngine(); ECDSA::useBestEngine();
extract(ECDSA::createKey($name)); $privatekey = ECDSA::createKey($name);
$publickey = $privatekey->getPublicKey();
$sig = $privatekey->sign($plaintext); $sig = $privatekey->sign($plaintext);
ECDSA::useInternalEngine(); ECDSA::useInternalEngine();
@ -207,11 +210,8 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', '6c82a562cb808d10d632be89c8513ebf6c929f34ddfa8c9f63c9960ef6e348a3528c8a3fcc2f044e39a3fc5b94492f8f032e7549a20098f95b'); $private = pack('H*', '6c82a562cb808d10d632be89c8513ebf6c929f34ddfa8c9f63c9960ef6e348a3528c8a3fcc2f044e39a3fc5b94492f8f032e7549a20098f95b');
$public = pack('H*', '5fd7449b59b461fd2ce787ec616ad46a1da1342485a70e1f8a0ea75d80e96778edf124769b46c7061bd6783df1e50f6cd1fa1abeafe8256180'); $public = pack('H*', '5fd7449b59b461fd2ce787ec616ad46a1da1342485a70e1f8a0ea75d80e96778edf124769b46c7061bd6783df1e50f6cd1fa1abeafe8256180');
$privateKey = new ECDSA(); $privateKey = PublicKeyLoader::load($private);
$privateKey->load($private); $publicKey = PublicKeyLoader::load($public);
$publicKey = new ECDSA();
$publicKey->load($public);
$expected = '533a37f6bbe457251f023c0d88f976ae2dfb504a843e34d2074fd823d41a591f2b233f034f628281f2fd7a22ddd47d7828c59bd0a21bfd3980' . $expected = '533a37f6bbe457251f023c0d88f976ae2dfb504a843e34d2074fd823d41a591f2b233f034f628281f2fd7a22ddd47d7828c59bd0a21bfd3980' .
'ff0d2028d4b18a9df63e006c5d1c2d345b925d8dc00b4104852db99ac5c7cdda8530a113a0f4dbb61149f05a7363268c71d95808ff2e652600'; 'ff0d2028d4b18a9df63e006c5d1c2d345b925d8dc00b4104852db99ac5c7cdda8530a113a0f4dbb61149f05a7363268c71d95808ff2e652600';
@ -221,19 +221,16 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', 'c4eab05d357007c632f3dbb48489924d552b08fe0c353a0d4a1f00acda2c463afbea67c5e8d2877c5e3bc397a659949ef8021e954e0a12274e'); $private = pack('H*', 'c4eab05d357007c632f3dbb48489924d552b08fe0c353a0d4a1f00acda2c463afbea67c5e8d2877c5e3bc397a659949ef8021e954e0a12274e');
$public = pack('H*', '43ba28f430cdff456ae531545f7ecd0ac834a55d9358c0372bfa0c6c6798c0866aea01eb00742802b8438ea4cb82169c235160627b4c3a9480'); $public = pack('H*', '43ba28f430cdff456ae531545f7ecd0ac834a55d9358c0372bfa0c6c6798c0866aea01eb00742802b8438ea4cb82169c235160627b4c3a9480');
$privateKey = new ECDSA(); $privateKey = PublicKeyLoader::load($private);
$privateKey->load($private); $publicKey = PublicKeyLoader::load($public);
$publicKey = new ECDSA();
$publicKey->load($public);
$expected = '26b8f91727bd62897af15e41eb43c377efb9c610d48f2335cb0bd0087810f4352541b143c4b981b7e18f62de8ccdf633fc1bf037ab7cd77980' . $expected = '26b8f91727bd62897af15e41eb43c377efb9c610d48f2335cb0bd0087810f4352541b143c4b981b7e18f62de8ccdf633fc1bf037ab7cd77980' .
'5e0dbcc0aae1cbcee1afb2e027df36bc04dcecbf154336c19f0af7e0a6472905e799f1953d2a0ff3348ab21aa4adafd1d234441cf807c03a00'; '5e0dbcc0aae1cbcee1afb2e027df36bc04dcecbf154336c19f0af7e0a6472905e799f1953d2a0ff3348ab21aa4adafd1d234441cf807c03a00';
$this->assertSame($expected, bin2hex($sig = $privateKey->sign("\x03"))); $this->assertSame($expected, bin2hex($sig = $privateKey->sign("\x03")));
$this->assertTrue($publicKey->verify("\x03", $sig)); $this->assertTrue($publicKey->verify("\x03", $sig));
$publicKey->setContext(pack('H*', '666f6f')); $publicKey = $publicKey->withContext(pack('H*', '666f6f'));
$privateKey->setContext(pack('H*', '666f6f')); $privateKey = $privateKey->withContext(pack('H*', '666f6f'));
$expected = 'd4f8f6131770dd46f40867d6fd5d5055de43541f8c5e35abbcd001b32a89f7d2151f7647f11d8ca2ae279fb842d607217fce6e042f6815ea00' . $expected = 'd4f8f6131770dd46f40867d6fd5d5055de43541f8c5e35abbcd001b32a89f7d2151f7647f11d8ca2ae279fb842d607217fce6e042f6815ea00' .
'0c85741de5c8da1144a6a1aba7f96de42505d7a7298524fda538fccbbb754f578c1cad10d54d0d5428407e85dcbc98a49155c13764e66c3c00'; '0c85741de5c8da1144a6a1aba7f96de42505d7a7298524fda538fccbbb754f578c1cad10d54d0d5428407e85dcbc98a49155c13764e66c3c00';
@ -243,11 +240,8 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', '258cdd4ada32ed9c9ff54e63756ae582fb8fab2ac721f2c8e676a72768513d939f63dddb55609133f29adf86ec9929dccb52c1c5fd2ff7e21b'); $private = pack('H*', '258cdd4ada32ed9c9ff54e63756ae582fb8fab2ac721f2c8e676a72768513d939f63dddb55609133f29adf86ec9929dccb52c1c5fd2ff7e21b');
$public = pack('H*', '3ba16da0c6f2cc1f30187740756f5e798d6bc5fc015d7c63cc9510ee3fd44adc24d8e968b6e46e6f94d19b945361726bd75e149ef09817f580'); $public = pack('H*', '3ba16da0c6f2cc1f30187740756f5e798d6bc5fc015d7c63cc9510ee3fd44adc24d8e968b6e46e6f94d19b945361726bd75e149ef09817f580');
$privateKey = new ECDSA(); $privateKey = PublicKeyLoader::load($private);
$privateKey->load($private); $publicKey = PublicKeyLoader::load($public);
$publicKey = new ECDSA();
$publicKey->load($public);
$message = pack('H*', '64a65f3cdedcdd66811e2915'); $message = pack('H*', '64a65f3cdedcdd66811e2915');
@ -259,11 +253,8 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', '7ef4e84544236752fbb56b8f31a23a10e42814f5f55ca037cdcc11c64c9a3b2949c1bb60700314611732a6c2fea98eebc0266a11a93970100e'); $private = pack('H*', '7ef4e84544236752fbb56b8f31a23a10e42814f5f55ca037cdcc11c64c9a3b2949c1bb60700314611732a6c2fea98eebc0266a11a93970100e');
$public = pack('H*', 'b3da079b0aa493a5772029f0467baebee5a8112d9d3a22532361da294f7bb3815c5dc59e176b4d9f381ca0938e13c6c07b174be65dfa578e80'); $public = pack('H*', 'b3da079b0aa493a5772029f0467baebee5a8112d9d3a22532361da294f7bb3815c5dc59e176b4d9f381ca0938e13c6c07b174be65dfa578e80');
$privateKey = new ECDSA(); $privateKey = PublicKeyLoader::load($private);
$privateKey->load($private); $publicKey = PublicKeyLoader::load($public);
$publicKey = new ECDSA();
$publicKey->load($public);
$message = pack('H*', '64a65f3cdedcdd66811e2915e7'); $message = pack('H*', '64a65f3cdedcdd66811e2915e7');
@ -275,11 +266,8 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', 'd65df341ad13e008567688baedda8e9dcdc17dc024974ea5b4227b6530e339bff21f99e68ca6968f3cca6dfe0fb9f4fab4fa135d5542ea3f01'); $private = pack('H*', 'd65df341ad13e008567688baedda8e9dcdc17dc024974ea5b4227b6530e339bff21f99e68ca6968f3cca6dfe0fb9f4fab4fa135d5542ea3f01');
$public = pack('H*', 'df9705f58edbab802c7f8363cfe5560ab1c6132c20a9f1dd163483a26f8ac53a39d6808bf4a1dfbd261b099bb03b3fb50906cb28bd8a081f00'); $public = pack('H*', 'df9705f58edbab802c7f8363cfe5560ab1c6132c20a9f1dd163483a26f8ac53a39d6808bf4a1dfbd261b099bb03b3fb50906cb28bd8a081f00');
$privateKey = new ECDSA(); $privateKey = PublicKeyLoader::load($private);
$privateKey->load($private); $publicKey = PublicKeyLoader::load($public);
$publicKey = new ECDSA();
$publicKey->load($public);
$message = 'bd0f6a3747cd561bdddf4640a332461a4a30a12a434cd0bf40d766d9c6d458e5512204a30c17d1f50b5079631f64eb3112182da3005835461113718d1a5ef944'; $message = 'bd0f6a3747cd561bdddf4640a332461a4a30a12a434cd0bf40d766d9c6d458e5512204a30c17d1f50b5079631f64eb3112182da3005835461113718d1a5ef944';
$message = pack('H*', $message); $message = pack('H*', $message);
@ -292,11 +280,8 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', '2ec5fe3c17045abdb136a5e6a913e32ab75ae68b53d2fc149b77e504132d37569b7e766ba74a19bd6162343a21c8590aa9cebca9014c636df5'); $private = pack('H*', '2ec5fe3c17045abdb136a5e6a913e32ab75ae68b53d2fc149b77e504132d37569b7e766ba74a19bd6162343a21c8590aa9cebca9014c636df5');
$public = pack('H*', '79756f014dcfe2079f5dd9e718be4171e2ef2486a08f25186f6bff43a9936b9bfe12402b08ae65798a3d81e22e9ec80e7690862ef3d4ed3a00'); $public = pack('H*', '79756f014dcfe2079f5dd9e718be4171e2ef2486a08f25186f6bff43a9936b9bfe12402b08ae65798a3d81e22e9ec80e7690862ef3d4ed3a00');
$privateKey = new ECDSA(); $privateKey = PublicKeyLoader::load($private);
$privateKey->load($private); $publicKey = PublicKeyLoader::load($public);
$publicKey = new ECDSA();
$publicKey->load($public);
$message = '15777532b0bdd0d1389f636c5f6b9ba734c90af572877e2d272dd078aa1e567cfa80e12928bb542330e8409f3174504107ecd5efac61ae7504dabe2a602ede89e5cca6257a7c77e27a702b3ae39fc769fc54f2395ae6a1178cab4738e543072fc1c177fe71e92e25bf03e4ecb72f47b64d0465aaea4c7fad372536c8ba516a60' . $message = '15777532b0bdd0d1389f636c5f6b9ba734c90af572877e2d272dd078aa1e567cfa80e12928bb542330e8409f3174504107ecd5efac61ae7504dabe2a602ede89e5cca6257a7c77e27a702b3ae39fc769fc54f2395ae6a1178cab4738e543072fc1c177fe71e92e25bf03e4ecb72f47b64d0465aaea4c7fad372536c8ba516a60' .
'39c3c2a39f0e4d832be432dfa9a706a6e5c7e19f397964ca4258002f7c0541b590316dbc5622b6b2a6fe7a4abffd96105eca76ea7b98816af0748c10df048ce012d901015a51f189f3888145c03650aa23ce894c3bd889e030d565071c59f409a9981b51878fd6fc110624dcbcde0bf7a69ccce38fabdf86f3bef6044819de11'; '39c3c2a39f0e4d832be432dfa9a706a6e5c7e19f397964ca4258002f7c0541b590316dbc5622b6b2a6fe7a4abffd96105eca76ea7b98816af0748c10df048ce012d901015a51f189f3888145c03650aa23ce894c3bd889e030d565071c59f409a9981b51878fd6fc110624dcbcde0bf7a69ccce38fabdf86f3bef6044819de11';
@ -310,11 +295,8 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', '872d093780f5d3730df7c212664b37b8a0f24f56810daa8382cd4fa3f77634ec44dc54f1c2ed9bea86fafb7632d8be199ea165f5ad55dd9ce8'); $private = pack('H*', '872d093780f5d3730df7c212664b37b8a0f24f56810daa8382cd4fa3f77634ec44dc54f1c2ed9bea86fafb7632d8be199ea165f5ad55dd9ce8');
$public = pack('H*', 'a81b2e8a70a5ac94ffdbcc9badfc3feb0801f258578bb114ad44ece1ec0e799da08effb81c5d685c0c56f64eecaef8cdf11cc38737838cf400'); $public = pack('H*', 'a81b2e8a70a5ac94ffdbcc9badfc3feb0801f258578bb114ad44ece1ec0e799da08effb81c5d685c0c56f64eecaef8cdf11cc38737838cf400');
$privateKey = new ECDSA(); $privateKey = PublicKeyLoader::load($private);
$privateKey->load($private); $publicKey = PublicKeyLoader::load($public);
$publicKey = new ECDSA();
$publicKey->load($public);
$message = '6ddf802e1aae4986935f7f981ba3f0351d6273c0a0c22c9c0e8339168e675412a3debfaf435ed651558007db4384b650fcc07e3b586a27a4f7a00ac8a6fec2cd86ae4bf1570c41e6a40c931db27b2faa15a8cedd52cff7362c4e6e23daec0fbc3a79b6806e316efcc7b68119bf46bc76a26067a53f296dafdbdc11c77f7777e9' . $message = '6ddf802e1aae4986935f7f981ba3f0351d6273c0a0c22c9c0e8339168e675412a3debfaf435ed651558007db4384b650fcc07e3b586a27a4f7a00ac8a6fec2cd86ae4bf1570c41e6a40c931db27b2faa15a8cedd52cff7362c4e6e23daec0fbc3a79b6806e316efcc7b68119bf46bc76a26067a53f296dafdbdc11c77f7777e9' .
'72660cf4b6a9b369a6665f02e0cc9b6edfad136b4fabe723d2813db3136cfde9b6d044322fee2947952e031b73ab5c603349b307bdc27bc6cb8b8bbd7bd323219b8033a581b59eadebb09b3c4f3d2277d4f0343624acc817804728b25ab797172b4c5c21a22f9c7839d64300232eb66e53f31c723fa37fe387c7d3e50bdf9813' . '72660cf4b6a9b369a6665f02e0cc9b6edfad136b4fabe723d2813db3136cfde9b6d044322fee2947952e031b73ab5c603349b307bdc27bc6cb8b8bbd7bd323219b8033a581b59eadebb09b3c4f3d2277d4f0343624acc817804728b25ab797172b4c5c21a22f9c7839d64300232eb66e53f31c723fa37fe387c7d3e50bdf9813' .
@ -342,12 +324,8 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', '9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60'); $private = pack('H*', '9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60');
$public = pack('H*', 'd75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a'); $public = pack('H*', 'd75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a');
$privateKey = new ECDSA(); $privateKey = PublicKeyLoader::load($private . $public); // libsodium format
// libsodium format $publicKey = PublicKeyLoader::load($public);
$privateKey->load($private . $public);
$publicKey = new ECDSA();
$publicKey->load($public);
$expected = 'e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e06522490155' . $expected = 'e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e06522490155' .
'5fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b'; '5fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b';
@ -357,11 +335,8 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', '4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb'); $private = pack('H*', '4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb');
$public = pack('H*', '3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c'); $public = pack('H*', '3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c');
$privateKey = new ECDSA(); $privateKey = PublicKeyLoader::load($private . $public);
$privateKey->load($private . $public); $publicKey = PublicKeyLoader::load($public);
$publicKey = new ECDSA();
$publicKey->load($public);
$expected = '92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da' . $expected = '92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da' .
'085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00'; '085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00';
@ -371,11 +346,8 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', 'c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7'); $private = pack('H*', 'c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7');
$public = pack('H*', 'fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025'); $public = pack('H*', 'fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025');
$privateKey = new ECDSA(); $privateKey = PublicKeyLoader::load($private . $public); // libsodium format
$privateKey->load($private . $public); $publicKey = PublicKeyLoader::load($public);
$publicKey = new ECDSA();
$publicKey->load($public);
$expected = '6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac' . $expected = '6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac' .
'18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a'; '18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a';
@ -385,11 +357,8 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', 'f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5'); $private = pack('H*', 'f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5');
$public = pack('H*', '278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e'); $public = pack('H*', '278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e');
$privateKey = new ECDSA(); $privateKey = PublicKeyLoader::load($private . $public); // libsodium format
$privateKey->load($private . $public); $publicKey = PublicKeyLoader::load($public);
$publicKey = new ECDSA();
$publicKey->load($public);
$message = '08b8b2b733424243760fe426a4b54908632110a66c2f6591eabd3345e3e4eb98' . $message = '08b8b2b733424243760fe426a4b54908632110a66c2f6591eabd3345e3e4eb98' .
'fa6e264bf09efe12ee50f8f54e9f77b1e355f6c50544e23fb1433ddf73be84d8' . 'fa6e264bf09efe12ee50f8f54e9f77b1e355f6c50544e23fb1433ddf73be84d8' .
@ -433,11 +402,8 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', '833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42'); $private = pack('H*', '833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42');
$public = pack('H*', 'ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf'); $public = pack('H*', 'ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf');
$privateKey = new ECDSA(); $privateKey = PublicKeyLoader::load($private . $public);
$privateKey->load($private . $public); $publicKey = PublicKeyLoader::load($public);
$publicKey = new ECDSA();
$publicKey->load($public);
$message = 'ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a' . $message = 'ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a' .
'2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f'; '2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f';
@ -451,14 +417,11 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', '0305334e381af78f141cb666f6199f57bc3495335a256a95bd2a55bf546663f6'); $private = pack('H*', '0305334e381af78f141cb666f6199f57bc3495335a256a95bd2a55bf546663f6');
$public = pack('H*', 'dfc9425e4f968f7f0c29f0259cf5f9aed6851c2bb4ad8bfb860cfee0ab248292'); $public = pack('H*', 'dfc9425e4f968f7f0c29f0259cf5f9aed6851c2bb4ad8bfb860cfee0ab248292');
$privateKey = new ECDSA(); $privateKey = PublicKeyLoader::load($private . $public);
$privateKey->load($private . $public); $publicKey = PublicKeyLoader::load($public);
$publicKey = new ECDSA(); $privateKey = $privateKey->withContext("\x62\x61\x72");
$publicKey->load($public); $publicKey = $publicKey->withContext("\x62\x61\x72");
$privateKey->setContext("\x62\x61\x72");
$publicKey->setContext("\x62\x61\x72");
$message = 'f726936d19c800494e3fdaff20b276a8'; $message = 'f726936d19c800494e3fdaff20b276a8';
$message = pack('H*', $message); $message = pack('H*', $message);
@ -471,14 +434,11 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', '0305334e381af78f141cb666f6199f57bc3495335a256a95bd2a55bf546663f6'); $private = pack('H*', '0305334e381af78f141cb666f6199f57bc3495335a256a95bd2a55bf546663f6');
$public = pack('H*', 'dfc9425e4f968f7f0c29f0259cf5f9aed6851c2bb4ad8bfb860cfee0ab248292'); $public = pack('H*', 'dfc9425e4f968f7f0c29f0259cf5f9aed6851c2bb4ad8bfb860cfee0ab248292');
$privateKey = new ECDSA(); $privateKey = PublicKeyLoader::load($private . $public);
$privateKey->load($private . $public); $publicKey = PublicKeyLoader::load($public);
$publicKey = new ECDSA(); $privateKey = $privateKey->withContext("\x66\x6f\x6f");
$publicKey->load($public); $publicKey = $publicKey->withContext("\x66\x6f\x6f");
$privateKey->setContext("\x66\x6f\x6f");
$publicKey->setContext("\x66\x6f\x6f");
$message = '508e9e6882b979fea900f62adceaca35'; $message = '508e9e6882b979fea900f62adceaca35';
$message = pack('H*', $message); $message = pack('H*', $message);
@ -491,14 +451,11 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
$private = pack('H*', 'ab9c2853ce297ddab85c993b3ae14bcad39b2c682beabc27d6d4eb20711d6560'); $private = pack('H*', 'ab9c2853ce297ddab85c993b3ae14bcad39b2c682beabc27d6d4eb20711d6560');
$public = pack('H*', '0f1d1274943b91415889152e893d80e93275a1fc0b65fd71b4b0dda10ad7d772'); $public = pack('H*', '0f1d1274943b91415889152e893d80e93275a1fc0b65fd71b4b0dda10ad7d772');
$privateKey = new ECDSA(); $privateKey = PublicKeyLoader::load($private . $public);
$privateKey->load($private . $public); $publicKey = PublicKeyLoader::load($public);
$publicKey = new ECDSA(); $privateKey = $privateKey->withContext("\x66\x6f\x6f");
$publicKey->load($public); $publicKey = $publicKey->withContext("\x66\x6f\x6f");
$privateKey->setContext("\x66\x6f\x6f");
$publicKey->setContext("\x66\x6f\x6f");
$message = 'f726936d19c800494e3fdaff20b276a8'; $message = 'f726936d19c800494e3fdaff20b276a8';
$message = pack('H*', $message); $message = pack('H*', $message);
@ -512,8 +469,7 @@ class Unit_Crypt_ECDSA_CurveTest extends PhpseclibTestCase
public function testRandomSignature() public function testRandomSignature()
{ {
$message = 'hello, world!'; $message = 'hello, world!';
$private = new ECDSA(); $private = PublicKeyLoader::load('PuTTY-User-Key-File-2: ecdsa-sha2-nistp256
$private->load('PuTTY-User-Key-File-2: ecdsa-sha2-nistp256
Encryption: none Encryption: none
Comment: ecdsa-key-20181105 Comment: ecdsa-key-20181105
Public-Lines: 3 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\PuTTY;
use phpseclib\Crypt\ECDSA\Keys\OpenSSH; use phpseclib\Crypt\ECDSA\Keys\OpenSSH;
use phpseclib\Crypt\ECDSA\Keys\XML; use phpseclib\Crypt\ECDSA\Keys\XML;
use phpseclib\Crypt\PublicKeyLoader;
class Unit_Crypt_ECDSA_LoadKeyTest extends PhpseclibTestCase class Unit_Crypt_ECDSA_LoadKeyTest extends PhpseclibTestCase
{ {
// openssl ecparam -name secp256k1 -genkey -noout -out secp256k1.pem // openssl ecparam -name secp256k1 -genkey -noout -out secp256k1.pem
public function testPKCS1PrivateKey() public function testPKCS1PrivateKey()
{ {
$key = new ECDSA; $key = PublicKeyLoader::load($expected = '-----BEGIN EC PRIVATE KEY-----
$key->load($expected = '-----BEGIN EC PRIVATE KEY-----
MHQCAQEEIEzUawcXqUsQhaEQ51JLeOIY0ddzlO2nNgwDk32ETqwkoAcGBSuBBAAK MHQCAQEEIEzUawcXqUsQhaEQ51JLeOIY0ddzlO2nNgwDk32ETqwkoAcGBSuBBAAK
oUQDQgAEFuVcVb9iCUhg2cknHPE+BouHGhQ39ORjMaMI3T4RfRxr6dj5HAXdEqVZ oUQDQgAEFuVcVb9iCUhg2cknHPE+BouHGhQ39ORjMaMI3T4RfRxr6dj5HAXdEqVZ
1W94KMe30ndmTndcJ8BPeT1Dd15FdQ== 1W94KMe30ndmTndcJ8BPeT1Dd15FdQ==
-----END EC PRIVATE KEY-----'); -----END EC PRIVATE KEY-----');
$this->assertSame('secp256k1', $key->getCurve()); $this->assertSame('secp256k1', $key->getCurve());
//PKCS1::useNamedCurve(); //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 // openssl ecparam -name secp256k1 -genkey -noout -out secp256k1.pem -param_enc explicit
public function testPKCS1PrivateKeySpecifiedCurve() public function testPKCS1PrivateKeySpecifiedCurve()
{ {
$key = new ECDSA; $key = PublicKeyLoader::load('-----BEGIN EC PRIVATE KEY-----
$key->load('-----BEGIN EC PRIVATE KEY-----
MIIBEwIBAQQgFr6TF5meGfgCXDqVxoSEltGI+T94G42PPbA6/ibq+ouggaUwgaIC MIIBEwIBAQQgFr6TF5meGfgCXDqVxoSEltGI+T94G42PPbA6/ibq+ouggaUwgaIC
AQEwLAYHKoZIzj0BAQIhAP////////////////////////////////////7///wv AQEwLAYHKoZIzj0BAQIhAP////////////////////////////////////7///wv
MAYEAQAEAQcEQQR5vmZ++dy7rFWgYpXOhwsHApv82y3OKNlZ8oFbFvgXmEg62ncm MAYEAQAEAQcEQQR5vmZ++dy7rFWgYpXOhwsHApv82y3OKNlZ8oFbFvgXmEg62ncm
@ -61,29 +60,27 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAABwRBBHm+Zn753LusVaBilc6HCwcCm/zbLc4o
E5w= E5w=
-----END EC PRIVATE KEY-----'; -----END EC PRIVATE KEY-----';
PKCS1::useSpecifiedCurve(); PKCS1::useSpecifiedCurve();
$this->assertSame($expected, $key->getPrivateKey('PKCS1')); $this->assertSame($expected, $key->toString('PKCS1'));
} }
// openssl ecparam -name secp256k1 -genkey -noout -out secp256k1.pem // openssl ecparam -name secp256k1 -genkey -noout -out secp256k1.pem
// openssl pkcs8 -topk8 -nocrypt -in secp256k1.pem -out secp256k1-2.pem // openssl pkcs8 -topk8 -nocrypt -in secp256k1.pem -out secp256k1-2.pem
public function testPKCS8PrivateKey() public function testPKCS8PrivateKey()
{ {
$key = new ECDSA; $key = PublicKeyLoader::load($expected = '-----BEGIN PRIVATE KEY-----
$key->load($expected = '-----BEGIN PRIVATE KEY-----
MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgAYCXwnhqMT6fCIKIkQ0w MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgAYCXwnhqMT6fCIKIkQ0w
cac7QqHrn4TCQMF9a+im74WhRANCAATwCjyGuP8xQbvVjznqazL36oeAnD32I+X2 cac7QqHrn4TCQMF9a+im74WhRANCAATwCjyGuP8xQbvVjznqazL36oeAnD32I+X2
+wscW3OmyTDpk41HaWYPh+j+BoufsSkCwf8dBRGEQbCieZbbZogy +wscW3OmyTDpk41HaWYPh+j+BoufsSkCwf8dBRGEQbCieZbbZogy
-----END PRIVATE KEY-----'); -----END PRIVATE KEY-----');
$this->assertSame('secp256k1', $key->getCurve()); $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 ecparam -name secp256k1 -genkey -noout -out secp256k1.pem -param_enc explicit
// openssl pkcs8 -topk8 -nocrypt -in secp256k1.pem -out secp256k1-2.pem // openssl pkcs8 -topk8 -nocrypt -in secp256k1.pem -out secp256k1-2.pem
public function testPKCS8PrivateKeySpecifiedCurve() public function testPKCS8PrivateKeySpecifiedCurve()
{ {
$key = new ECDSA; $key = PublicKeyLoader::load('-----BEGIN PRIVATE KEY-----
$key->load('-----BEGIN PRIVATE KEY-----
MIIBIwIBADCBrgYHKoZIzj0CATCBogIBATAsBgcqhkjOPQEBAiEA//////////// MIIBIwIBADCBrgYHKoZIzj0CATCBogIBATAsBgcqhkjOPQEBAiEA////////////
/////////////////////////v///C8wBgQBAAQBBwRBBHm+Zn753LusVaBilc6H /////////////////////////v///C8wBgQBAAQBBwRBBHm+Zn753LusVaBilc6H
CwcCm/zbLc4o2VnygVsW+BeYSDradyajxGVdpPv8DhEIqP0XtEimhVQZnEfQj/sQ CwcCm/zbLc4o2VnygVsW+BeYSDradyajxGVdpPv8DhEIqP0XtEimhVQZnEfQj/sQ
@ -107,28 +104,26 @@ AASdfrr5QLNRbdP9+QsYgh9mMmblsgzABXzkukOibaEjjjUlHH79bhaq0a5b4H8s
AFLpken6rN6lOEIeyNLdD097 AFLpken6rN6lOEIeyNLdD097
-----END PRIVATE KEY-----'; -----END PRIVATE KEY-----';
PKCS8::useSpecifiedCurve(); PKCS8::useSpecifiedCurve();
$this->assertSame($expected, $key->getPrivateKey('PKCS8')); $this->assertSame($expected, $key->toString('PKCS8'));
} }
// openssl ecparam -name sect113r1 -genkey -noout -out sect113r1.pem // openssl ecparam -name sect113r1 -genkey -noout -out sect113r1.pem
public function testBinaryPKCS1PrivateKey() public function testBinaryPKCS1PrivateKey()
{ {
$key = new ECDSA; $key = PublicKeyLoader::load($expected = '-----BEGIN EC PRIVATE KEY-----
$key->load($expected = '-----BEGIN EC PRIVATE KEY-----
MEECAQEEDwBZdP4eSzKk/uQa6jdtfKAHBgUrgQQABKEiAyAABAHqCoNb++mK5qvE MEECAQEEDwBZdP4eSzKk/uQa6jdtfKAHBgUrgQQABKEiAyAABAHqCoNb++mK5qvE
c4rCzQEuI19czqvXpEPcAWSXew== c4rCzQEuI19czqvXpEPcAWSXew==
-----END EC PRIVATE KEY-----'); -----END EC PRIVATE KEY-----');
$this->assertSame('sect113r1', $key->getCurve()); $this->assertSame('sect113r1', $key->getCurve());
PKCS1::useNamedCurve(); 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 // openssl ecparam -name sect113r1 -genkey -noout -out sect113r1.pem -param_enc explicit
public function testBinaryPKCS1PrivateKeySpecifiedCurve() public function testBinaryPKCS1PrivateKeySpecifiedCurve()
{ {
$key = new ECDSA; $key = PublicKeyLoader::load('-----BEGIN EC PRIVATE KEY-----
$key->load('-----BEGIN EC PRIVATE KEY-----
MIHNAgEBBA8AuSc4BeeyYTq9rbSDuL2ggZIwgY8CAQEwHAYHKoZIzj0BAjARAgFx MIHNAgEBBA8AuSc4BeeyYTq9rbSDuL2ggZIwgY8CAQEwHAYHKoZIzj0BAjARAgFx
BgkqhkjOPQECAwICAQkwNwQOMIglDKbnx/5knOhYIPcEDui+5NPiJgdEGIvg6ccj BgkqhkjOPQECAwICAQkwNwQOMIglDKbnx/5knOhYIPcEDui+5NPiJgdEGIvg6ccj
AxUAEOcjqxTWluZ2h1YVF1b+v4/LSakEHwQAnXNhbzX0qxQH1zViwQ8ApSgwJ3lY AxUAEOcjqxTWluZ2h1YVF1b+v4/LSakEHwQAnXNhbzX0qxQH1zViwQ8ApSgwJ3lY
@ -149,7 +144,7 @@ BACdc2FvNfSrFAfXNWLBDwClKDAneVjuhNExXtMYhgIPAQAAAAAAAADZzOyKOeVv
oSIDIAAEAULtznTLu7D6K4d4wK1bAKko0FRxV6IeZ7rT0O/+ oSIDIAAEAULtznTLu7D6K4d4wK1bAKko0FRxV6IeZ7rT0O/+
-----END EC PRIVATE KEY-----'; -----END EC PRIVATE KEY-----';
PKCS1::useSpecifiedCurve(); PKCS1::useSpecifiedCurve();
$this->assertSame($expected, $key->getPrivateKey('PKCS1')); $this->assertSame($expected, $key->toString('PKCS1'));
} }
// openssl ecparam -name sect113r1 -genkey -noout -out sect113r1.pem // openssl ecparam -name sect113r1 -genkey -noout -out sect113r1.pem
@ -157,23 +152,21 @@ oSIDIAAEAULtznTLu7D6K4d4wK1bAKko0FRxV6IeZ7rT0O/+
// sect113r1's reduction polynomial is a trinomial // sect113r1's reduction polynomial is a trinomial
public function testBinaryPKCS8PrivateKey() public function testBinaryPKCS8PrivateKey()
{ {
$key = new ECDSA; $key = PublicKeyLoader::load($expected = '-----BEGIN PRIVATE KEY-----
$key->load($expected = '-----BEGIN PRIVATE KEY-----
MFECAQAwEAYHKoZIzj0CAQYFK4EEAAQEOjA4AgEBBA8A5OuqAY8HYoFOaz9mE6mh MFECAQAwEAYHKoZIzj0CAQYFK4EEAAQEOjA4AgEBBA8A5OuqAY8HYoFOaz9mE6mh
IgMgAAQASF3rOTPXvH0QdRBvsrMBdLMf27yd8AWABrZTxvI= IgMgAAQASF3rOTPXvH0QdRBvsrMBdLMf27yd8AWABrZTxvI=
-----END PRIVATE KEY-----'); -----END PRIVATE KEY-----');
$this->assertSame('sect113r1', $key->getCurve()); $this->assertSame('sect113r1', $key->getCurve());
PKCS8::useNamedCurve(); 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 ecparam -name sect113r1 -genkey -noout -out sect113r1.pem -param_enc explicit
// openssl pkcs8 -topk8 -nocrypt -in sect113r1.pem -out sect113r1-2.pem // openssl pkcs8 -topk8 -nocrypt -in sect113r1.pem -out sect113r1-2.pem
public function testBinaryPKCS8PrivateKeySpecifiedCurve() public function testBinaryPKCS8PrivateKeySpecifiedCurve()
{ {
$key = new ECDSA; $key = PublicKeyLoader::load('-----BEGIN PRIVATE KEY-----
$key->load('-----BEGIN PRIVATE KEY-----
MIHdAgEAMIGbBgcqhkjOPQIBMIGPAgEBMBwGByqGSM49AQIwEQIBcQYJKoZIzj0B MIHdAgEAMIGbBgcqhkjOPQIBMIGPAgEBMBwGByqGSM49AQIwEQIBcQYJKoZIzj0B
AgMCAgEJMDcEDjCIJQym58f+ZJzoWCD3BA7ovuTT4iYHRBiL4OnHIwMVABDnI6sU AgMCAgEJMDcEDjCIJQym58f+ZJzoWCD3BA7ovuTT4iYHRBiL4OnHIwMVABDnI6sU
1pbmdodWFRdW/r+Py0mpBB8EAJ1zYW819KsUB9c1YsEPAKUoMCd5WO6E0TFe0xiG 1pbmdodWFRdW/r+Py0mpBB8EAJ1zYW819KsUB9c1YsEPAKUoMCd5WO6E0TFe0xiG
@ -193,15 +186,14 @@ BA8AXtfDMRsRTx8snPbWHquhIgMgAAQA9xdWGJ6vV23+vkdq0C8BLJVg5E3amMyf
/5keGa4= /5keGa4=
-----END PRIVATE KEY-----'; -----END PRIVATE KEY-----';
PKCS8::useSpecifiedCurve(); 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 // openssl ecparam -name sect131r1 -genkey -noout -out sect131r1.pem -param_enc explicit
// sect131r1's reduction polynomial is a pentanomial // sect131r1's reduction polynomial is a pentanomial
public function testBinaryPentanomialPKCS1PrivateKey() public function testBinaryPentanomialPKCS1PrivateKey()
{ {
$key = new ECDSA; $key = PublicKeyLoader::load('-----BEGIN EC PRIVATE KEY-----
$key->load('-----BEGIN EC PRIVATE KEY-----
MIHoAgEBBBECPEK9NCISWf2riBsORoTM+6CBpzCBpAIBATAlBgcqhkjOPQECMBoC MIHoAgEBBBECPEK9NCISWf2riBsORoTM+6CBpzCBpAIBATAlBgcqhkjOPQECMBoC
AgCDBgkqhkjOPQECAwMwCQIBAgIBAwIBCDA9BBEHoRsJp2tWIURBj/P/jCVwuAQR AgCDBgkqhkjOPQECAwMwCQIBAgIBAwIBCDA9BBEHoRsJp2tWIURBj/P/jCVwuAQR
AhfAVhCIS2O5xscpFnj500EDFQBNaW5naHVhUXWYW9OtutohtDqX4gQjBACBuvkf AhfAVhCIS2O5xscpFnj500EDFQBNaW5naHVhUXWYW9OtutohtDqX4gQjBACBuvkf
@ -221,14 +213,13 @@ SxtO+eFQAhEEAAAAAAAAAAIxI5U6lGS1TaEmAyQABARCKJRo6OZZ7GKjWoKmDzmh
BjoJZJZQztmlj7Qep/sf1l8= BjoJZJZQztmlj7Qep/sf1l8=
-----END EC PRIVATE KEY-----'; -----END EC PRIVATE KEY-----';
PKCS1::useSpecifiedCurve(); 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 // from https://tools.ietf.org/html/draft-ietf-curdle-pkix-07#section-10.1
public function testEd25519PublicKey() public function testEd25519PublicKey()
{ {
$key = new ECDSA; $key = PublicKeyLoader::load('-----BEGIN PUBLIC KEY-----
$key->load('-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE= MCowBQYDK2VwAyEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE=
-----END PUBLIC KEY-----'); -----END PUBLIC KEY-----');
$this->assertSame('Ed25519', $key->getCurve()); $this->assertSame('Ed25519', $key->getCurve());
@ -240,23 +231,21 @@ MCowBQYDK2VwAyEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE=
$expected = '-----BEGIN PUBLIC KEY----- $expected = '-----BEGIN PUBLIC KEY-----
MCwwBwYDK2VwBQADIQAZv0QJaYTN/oVBusFn3DuWyFCGqjC2tssMXDitcDFm4Q== MCwwBwYDK2VwBQADIQAZv0QJaYTN/oVBusFn3DuWyFCGqjC2tssMXDitcDFm4Q==
-----END PUBLIC KEY-----'; -----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 // from https://tools.ietf.org/html/draft-ietf-curdle-pkix-07#section-10.3
public function testEd25519PrivateKey() public function testEd25519PrivateKey()
{ {
// without public key (public key should be derived) // without public key (public key should be derived)
$key = new ECDSA; $key = PublicKeyLoader::load('-----BEGIN PRIVATE KEY-----
$key->load('-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC MC4CAQAwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC
-----END PRIVATE KEY-----'); -----END PRIVATE KEY-----');
$this->assertSame('Ed25519', $key->getCurve()); $this->assertSame('Ed25519', $key->getCurve());
$this->assertSame('Ed25519', $key->getPublicKey()->getCurve()); $this->assertSame('Ed25519', $key->getPublicKey()->getCurve());
// with public key // with public key
$key = new ECDSA; $key = PublicKeyLoader::load('-----BEGIN PRIVATE KEY-----
$key->load('-----BEGIN PRIVATE KEY-----
MHICAQEwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC MHICAQEwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC
oB8wHQYKKoZIhvcNAQkJFDEPDA1DdXJkbGUgQ2hhaXJzgSEAGb9ECWmEzf6FQbrB oB8wHQYKKoZIhvcNAQkJFDEPDA1DdXJkbGUgQ2hhaXJzgSEAGb9ECWmEzf6FQbrB
Z9w7lshQhqowtrbLDFw4rXAxZuE= Z9w7lshQhqowtrbLDFw4rXAxZuE=
@ -271,13 +260,12 @@ Z9w7lshQhqowtrbLDFw4rXAxZuE=
MFICAQEwBwYDK2VwBQAEIgQg1O5y2/kTWErVttjx92n4rTr+fCjL8dT74Jeoj0R1 MFICAQEwBwYDK2VwBQAEIgQg1O5y2/kTWErVttjx92n4rTr+fCjL8dT74Jeoj0R1
WEKBIBm/RAlphM3+hUG6wWfcO5bIUIaqMLa2ywxcOK1wMWbh WEKBIBm/RAlphM3+hUG6wWfcO5bIUIaqMLa2ywxcOK1wMWbh
-----END PRIVATE KEY-----'; -----END PRIVATE KEY-----';
$this->assertSame($expected, $key->getPrivateKey('PKCS8')); $this->assertSame($expected, $key->toString('PKCS8'));
} }
public function testPuTTYnistp256() public function testPuTTYnistp256()
{ {
$key = new ECDSA; $key = PublicKeyLoader::load($expected = 'PuTTY-User-Key-File-2: ecdsa-sha2-nistp256
$key->load($expected = 'PuTTY-User-Key-File-2: ecdsa-sha2-nistp256
Encryption: none Encryption: none
Comment: ecdsa-key-20181105 Comment: ecdsa-key-20181105
Public-Lines: 3 Public-Lines: 3
@ -291,20 +279,18 @@ Private-MAC: b85ca0eb7c612df5d18af85128821bd53faaa3ef
$this->assertSame('nistp256', $key->getCurve()); $this->assertSame('nistp256', $key->getCurve());
PuTTY::setComment('ecdsa-key-20181105'); PuTTY::setComment('ecdsa-key-20181105');
$this->assertSame($expected, $key->getPrivateKey('PuTTY')); $this->assertSame($expected, $key->toString('PuTTY'));
$key = new ECDSA; $key = PublicKeyLoader::load($expected = 'ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJEXCsWA8s18m25MJlVE1urbXPYFi4q8oMbb2H0kE2f5WPxizsKXRmb1J68paXQizryL9fC4FTqICJ1+UnaPfk0= ecdsa-key-20181105');
$key->load($expected = 'ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJEXCsWA8s18m25MJlVE1urbXPYFi4q8oMbb2H0kE2f5WPxizsKXRmb1J68paXQizryL9fC4FTqICJ1+UnaPfk0= ecdsa-key-20181105');
$this->assertSame('nistp256', $key->getCurve()); $this->assertSame('nistp256', $key->getCurve());
OpenSSH::setComment('ecdsa-key-20181105'); OpenSSH::setComment('ecdsa-key-20181105');
$this->assertSame($expected, $key->getPublicKey('OpenSSH')); $this->assertSame($expected, $key->toString('OpenSSH'));
} }
public function testPuTTYnistp384() public function testPuTTYnistp384()
{ {
$key = new ECDSA; $key = PublicKeyLoader::load($expected = 'PuTTY-User-Key-File-2: ecdsa-sha2-nistp384
$key->load($expected = 'PuTTY-User-Key-File-2: ecdsa-sha2-nistp384
Encryption: none Encryption: none
Comment: ecdsa-key-20181105 Comment: ecdsa-key-20181105
Public-Lines: 3 Public-Lines: 3
@ -319,21 +305,19 @@ Private-MAC: 97a990a3d5f6b8f268d4be9c4ab9ebfd8fa79849
$this->assertSame('nistp384', $key->getCurve()); $this->assertSame('nistp384', $key->getCurve());
PuTTY::setComment('ecdsa-key-20181105'); PuTTY::setComment('ecdsa-key-20181105');
$this->assertSame($expected, $key->getPrivateKey('PuTTY')); $this->assertSame($expected, $key->toString('PuTTY'));
$key = new ECDSA; $key = PublicKeyLoader::load($expected = 'ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBOI53wHG3CdcAJZq5PXWZAEAxxsNVFQlQgOX9toWEOgqQF5LbK2nWLKRvaHMzocUXaTYZDccSS0ATZFPT3j1Er1LU9cu4PHpyS07v262jdzkxIvKCPcAeISuV80MC7rHog== ecdsa-key-20181105');
$key->load($expected = 'ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBOI53wHG3CdcAJZq5PXWZAEAxxsNVFQlQgOX9toWEOgqQF5LbK2nWLKRvaHMzocUXaTYZDccSS0ATZFPT3j1Er1LU9cu4PHpyS07v262jdzkxIvKCPcAeISuV80MC7rHog== ecdsa-key-20181105');
$this->assertSame('nistp384', $key->getCurve()); $this->assertSame('nistp384', $key->getCurve());
OpenSSH::setComment('ecdsa-key-20181105'); OpenSSH::setComment('ecdsa-key-20181105');
$this->assertSame($expected, $key->getPublicKey('OpenSSH')); $this->assertSame($expected, $key->toString('OpenSSH'));
} }
public function testPuTTYnistp521() public function testPuTTYnistp521()
{ {
$key = new ECDSA; $key = PublicKeyLoader::load($expected = 'PuTTY-User-Key-File-2: ecdsa-sha2-nistp521
$key->load($expected = 'PuTTY-User-Key-File-2: ecdsa-sha2-nistp521
Encryption: none Encryption: none
Comment: ecdsa-key-20181105 Comment: ecdsa-key-20181105
Public-Lines: 4 Public-Lines: 4
@ -349,20 +333,18 @@ Private-MAC: 6d49ce289b85549a43d74422dd8bb3ba8798c72c
$this->assertSame('nistp521', $key->getCurve()); $this->assertSame('nistp521', $key->getCurve());
PuTTY::setComment('ecdsa-key-20181105'); PuTTY::setComment('ecdsa-key-20181105');
$this->assertSame($expected, $key->getPrivateKey('PuTTY')); $this->assertSame($expected, $key->toString('PuTTY'));
$key = new ECDSA; $key = PublicKeyLoader::load($expected = 'ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAF1Eg0MjaJwooFj6HCNh4RWbvmQRY+sdczJyBdT3EaTc/6IUcCfW7w7rAeRp2CDdE9RlAVD8IuLqW7DJH06Xeov8wBO5G6jUqXu0rlHsOSiC6VcCxBJuWVNB1IorHnS7PX0f6HdLlIEme73P77drqpn5YY0XLtP6hFrF7H5XfCxpNyaJA== ecdsa-key-20181105');
$key->load($expected = 'ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAF1Eg0MjaJwooFj6HCNh4RWbvmQRY+sdczJyBdT3EaTc/6IUcCfW7w7rAeRp2CDdE9RlAVD8IuLqW7DJH06Xeov8wBO5G6jUqXu0rlHsOSiC6VcCxBJuWVNB1IorHnS7PX0f6HdLlIEme73P77drqpn5YY0XLtP6hFrF7H5XfCxpNyaJA== ecdsa-key-20181105');
$this->assertSame('nistp521', $key->getCurve()); $this->assertSame('nistp521', $key->getCurve());
OpenSSH::setComment('ecdsa-key-20181105'); OpenSSH::setComment('ecdsa-key-20181105');
$this->assertSame($expected, $key->getPublicKey('OpenSSH')); $this->assertSame($expected, $key->toString('OpenSSH'));
} }
public function testPuTTYed25519() public function testPuTTYed25519()
{ {
$key = new ECDSA; $key = PublicKeyLoader::load($expected = 'PuTTY-User-Key-File-2: ssh-ed25519
$key->load($expected = 'PuTTY-User-Key-File-2: ssh-ed25519
Encryption: none Encryption: none
Comment: ed25519-key-20181105 Comment: ed25519-key-20181105
Public-Lines: 2 Public-Lines: 2
@ -375,14 +357,13 @@ Private-MAC: 8a06821a1c8b8b40fc40f876e543c4ea3fb81bb9
$this->assertSame('Ed25519', $key->getCurve()); $this->assertSame('Ed25519', $key->getCurve());
PuTTY::setComment('ed25519-key-20181105'); PuTTY::setComment('ed25519-key-20181105');
$this->assertSame($expected, $key->getPrivateKey('PuTTY')); $this->assertSame($expected, $key->toString('PuTTY'));
$key = new ECDSA; $key = PublicKeyLoader::load($expected = 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC6I6RyYAqtBcWXws9EDqGbhFtc5rKG4NMn/G7temQtu ed25519-key-20181105');
$key->load($expected = 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC6I6RyYAqtBcWXws9EDqGbhFtc5rKG4NMn/G7temQtu ed25519-key-20181105');
$this->assertSame('Ed25519', $key->getCurve()); $this->assertSame('Ed25519', $key->getCurve());
OpenSSH::setComment('ed25519-key-20181105'); OpenSSH::setComment('ed25519-key-20181105');
$this->assertSame($expected, $key->getPublicKey('OpenSSH')); $this->assertSame($expected, $key->toString('OpenSSH'));
} }
public function testlibsodium() public function testlibsodium()
@ -393,22 +374,19 @@ Private-MAC: 8a06821a1c8b8b40fc40f876e543c4ea3fb81bb9
$kp = sodium_crypto_sign_keypair(); $kp = sodium_crypto_sign_keypair();
$key = new ECDSA; $key = PublicKeyLoader::load($expected = sodium_crypto_sign_secretkey($kp));
$key->load($expected = sodium_crypto_sign_secretkey($kp));
$this->assertSame('Ed25519', $key->getCurve()); $this->assertSame('Ed25519', $key->getCurve());
$this->assertSame($expected, $key->getPrivateKey('libsodium')); $this->assertSame($expected, $key->toString('libsodium'));
$key = new ECDSA; $key = PublicKeyLoader::load($expected = sodium_crypto_sign_publickey($kp));
$key->load($expected = sodium_crypto_sign_publickey($kp));
$this->assertSame('Ed25519', $key->getCurve()); $this->assertSame('Ed25519', $key->getCurve());
$this->assertSame($expected, $key->getPublicKey('libsodium')); $this->assertSame($expected, $key->toString('libsodium'));
} }
// ssh-keygen -t ed25519 // ssh-keygen -t ed25519
public function testOpenSSHPrivateKey() public function testOpenSSHPrivateKey()
{ {
$key = new ECDSA; $key = PublicKeyLoader::load('-----BEGIN OPENSSH PRIVATE KEY-----
$key->load('-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACCpm7dS1/WDTW+uuhp2+aFLPKaJle6+oJqDGLXhlQAX4AAAAJg8TmN5PE5j QyNTUxOQAAACCpm7dS1/WDTW+uuhp2+aFLPKaJle6+oJqDGLXhlQAX4AAAAJg8TmN5PE5j
eQAAAAtzc2gtZWQyNTUxOQAAACCpm7dS1/WDTW+uuhp2+aFLPKaJle6+oJqDGLXhlQAX4A eQAAAAtzc2gtZWQyNTUxOQAAACCpm7dS1/WDTW+uuhp2+aFLPKaJle6+oJqDGLXhlQAX4A
@ -424,16 +402,14 @@ pomV7r6gmoMYteGVABfgAAAAD3ZhZ3JhbnRAdmFncmFudAECAwQFBg==
// support encrypted keys // support encrypted keys
// none-the-less, because of the randomized component we can't easily // none-the-less, because of the randomized component we can't easily
// see if the key string is equal to another known string // see if the key string is equal to another known string
$key2 = new ECDSA; $key2 = PublicKeyLoader::load($key->toString('OpenSSH'));
$key2->load($key->getPrivateKey('OpenSSH'));
$this->assertSame('Ed25519', $key2->getCurve()); $this->assertSame('Ed25519', $key2->getCurve());
} }
// from https://www.w3.org/TR/xmldsig-core/#sec-RFC4050Compat // from https://www.w3.org/TR/xmldsig-core/#sec-RFC4050Compat
public function testXMLKey() public function testXMLKey()
{ {
$key = new ECDSA; $key = PublicKeyLoader::load($orig = '<ECDSAKeyValue xmlns="http://www.w3.org/2001/04/xmldsig-more#">
$key->load($orig = '<ECDSAKeyValue xmlns="http://www.w3.org/2001/04/xmldsig-more#">
<DomainParameters> <DomainParameters>
<NamedCurve URN="urn:oid:1.2.840.10045.3.1.7" /> <NamedCurve URN="urn:oid:1.2.840.10045.3.1.7" />
</DomainParameters> </DomainParameters>
@ -453,22 +429,12 @@ pomV7r6gmoMYteGVABfgAAAAD3ZhZ3JhbnRAdmFncmFudAECAwQFBg==
//$dom = new DOMDocument(); //$dom = new DOMDocument();
//$dom->preserveWhiteSpace = false; //$dom->preserveWhiteSpace = false;
$dom->loadXML($key->getPublicKey('XML')); $dom->loadXML($key->toString('XML'));
$actual = $dom->C14N(); $actual = $dom->C14N();
$this->assertSame($expected, $actual); $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 = '') public static function assertSame($expected, $actual, $message = '')
{ {
$expected = str_replace("\r\n", "\n", $expected); $expected = str_replace("\r\n", "\n", $expected);

View File

@ -8,14 +8,17 @@
use phpseclib\Crypt\RSA; use phpseclib\Crypt\RSA;
use phpseclib\Crypt\RSA\Keys\PKCS1; use phpseclib\Crypt\RSA\Keys\PKCS1;
use phpseclib\Crypt\RSA\PrivateKey;
use phpseclib\Crypt\RSA\PublicKey;
class Unit_Crypt_RSA_CreateKeyTest extends PhpseclibTestCase class Unit_Crypt_RSA_CreateKeyTest extends PhpseclibTestCase
{ {
public function testCreateKey() public function testCreateKey()
{ {
extract(RSA::createKey(768)); $privatekey = RSA::createKey(768);
$this->assertInstanceOf('\phpseclib\Crypt\RSA', $privatekey); $publickey = $privatekey->getPublicKey();
$this->assertInstanceOf('\phpseclib\Crypt\RSA', $publickey); $this->assertInstanceOf(PrivateKey::class, $privatekey);
$this->assertInstanceOf(PublicKey::class, $publickey);
$this->assertNotEmpty("$privatekey"); $this->assertNotEmpty("$privatekey");
$this->assertNotEmpty("$publickey"); $this->assertNotEmpty("$publickey");
$this->assertSame($privatekey->getLength(), 768); $this->assertSame($privatekey->getLength(), 768);
@ -40,15 +43,15 @@ class Unit_Crypt_RSA_CreateKeyTest extends PhpseclibTestCase
{ {
RSA::useInternalEngine(); RSA::useInternalEngine();
RSA::setSmallestPrime(256); RSA::setSmallestPrime(256);
extract(RSA::createKey(1024)); $privatekey = RSA::createKey(1024);
$this->assertInstanceOf('\phpseclib\Crypt\RSA', $privatekey); $publickey = $privatekey->getPublicKey();
$this->assertInstanceOf('\phpseclib\Crypt\RSA', $publickey); $this->assertInstanceOf(PrivateKey::class, $privatekey);
$privatekey->setPrivateKeyFormat('PKCS1'); $this->assertInstanceOf(PublicKey::class, $publickey);
$this->assertNotEmpty("$privatekey"); $this->assertNotEmpty($privatekey->toString('PKCS1'));
$this->assertNotEmpty("$publickey"); $this->assertNotEmpty($publickey->toString('PKCS1'));
$this->assertSame($privatekey->getLength(), 1024); $this->assertSame($privatekey->getLength(), 1024);
$this->assertSame($publickey->getLength(), 1024); $this->assertSame($publickey->getLength(), 1024);
$r = PKCS1::load("$privatekey"); $r = PKCS1::load($privatekey->toString('PKCS1'));
$this->assertCount(4, $r['primes']); $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 // 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 // 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); $this->assertSame($prime->getLength(), 256);
} }
$rsa = new RSA(); $rsa = RSA::load($privatekey->toString('PKCS1'));
$rsa->load($privatekey->getPrivateKey());
$signature = $rsa->sign('zzz'); $signature = $rsa->sign('zzz');
$rsa->load($rsa->getPublicKey()); $rsa = RSA::load($rsa->getPublicKey()->toString('PKCS1'));
$this->assertTrue($rsa->verify('zzz', $signature)); $this->assertTrue($rsa->verify('zzz', $signature));
RSA::useBestEngine(); RSA::useBestEngine();

View File

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

View File

@ -7,6 +7,8 @@
use phpseclib\Crypt\RSA; use phpseclib\Crypt\RSA;
use phpseclib\Math\BigInteger; use phpseclib\Math\BigInteger;
use phpseclib\Crypt\PublicKeyLoader;
use phpseclib\Crypt\RSA\Keys\PKCS8;
class Unit_Crypt_RSA_ModeTest extends PhpseclibTestCase class Unit_Crypt_RSA_ModeTest extends PhpseclibTestCase
{ {
@ -14,8 +16,6 @@ class Unit_Crypt_RSA_ModeTest extends PhpseclibTestCase
{ {
$plaintext = 'a'; $plaintext = 'a';
$rsa = new RSA();
$privatekey = '-----BEGIN RSA PRIVATE KEY----- $privatekey = '-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp
wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5 wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5
@ -29,19 +29,21 @@ X6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2epl
U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ
37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0= 37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0=
-----END RSA PRIVATE KEY-----'; -----END RSA PRIVATE KEY-----';
$rsa->load($privatekey); $rsa = PublicKeyLoader::load($privatekey);
$rsa->load($rsa->getPublicKey()); $rsa = $rsa->getPublicKey()
->withPadding(RSA::ENCRYPTION_NONE);
$expected = '105b92f59a87a8ad4da52c128b8c99491790ef5a54770119e0819060032fb9e772ed6772828329567f3d7e9472154c1530f8156ba7fd732f52ca1c06' . $expected = '105b92f59a87a8ad4da52c128b8c99491790ef5a54770119e0819060032fb9e772ed6772828329567f3d7e9472154c1530f8156ba7fd732f52ca1c06' .
'5a3f5ed8a96c442e4662e0464c97f133aed31262170201993085a589565d67cc9e727e0d087e3b225c8965203b271e38a499c92fc0d6502297eca712' . '5a3f5ed8a96c442e4662e0464c97f133aed31262170201993085a589565d67cc9e727e0d087e3b225c8965203b271e38a499c92fc0d6502297eca712' .
'4d04bd467f6f1e7c'; '4d04bd467f6f1e7c';
$expected = pack('H*', $expected); $expected = pack('H*', $expected);
$result = $rsa->encrypt($plaintext, RSA::PADDING_NONE); $result = $rsa->encrypt($plaintext);
$this->assertEquals($result, $expected); $this->assertEquals($result, $expected);
$rsa->load($privatekey); $rsa = PublicKeyLoader::load($privatekey)
$this->assertEquals(trim($rsa->decrypt($result, RSA::PADDING_NONE), "\0"), $plaintext); ->withPadding(RSA::ENCRYPTION_NONE);
$this->assertEquals(trim($rsa->decrypt($result), "\0"), $plaintext);
} }
/** /**
@ -49,15 +51,14 @@ U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ
*/ */
public function testPSSSigs() public function testPSSSigs()
{ {
$rsa = new RSA(); $rsa = PublicKeyLoader::load('-----BEGIN PUBLIC KEY-----
$rsa->setHash('sha1');
$rsa->setMGFHash('sha1');
$rsa->load('-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqGKukO1De7zhZj6+H0qtjTkVx MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqGKukO1De7zhZj6+H0qtjTkVx
wTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUpwmJG8wVQZKjeGcjDOL5UlsuusFnc wTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUpwmJG8wVQZKjeGcjDOL5UlsuusFnc
CzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ51s1SPrCBkedbNf0T CzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ51s1SPrCBkedbNf0T
p0GbMJDyR4e9T04ZZwIDAQAB p0GbMJDyR4e9T04ZZwIDAQAB
-----END PUBLIC KEY-----'); -----END PUBLIC KEY-----')
->withHash('sha1')
->withMGFHash('sha1');
$sig = pack('H*', '1bd29a1d704a906cd7f726370ce1c63d8fb7b9a620871a05f3141a311c0d6e75fefb5d36dfb50d3ea2d37cd67992471419bfadd35da6e13b494' . $sig = pack('H*', '1bd29a1d704a906cd7f726370ce1c63d8fb7b9a620871a05f3141a311c0d6e75fefb5d36dfb50d3ea2d37cd67992471419bfadd35da6e13b494' .
'058ddc9b568d4cfea13ddc3c62b86a6256f5f296980d1131d3eaec6089069a3de79983f73eae20198a18721338b4a66e9cfe80e4f8e4fcef7a5bead5cbb' . '058ddc9b568d4cfea13ddc3c62b86a6256f5f296980d1131d3eaec6089069a3de79983f73eae20198a18721338b4a66e9cfe80e4f8e4fcef7a5bead5cbb' .
@ -72,22 +73,24 @@ p0GbMJDyR4e9T04ZZwIDAQAB
public function testSmallModulo() public function testSmallModulo()
{ {
$plaintext = 'x'; $plaintext = 'x';
$n = new BigInteger(base64_decode('272435F22706FA96DE26E980D22DFF67'), 256);
$e = new BigInteger(base64_decode('158753FF2AF4D1E5BBAB574D5AE6B54D'), 256);
$rsa = new RSA(); $key = PKCS8::savePublicKey(
$rsa->load(['n' => $n, 'e' => $e]); new BigInteger(base64_decode('272435F22706FA96DE26E980D22DFF67'), 256), // n
new BigInteger(base64_decode('158753FF2AF4D1E5BBAB574D5AE6B54D'), 256) // e
);
$rsa = PublicKeyLoader::load($key);
$rsa->encrypt($plaintext); $rsa->encrypt($plaintext);
} }
public function testPKCS1LooseVerify() public function testPKCS1LooseVerify()
{ {
$rsa = new RSA(); $rsa = PublicKeyLoader::load('-----BEGIN RSA PUBLIC KEY-----
$rsa->load('-----BEGIN RSA PUBLIC KEY-----
MIGJAoGBAMuqkz8ij+ESAaNvgocVGmapjlrIldmhRo4h2NX4e6IXiCLTSxASQtY4 MIGJAoGBAMuqkz8ij+ESAaNvgocVGmapjlrIldmhRo4h2NX4e6IXiCLTSxASQtY4
iqRnmyxqQSfaan2okTfQ6sP95bl8Qz8lgneW3ClC6RXG/wpJgsx7TXQ2kodlcKBF iqRnmyxqQSfaan2okTfQ6sP95bl8Qz8lgneW3ClC6RXG/wpJgsx7TXQ2kodlcKBF
m4k72G75QXhZ+I40ZG7cjBf1/9egakR0a0X0MpeOrKCzMBLv9+mpAgMBAAE= m4k72G75QXhZ+I40ZG7cjBf1/9egakR0a0X0MpeOrKCzMBLv9+mpAgMBAAE=
-----END RSA PUBLIC KEY-----'); -----END RSA PUBLIC KEY-----')
->withPadding(RSA::SIGNATURE_RELAXED_PKCS1);
$message = base64_decode('MYIBLjAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xNDA1MTUxNDM4MzRaMC8GCSqGSIb3DQEJBDEiBCBLzLIBGdOf0L2WRrIY' . $message = base64_decode('MYIBLjAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xNDA1MTUxNDM4MzRaMC8GCSqGSIb3DQEJBDEiBCBLzLIBGdOf0L2WRrIY' .
'9KTwiHnReBW48S9C7LNRaPp5mDCBwgYLKoZIhvcNAQkQAi8xgbIwga8wgawwgakEIJDB9ZGwihf+TaiwrHQNkNHkqbN8Nuws0e77QNObkvFZMIGEMHCkbjBs' . '9KTwiHnReBW48S9C7LNRaPp5mDCBwgYLKoZIhvcNAQkQAi8xgbIwga8wgawwgakEIJDB9ZGwihf+TaiwrHQNkNHkqbN8Nuws0e77QNObkvFZMIGEMHCkbjBs' .
@ -97,16 +100,14 @@ m4k72G75QXhZ+I40ZG7cjBf1/9egakR0a0X0MpeOrKCzMBLv9+mpAgMBAAE=
$sig = base64_decode('XDSZWw6IcUj8ICxRJf04HzF8stzoiFAZSR2a0Rw3ziZxTOT0/NVUYJO5+9TaaREXEgxuCLpgmA+6W2SWrrGoxbbNfaI90ZoKeOAws4IX+9RfiWuooibjKcvt' . $sig = base64_decode('XDSZWw6IcUj8ICxRJf04HzF8stzoiFAZSR2a0Rw3ziZxTOT0/NVUYJO5+9TaaREXEgxuCLpgmA+6W2SWrrGoxbbNfaI90ZoKeOAws4IX+9RfiWuooibjKcvt' .
'GJYVVOCcjvQYxUUNbQ4EjCUonk3h7ECXfCCmWqbeq2LsyXeeYGE='); 'GJYVVOCcjvQYxUUNbQ4EjCUonk3h7ECXfCCmWqbeq2LsyXeeYGE=');
$this->assertTrue($rsa->verify($message, $sig, RSA::PADDING_RELAXED_PKCS1)); $this->assertTrue($rsa->verify($message, $sig));
} }
public function testZeroLengthSalt() public function testZeroLengthSalt()
{ {
$plaintext = 'a'; $plaintext = 'a';
$rsa = new RSA(); $rsa = PublicKeyLoader::load('-----BEGIN RSA PRIVATE KEY-----
$privatekey = '-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp
wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5 wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5
1s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQABAoGAFijko56+qGyN8M0RVyaRAXz++xTqHBLh 1s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQABAoGAFijko56+qGyN8M0RVyaRAXz++xTqHBLh
@ -118,96 +119,17 @@ L0NDt4SkosjgGwJAFklyR1uZ/wPJjj611cdBcztlPdqoxssQGnh85BzCj/u3WqBpE2vjvyyvyI5k
X6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2epl X6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2epl
U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ
37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0= 37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0=
-----END RSA PRIVATE KEY-----'; -----END RSA PRIVATE KEY-----')
$rsa->load($privatekey); ->withSaltLength(0)
$rsa->setSaltLength(0); ->withHash('sha1')
$rsa->setHash('sha1'); ->withMGFHash('sha1');
$rsa->setMGFHash('sha1');
// Check we generate the correct signature. // Check we generate the correct signature.
$sig = pack('H*', '0ddfc93548e21d015c0a289a640b3b79aecfdfae045f583c5925b91cc5c399bba181616ad6ae20d9662d966f0eb2fddb550f4733268e34d640f4c9dadcaf25b3c82c42130a5081c6ebad7883331c65b25b6a37ffa7c4233a468dae56180787e2718ed87c48d8d50b72f5850e4a40963b4f36710be250ecef6fe0bb91249261a3'); $sig = pack('H*', '0ddfc93548e21d015c0a289a640b3b79aecfdfae045f583c5925b91cc5c399bba181616ad6ae20d9662d966f0eb2fddb550f4733268e34d640f4c9dadcaf25b3c82c42130a5081c6ebad7883331c65b25b6a37ffa7c4233a468dae56180787e2718ed87c48d8d50b72f5850e4a40963b4f36710be250ecef6fe0bb91249261a3');
$this->assertEquals($sig, $rsa->sign($plaintext)); $this->assertEquals($sig, $rsa->sign($plaintext));
// Check we can verify the signature correctly. // Check we can verify the signature correctly.
$rsa->load($rsa->getPublicKey()); $rsa = $rsa->getPublicKey();
$this->assertTrue($rsa->verify($plaintext, $sig)); $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\File\X509;
use phpseclib\Crypt\RSA; use phpseclib\Crypt\RSA;
use phpseclib\Crypt\PublicKeyLoader;
class Unit_File_X509_CSRTest extends PhpseclibTestCase 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 // on PHP 7.1, with older versions of phpseclib, this would produce a "A non-numeric value encountered" warning
public function testNewCSR() public function testNewCSR()
{ {
$rsa = new RSA();
$x509 = new X509(); $x509 = new X509();
$rsa->load('-----BEGIN RSA PRIVATE KEY----- $rsa = PublicKeyLoader::load('-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp
wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5 wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5
1s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQABAoGAFijko56+qGyN8M0RVyaRAXz++xTqHBLh 1s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQABAoGAFijko56+qGyN8M0RVyaRAXz++xTqHBLh

View File

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

View File

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