diff --git a/phpseclib/Crypt/RSA.php b/phpseclib/Crypt/RSA.php index b6d7e628..254b7351 100644 --- a/phpseclib/Crypt/RSA.php +++ b/phpseclib/Crypt/RSA.php @@ -8,36 +8,32 @@ * Here's an example of how to encrypt and decrypt text with this library: * * createKey()); + * extract(\phpseclib\Crypt\RSA::::createKey()); * - * $plaintext = 'terrafrost'; + * $plaintext = 'terrafrost'; * - * $rsa->loadKey($privatekey); - * $ciphertext = $rsa->encrypt($plaintext); + * $ciphertext = $publickey->encrypt($plaintext); * - * $rsa->loadKey($publickey); - * echo $rsa->decrypt($ciphertext); + * echo $privatekey->decrypt($ciphertext); * ?> * * * Here's an example of how to create signatures and verify signatures with this library: * * createKey()); + * extract(\phpseclib\Crypt\RSA::createKey()); * - * $plaintext = 'terrafrost'; + * $plaintext = 'terrafrost'; * - * $rsa->loadKey($privatekey); - * $signature = $rsa->sign($plaintext); + * $rsa->loadKey($privatekey); + * $signature = $rsa->sign($plaintext); * - * $rsa->loadKey($publickey); - * echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified'; + * $rsa->loadKey($publickey); + * echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified'; * ?> * * @@ -51,13 +47,8 @@ namespace phpseclib\Crypt; -use phpseclib\Crypt\AES; -use phpseclib\Crypt\Base; -use phpseclib\Crypt\DES; use phpseclib\Crypt\Hash; use phpseclib\Crypt\Random; -use phpseclib\Crypt\RSA; -use phpseclib\Crypt\TripleDES; use phpseclib\Math\BigInteger; /** @@ -105,7 +96,7 @@ class RSA * @see \phpseclib\Crypt\RSA::sign() * @see \phpseclib\Crypt\RSA::verify() * @see \phpseclib\Crypt\RSA::setHash() - */ + */ /** * Use the Probabilistic Signature Scheme for signing * @@ -127,7 +118,7 @@ class RSA /**#@+ * @access private * @see \phpseclib\Crypt\RSA::createKey() - */ + */ /** * ASN1 Integer */ @@ -153,7 +144,7 @@ class RSA /**#@+ * @access private * @see \phpseclib\Crypt\RSA::__construct() - */ + */ /** * To use the pure-PHP implementation */ @@ -166,96 +157,13 @@ class RSA const MODE_OPENSSL = 2; /**#@-*/ - /**#@+ - * @access public - * @see \phpseclib\Crypt\RSA::createKey() - * @see \phpseclib\Crypt\RSA::setPrivateKeyFormat() - */ - /** - * PKCS#1 formatted private key - * - * Used by OpenSSH - */ - const PRIVATE_FORMAT_PKCS1 = 0; - /** - * PuTTY formatted private key - */ - const PRIVATE_FORMAT_PUTTY = 1; - /** - * XML formatted private key - */ - const PRIVATE_FORMAT_XML = 2; - /** - * PKCS#8 formatted private key - */ - const PRIVATE_FORMAT_PKCS8 = 3; - /**#@-*/ - - /**#@+ - * @access public - * @see \phpseclib\Crypt\RSA::createKey() - * @see \phpseclib\Crypt\RSA::setPublicKeyFormat() - */ - /** - * Raw public key - * - * An array containing two \phpseclib\Math\BigInteger objects. - * - * The exponent can be indexed with any of the following: - * - * 0, e, exponent, publicExponent - * - * The modulus can be indexed with any of the following: - * - * 1, n, modulo, modulus - */ - const PUBLIC_FORMAT_RAW = 3; - /** - * PKCS#1 formatted public key (raw) - * - * Used by File/X509.php - * - * Has the following header: - * - * -----BEGIN RSA PUBLIC KEY----- - * - * Analogous to ssh-keygen's pem format (as specified by -m) - */ - const PUBLIC_FORMAT_PKCS1 = 4; - const PUBLIC_FORMAT_PKCS1_RAW = 4; - /** - * XML formatted public key - */ - const PUBLIC_FORMAT_XML = 5; - /** - * OpenSSH formatted public key - * - * Place in $HOME/.ssh/authorized_keys - */ - const PUBLIC_FORMAT_OPENSSH = 6; - /** - * PKCS#1 formatted public key (encapsulated) - * - * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set) - * - * Has the following header: - * - * -----BEGIN PUBLIC KEY----- - * - * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8 - * is specific to private keys it's basically creating a DER-encoded wrapper - * for keys. This just extends that same concept to public keys (much like ssh-keygen) - */ - const PUBLIC_FORMAT_PKCS8 = 7; - /**#@-*/ - /** * Precomputed Zero * * @var array * @access private */ - var $zero; + static $zero; /** * Precomputed One @@ -263,23 +171,23 @@ class RSA * @var array * @access private */ - var $one; + static $one; /** * Private Key Format * - * @var int + * @var string * @access private */ - var $privateKeyFormat = self::PRIVATE_FORMAT_PKCS1; + var $privateKeyFormat = 'PKCS1'; /** * Public Key Format * - * @var int - * @access public + * @var string + * @access private */ - var $publicKeyFormat = self::PUBLIC_FORMAT_PKCS8; + var $publicKeyFormat = 'PKCS8'; /** * Modulus (ie. n) @@ -409,47 +317,39 @@ class RSA */ var $password = false; - /** - * Components - * - * For use with parsing XML formatted keys. PHP's XML Parser functions use utilized - instead of PHP's DOM functions - - * because PHP's XML Parser functions work on PHP4 whereas PHP's DOM functions - although surperior - don't. - * - * @see \phpseclib\Crypt\RSA::_start_element_handler() - * @var array - * @access private - */ - var $components = array(); - - /** - * Current String - * - * For use with parsing XML formatted keys. - * - * @see \phpseclib\Crypt\RSA::_character_handler() - * @see \phpseclib\Crypt\RSA::_stop_element_handler() - * @var mixed - * @access private - */ - var $current; - /** * OpenSSL configuration file name. * * Set to null to use system configuration file. + * * @see \phpseclib\Crypt\RSA::createKey() * @var mixed - * @Access public + * @access public */ - var $configFile; + static $configFile; /** - * Public key comment field. + * Supported file formats * - * @var string + * @see \phpseclib\Crypt\RSA::load() + * @var array * @access private */ - var $comment = 'phpseclib-generated-key'; + static $fileFormats = false; + + /** + * Initialize static variables + * + * @access private + */ + static function _initialize_static_variables() + { + if (!isset(self::$zero)) { + self::$zero= new BigInteger(0); + self::$one = new BigInteger(1); + self::$configFile = __DIR__ . '/../openssl.cnf'; + } + } /** * The constructor @@ -463,7 +363,32 @@ class RSA */ function __construct() { - $this->configFile = dirname(__FILE__) . '/../openssl.cnf'; + self::_initialize_static_variables(); + + $this->hash = new Hash('sha1'); + $this->hLen = $this->hash->getLength(); + $this->hashName = 'sha1'; + $this->mgfHash = new Hash('sha1'); + $this->mgfHLen = $this->mgfHash->getLength(); + } + + /** + * Create public / private key pair + * + * Returns an array with the following three elements: + * - 'privatekey': The private key. + * - 'publickey': The public key. + * - 'partialkey': A partially computed key (if the execution time exceeded $timeout). + * Will need to be passed back to \phpseclib\Crypt\RSA::createKey() as the third parameter for further processing. + * + * @access public + * @param int $bits + * @param int $timeout + * @param array $p + */ + static function createKey($bits = 1024, $timeout = false, $partial = array()) + { + self::_initialize_static_variables(); if (!defined('CRYPT_RSA_MODE')) { switch (true) { @@ -473,7 +398,7 @@ class RSA case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'): define('CRYPT_RSA_MODE', self::MODE_INTERNAL); break; - case extension_loaded('openssl') && file_exists($this->configFile): + case extension_loaded('openssl') && file_exists(self::$configFile): // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work ob_start(); @phpinfo(); @@ -513,32 +438,6 @@ class RSA } } - $this->zero = new BigInteger(); - $this->one = new BigInteger(1); - - $this->hash = new Hash('sha1'); - $this->hLen = $this->hash->getLength(); - $this->hashName = 'sha1'; - $this->mgfHash = new Hash('sha1'); - $this->mgfHLen = $this->mgfHash->getLength(); - } - - /** - * Create public / private key pair - * - * Returns an array with the following three elements: - * - 'privatekey': The private key. - * - 'publickey': The public key. - * - 'partialkey': A partially computed key (if the execution time exceeded $timeout). - * Will need to be passed back to \phpseclib\Crypt\RSA::createKey() as the third parameter for further processing. - * - * @access public - * @param int $bits - * @param int $timeout - * @param array $p - */ - function createKey($bits = 1024, $timeout = false, $partial = array()) - { if (!defined('CRYPT_RSA_EXPONENT')) { // http://en.wikipedia.org/wiki/65537_%28number%29 define('CRYPT_RSA_EXPONENT', '65537'); @@ -556,16 +455,17 @@ class RSA // OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum if (CRYPT_RSA_MODE == self::MODE_OPENSSL && $bits >= 384 && CRYPT_RSA_EXPONENT == 65537) { $config = array(); - if (isset($this->configFile)) { - $config['config'] = $this->configFile; + if (isset(self::$configFile)) { + $config['config'] = self::$configFile; } $rsa = openssl_pkey_new(array('private_key_bits' => $bits) + $config); - openssl_pkey_export($rsa, $privatekey, null, $config); - $publickey = openssl_pkey_get_details($rsa); - $publickey = $publickey['key']; + openssl_pkey_export($rsa, $privatekeystr, null, $config); + $privatekey = new RSA(); + $privatekey->load($privatekeystr); - $privatekey = call_user_func_array(array($this, '_convertPrivateKey'), array_values($this->_parseKey($privatekey, self::PRIVATE_FORMAT_PKCS1))); - $publickey = call_user_func_array(array($this, '_convertPublicKey'), array_values($this->_parseKey($publickey, self::PUBLIC_FORMAT_PKCS1))); + $publickeyarr = openssl_pkey_get_details($rsa); + $publickey = new RSA(); + $publickey->load($publickeyarr['key']); // clear the buffer of error strings stemming from a minimalistic openssl.cnf while (openssl_error_string() !== false) { @@ -583,7 +483,7 @@ class RSA $e = new BigInteger(CRYPT_RSA_EXPONENT); } - extract($this->_generateMinMax($bits)); + extract(self::_generateMinMax($bits)); $absoluteMin = $min; $temp = $bits >> 1; // divide by two to see how many bits P and Q would be if ($temp > CRYPT_RSA_SMALLEST_PRIME) { @@ -592,19 +492,19 @@ class RSA } else { $num_primes = 2; } - extract($this->_generateMinMax($temp + $bits % $temp)); + extract(self::_generateMinMax($temp + $bits % $temp)); $finalMax = $max; - extract($this->_generateMinMax($temp)); + extract(self::_generateMinMax($temp)); $generator = new BigInteger(); - $n = $this->one->copy(); + $n = self::$one->copy(); if (!empty($partial)) { extract(unserialize($partial)); } else { $exponents = $coefficients = $primes = array(); $lcm = array( - 'top' => $this->one->copy(), + 'top' => self::$one->copy(), 'bottom' => false ); } @@ -633,8 +533,8 @@ class RSA if ($i == $num_primes) { list($min, $temp) = $absoluteMin->divide($n); - if (!$temp->equals($this->zero)) { - $min = $min->add($this->one); // ie. ceil() + if (!$temp->equals(self::$zero)) { + $min = $min->add(self::$one); // ie. ceil() } $primes[$i] = $generator->randomPrime($min, $finalMax, $timeout); } else { @@ -655,8 +555,8 @@ class RSA } return array( - 'privatekey' => '', - 'publickey' => '', + 'privatekey' => false, + 'publickey' => false, 'partialkey' => $partialkey ); } @@ -669,7 +569,7 @@ class RSA $n = $n->multiply($primes[$i]); - $temp = $primes[$i]->subtract($this->one); + $temp = $primes[$i]->subtract(self::$one); // textbook RSA implementations use Euler's totient function instead of the least common multiple. // see http://en.wikipedia.org/wiki/Euler%27s_totient_function @@ -682,7 +582,7 @@ class RSA list($temp) = $lcm['top']->divide($lcm['bottom']); $gcd = $temp->gcd($e); $i0 = 1; - } while (!$gcd->equals($this->one)); + } while (!$gcd->equals(self::$one)); $d = $e->modInverse($temp); @@ -701,806 +601,48 @@ class RSA // coefficient INTEGER, -- (inverse of q) mod p // otherPrimeInfos OtherPrimeInfos OPTIONAL // } + $privatekey = new RSA(); + $privatekey->modulus = $n; + $privatekey->k = $bits >> 3; + $privatekey->publicExponent = $e; + $privatekey->exponent = $d; + $privatekey->privateExponent = $e; + $privatekey->primes = $primes; + $privatekey->exponents = $exponents; + $privatekey->coefficients = $coefficients; + + $publickey = new RSA(); + $publickey->modulus = $n; + $publickey->k = $bits >> 3; + $publickey->exponent = $e; return array( - 'privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients), - 'publickey' => $this->_convertPublicKey($n, $e), + 'privatekey' => $privatekey, + 'publickey' => $publickey, 'partialkey' => false ); } /** - * Convert a private key to the appropriate format. + * Pre-loads all the key format plugins * + * @see load() * @access private - * @see setPrivateKeyFormat() - * @param string $RSAPrivateKey - * @return string */ - function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients) + function _loadFileFormats() { - $signed = $this->privateKeyFormat != self::PRIVATE_FORMAT_XML; - $num_primes = count($primes); - $raw = array( - 'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi - 'modulus' => $n->toBytes($signed), - 'publicExponent' => $e->toBytes($signed), - 'privateExponent' => $d->toBytes($signed), - 'prime1' => $primes[1]->toBytes($signed), - 'prime2' => $primes[2]->toBytes($signed), - 'exponent1' => $exponents[1]->toBytes($signed), - 'exponent2' => $exponents[2]->toBytes($signed), - 'coefficient' => $coefficients[2]->toBytes($signed) - ); - - // if the format in question does not support multi-prime rsa and multi-prime rsa was used, - // call _convertPublicKey() instead. - switch ($this->privateKeyFormat) { - case self::PRIVATE_FORMAT_XML: - if ($num_primes != 2) { - return false; + if (self::$fileFormats === false) { + self::$fileFormats = array(); + foreach (glob(__DIR__ . '/RSA/*.php') as $file) { + $type = 'phpseclib\Crypt\RSA\\' . pathinfo($file, PATHINFO_FILENAME); + $meta = new \ReflectionClass($type); + if (!$meta->isAbstract()) { + self::$fileFormats[] = $type; } - return "\r\n" . - ' ' . base64_encode($raw['modulus']) . "\r\n" . - ' ' . base64_encode($raw['publicExponent']) . "\r\n" . - '

' . base64_encode($raw['prime1']) . "

\r\n" . - ' ' . base64_encode($raw['prime2']) . "\r\n" . - ' ' . base64_encode($raw['exponent1']) . "\r\n" . - ' ' . base64_encode($raw['exponent2']) . "\r\n" . - ' ' . base64_encode($raw['coefficient']) . "\r\n" . - ' ' . base64_encode($raw['privateExponent']) . "\r\n" . - '
'; - break; - case self::PRIVATE_FORMAT_PUTTY: - if ($num_primes != 2) { - return false; - } - $key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: "; - $encryption = (!empty($this->password) || is_string($this->password)) ? 'aes256-cbc' : 'none'; - $key.= $encryption; - $key.= "\r\nComment: " . $this->comment . "\r\n"; - $public = pack( - 'Na*Na*Na*', - strlen('ssh-rsa'), - 'ssh-rsa', - strlen($raw['publicExponent']), - $raw['publicExponent'], - strlen($raw['modulus']), - $raw['modulus'] - ); - $source = pack( - 'Na*Na*Na*Na*', - strlen('ssh-rsa'), - 'ssh-rsa', - strlen($encryption), - $encryption, - strlen($this->comment), - $this->comment, - strlen($public), - $public - ); - $public = base64_encode($public); - $key.= "Public-Lines: " . ((strlen($public) + 63) >> 6) . "\r\n"; - $key.= chunk_split($public, 64); - $private = pack( - 'Na*Na*Na*Na*', - strlen($raw['privateExponent']), - $raw['privateExponent'], - strlen($raw['prime1']), - $raw['prime1'], - strlen($raw['prime2']), - $raw['prime2'], - strlen($raw['coefficient']), - $raw['coefficient'] - ); - if (empty($this->password) && !is_string($this->password)) { - $source.= pack('Na*', strlen($private), $private); - $hashkey = 'putty-private-key-file-mac-key'; - } else { - $private.= Random::string(16 - (strlen($private) & 15)); - $source.= pack('Na*', strlen($private), $private); - $sequence = 0; - $symkey = ''; - while (strlen($symkey) < 32) { - $temp = pack('Na*', $sequence++, $this->password); - $symkey.= pack('H*', sha1($temp)); - } - $symkey = substr($symkey, 0, 32); - $crypto = new AES(); - - $crypto->setKey($symkey); - $crypto->disablePadding(); - $private = $crypto->encrypt($private); - $hashkey = 'putty-private-key-file-mac-key' . $this->password; - } - - $private = base64_encode($private); - $key.= 'Private-Lines: ' . ((strlen($private) + 63) >> 6) . "\r\n"; - $key.= chunk_split($private, 64); - $hash = new Hash('sha1'); - $hash->setKey(pack('H*', sha1($hashkey))); - $key.= 'Private-MAC: ' . bin2hex($hash->hash($source)) . "\r\n"; - - return $key; - default: // eg. self::PRIVATE_FORMAT_PKCS1 - $components = array(); - foreach ($raw as $name => $value) { - $components[$name] = pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($value)), $value); - } - - $RSAPrivateKey = implode('', $components); - - if ($num_primes > 2) { - $OtherPrimeInfos = ''; - for ($i = 3; $i <= $num_primes; $i++) { - // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo - // - // OtherPrimeInfo ::= SEQUENCE { - // prime INTEGER, -- ri - // exponent INTEGER, -- di - // coefficient INTEGER -- ti - // } - $OtherPrimeInfo = pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true)); - $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true)); - $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true)); - $OtherPrimeInfos.= pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo); - } - $RSAPrivateKey.= pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos); - } - - $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); - - if ($this->privateKeyFormat == self::PRIVATE_FORMAT_PKCS8) { - $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA - $RSAPrivateKey = pack( - 'Ca*a*Ca*a*', - self::ASN1_INTEGER, - "\01\00", - $rsaOID, - 4, - $this->_encodeLength(strlen($RSAPrivateKey)), - $RSAPrivateKey - ); - $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); - if (!empty($this->password) || is_string($this->password)) { - $salt = Random::string(8); - $iterationCount = 2048; - - $crypto = new DES(); - $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount); - $RSAPrivateKey = $crypto->encrypt($RSAPrivateKey); - - $parameters = pack( - 'Ca*a*Ca*N', - self::ASN1_OCTETSTRING, - $this->_encodeLength(strlen($salt)), - $salt, - self::ASN1_INTEGER, - $this->_encodeLength(4), - $iterationCount - ); - $pbeWithMD5AndDES_CBC = "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03"; - - $encryptionAlgorithm = pack( - 'Ca*a*Ca*a*', - self::ASN1_OBJECT, - $this->_encodeLength(strlen($pbeWithMD5AndDES_CBC)), - $pbeWithMD5AndDES_CBC, - self::ASN1_SEQUENCE, - $this->_encodeLength(strlen($parameters)), - $parameters - ); - - $RSAPrivateKey = pack( - 'Ca*a*Ca*a*', - self::ASN1_SEQUENCE, - $this->_encodeLength(strlen($encryptionAlgorithm)), - $encryptionAlgorithm, - self::ASN1_OCTETSTRING, - $this->_encodeLength(strlen($RSAPrivateKey)), - $RSAPrivateKey - ); - - $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); - - $RSAPrivateKey = "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" . - chunk_split(base64_encode($RSAPrivateKey), 64) . - '-----END ENCRYPTED PRIVATE KEY-----'; - } else { - $RSAPrivateKey = "-----BEGIN PRIVATE KEY-----\r\n" . - chunk_split(base64_encode($RSAPrivateKey), 64) . - '-----END PRIVATE KEY-----'; - } - return $RSAPrivateKey; - } - - if (!empty($this->password) || is_string($this->password)) { - $iv = Random::string(8); - $symkey = pack('H*', md5($this->password . $iv)); // symkey is short for symmetric key - $symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8); - $des = new TripleDES(); - $des->setKey($symkey); - $des->setIV($iv); - $iv = strtoupper(bin2hex($iv)); - $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" . - "Proc-Type: 4,ENCRYPTED\r\n" . - "DEK-Info: DES-EDE3-CBC,$iv\r\n" . - "\r\n" . - chunk_split(base64_encode($des->encrypt($RSAPrivateKey)), 64) . - '-----END RSA PRIVATE KEY-----'; - } else { - $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" . - chunk_split(base64_encode($RSAPrivateKey), 64) . - '-----END RSA PRIVATE KEY-----'; - } - - return $RSAPrivateKey; + } } } - /** - * Convert a public key to the appropriate format - * - * @access private - * @see setPublicKeyFormat() - * @param string $RSAPrivateKey - * @return string - */ - function _convertPublicKey($n, $e) - { - $signed = $this->publicKeyFormat != self::PUBLIC_FORMAT_XML; - - $modulus = $n->toBytes($signed); - $publicExponent = $e->toBytes($signed); - - switch ($this->publicKeyFormat) { - case self::PUBLIC_FORMAT_RAW: - return array('e' => $e->copy(), 'n' => $n->copy()); - case self::PUBLIC_FORMAT_XML: - return "\r\n" . - ' ' . base64_encode($modulus) . "\r\n" . - ' ' . base64_encode($publicExponent) . "\r\n" . - ''; - break; - case self::PUBLIC_FORMAT_OPENSSH: - // from : - // string "ssh-rsa" - // mpint e - // mpint n - $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus); - $RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment; - - return $RSAPublicKey; - default: // eg. self::PUBLIC_FORMAT_PKCS1_RAW or self::PUBLIC_FORMAT_PKCS1 - // from : - // RSAPublicKey ::= SEQUENCE { - // modulus INTEGER, -- n - // publicExponent INTEGER -- e - // } - $components = array( - 'modulus' => pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus), - 'publicExponent' => pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($publicExponent)), $publicExponent) - ); - - $RSAPublicKey = pack( - 'Ca*a*a*', - self::ASN1_SEQUENCE, - $this->_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])), - $components['modulus'], - $components['publicExponent'] - ); - - if ($this->publicKeyFormat == self::PUBLIC_FORMAT_PKCS1_RAW) { - $RSAPublicKey = "-----BEGIN RSA PUBLIC KEY-----\r\n" . - chunk_split(base64_encode($RSAPublicKey), 64) . - '-----END RSA PUBLIC KEY-----'; - } else { - // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption. - $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA - $RSAPublicKey = chr(0) . $RSAPublicKey; - $RSAPublicKey = chr(3) . $this->_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey; - - $RSAPublicKey = pack( - 'Ca*a*', - self::ASN1_SEQUENCE, - $this->_encodeLength(strlen($rsaOID . $RSAPublicKey)), - $rsaOID . $RSAPublicKey - ); - - $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" . - chunk_split(base64_encode($RSAPublicKey), 64) . - '-----END PUBLIC KEY-----'; - } - - return $RSAPublicKey; - } - } - - /** - * Break a public or private key down into its constituant components - * - * @access private - * @see _convertPublicKey() - * @see _convertPrivateKey() - * @param string $key - * @param int $type - * @return array - */ - function _parseKey($key, $type) - { - if ($type != self::PUBLIC_FORMAT_RAW && !is_string($key)) { - return false; - } - - switch ($type) { - case self::PUBLIC_FORMAT_RAW: - if (!is_array($key)) { - return false; - } - $components = array(); - switch (true) { - case isset($key['e']): - $components['publicExponent'] = $key['e']->copy(); - break; - case isset($key['exponent']): - $components['publicExponent'] = $key['exponent']->copy(); - break; - case isset($key['publicExponent']): - $components['publicExponent'] = $key['publicExponent']->copy(); - break; - case isset($key[0]): - $components['publicExponent'] = $key[0]->copy(); - } - switch (true) { - case isset($key['n']): - $components['modulus'] = $key['n']->copy(); - break; - case isset($key['modulo']): - $components['modulus'] = $key['modulo']->copy(); - break; - case isset($key['modulus']): - $components['modulus'] = $key['modulus']->copy(); - break; - case isset($key[1]): - $components['modulus'] = $key[1]->copy(); - } - return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false; - case self::PRIVATE_FORMAT_PKCS1: - case self::PRIVATE_FORMAT_PKCS8: - case self::PUBLIC_FORMAT_PKCS1: - /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is - "outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to - protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding - two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here: - - http://tools.ietf.org/html/rfc1421#section-4.6.1.1 - http://tools.ietf.org/html/rfc1421#section-4.6.1.3 - - DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell. - DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation - function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's - own implementation. ie. the implementation *is* the standard and any bugs that may exist in that - implementation are part of the standard, as well. - - * OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */ - if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) { - $iv = pack('H*', trim($matches[2])); - $symkey = pack('H*', md5($this->password . substr($iv, 0, 8))); // symkey is short for symmetric key - $symkey.= pack('H*', md5($symkey . $this->password . substr($iv, 0, 8))); - // remove the Proc-Type / DEK-Info sections as they're no longer needed - $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key); - $ciphertext = $this->_extractBER($key); - if ($ciphertext === false) { - $ciphertext = $key; - } - switch ($matches[1]) { - case 'AES-256-CBC': - $crypto = new AES(); - break; - case 'AES-128-CBC': - $symkey = substr($symkey, 0, 16); - $crypto = new AES(); - break; - case 'DES-EDE3-CFB': - $crypto = new TripleDES(Base::MODE_CFB); - break; - case 'DES-EDE3-CBC': - $symkey = substr($symkey, 0, 24); - $crypto = new TripleDES(); - break; - case 'DES-CBC': - $crypto = new DES(); - break; - default: - return false; - } - $crypto->setKey($symkey); - $crypto->setIV($iv); - $decoded = $crypto->decrypt($ciphertext); - } else { - $decoded = $this->_extractBER($key); - } - - if ($decoded !== false) { - $key = $decoded; - } - - $components = array(); - - if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) { - return false; - } - if ($this->_decodeLength($key) != strlen($key)) { - return false; - } - - $tag = ord($this->_string_shift($key)); - /* intended for keys for which OpenSSL's asn1parse returns the following: - - 0:d=0 hl=4 l= 631 cons: SEQUENCE - 4:d=1 hl=2 l= 1 prim: INTEGER :00 - 7:d=1 hl=2 l= 13 cons: SEQUENCE - 9:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption - 20:d=2 hl=2 l= 0 prim: NULL - 22:d=1 hl=4 l= 609 prim: OCTET STRING - - ie. PKCS8 keys*/ - - if ($tag == self::ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") { - $this->_string_shift($key, 3); - $tag = self::ASN1_SEQUENCE; - } - - if ($tag == self::ASN1_SEQUENCE) { - $temp = $this->_string_shift($key, $this->_decodeLength($key)); - if (ord($this->_string_shift($temp)) != self::ASN1_OBJECT) { - return false; - } - $length = $this->_decodeLength($temp); - switch ($this->_string_shift($temp, $length)) { - case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption - break; - case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC - /* - PBEParameter ::= SEQUENCE { - salt OCTET STRING (SIZE(8)), - iterationCount INTEGER } - */ - if (ord($this->_string_shift($temp)) != self::ASN1_SEQUENCE) { - return false; - } - if ($this->_decodeLength($temp) != strlen($temp)) { - return false; - } - $this->_string_shift($temp); // assume it's an octet string - $salt = $this->_string_shift($temp, $this->_decodeLength($temp)); - if (ord($this->_string_shift($temp)) != self::ASN1_INTEGER) { - return false; - } - $this->_decodeLength($temp); - list(, $iterationCount) = unpack('N', str_pad($temp, 4, chr(0), STR_PAD_LEFT)); - $this->_string_shift($key); // assume it's an octet string - $length = $this->_decodeLength($key); - if (strlen($key) != $length) { - return false; - } - - $crypto = new DES(); - $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount); - $key = $crypto->decrypt($key); - if ($key === false) { - return false; - } - return $this->_parseKey($key, self::PRIVATE_FORMAT_PKCS1); - default: - return false; - } - /* intended for keys for which OpenSSL's asn1parse returns the following: - - 0:d=0 hl=4 l= 290 cons: SEQUENCE - 4:d=1 hl=2 l= 13 cons: SEQUENCE - 6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption - 17:d=2 hl=2 l= 0 prim: NULL - 19:d=1 hl=4 l= 271 prim: BIT STRING */ - $tag = ord($this->_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag - $this->_decodeLength($key); // skip over the BIT STRING / OCTET STRING length - // "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of - // unused bits in the final subsequent octet. The number shall be in the range zero to seven." - // -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2) - if ($tag == self::ASN1_BITSTRING) { - $this->_string_shift($key); - } - if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) { - return false; - } - if ($this->_decodeLength($key) != strlen($key)) { - return false; - } - $tag = ord($this->_string_shift($key)); - } - if ($tag != self::ASN1_INTEGER) { - return false; - } - - $length = $this->_decodeLength($key); - $temp = $this->_string_shift($key, $length); - if (strlen($temp) != 1 || ord($temp) > 2) { - $components['modulus'] = new BigInteger($temp, 256); - $this->_string_shift($key); // skip over self::ASN1_INTEGER - $length = $this->_decodeLength($key); - $components[$type == self::PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = new BigInteger($this->_string_shift($key, $length), 256); - - return $components; - } - if (ord($this->_string_shift($key)) != self::ASN1_INTEGER) { - return false; - } - $length = $this->_decodeLength($key); - $components['modulus'] = new BigInteger($this->_string_shift($key, $length), 256); - $this->_string_shift($key); - $length = $this->_decodeLength($key); - $components['publicExponent'] = new BigInteger($this->_string_shift($key, $length), 256); - $this->_string_shift($key); - $length = $this->_decodeLength($key); - $components['privateExponent'] = new BigInteger($this->_string_shift($key, $length), 256); - $this->_string_shift($key); - $length = $this->_decodeLength($key); - $components['primes'] = array(1 => new BigInteger($this->_string_shift($key, $length), 256)); - $this->_string_shift($key); - $length = $this->_decodeLength($key); - $components['primes'][] = new BigInteger($this->_string_shift($key, $length), 256); - $this->_string_shift($key); - $length = $this->_decodeLength($key); - $components['exponents'] = array(1 => new BigInteger($this->_string_shift($key, $length), 256)); - $this->_string_shift($key); - $length = $this->_decodeLength($key); - $components['exponents'][] = new BigInteger($this->_string_shift($key, $length), 256); - $this->_string_shift($key); - $length = $this->_decodeLength($key); - $components['coefficients'] = array(2 => new BigInteger($this->_string_shift($key, $length), 256)); - - if (!empty($key)) { - if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) { - return false; - } - $this->_decodeLength($key); - while (!empty($key)) { - if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) { - return false; - } - $this->_decodeLength($key); - $key = substr($key, 1); - $length = $this->_decodeLength($key); - $components['primes'][] = new BigInteger($this->_string_shift($key, $length), 256); - $this->_string_shift($key); - $length = $this->_decodeLength($key); - $components['exponents'][] = new BigInteger($this->_string_shift($key, $length), 256); - $this->_string_shift($key); - $length = $this->_decodeLength($key); - $components['coefficients'][] = new BigInteger($this->_string_shift($key, $length), 256); - } - } - - return $components; - case self::PUBLIC_FORMAT_OPENSSH: - $parts = explode(' ', $key, 3); - - $key = isset($parts[1]) ? base64_decode($parts[1]) : false; - if ($key === false) { - return false; - } - - $comment = isset($parts[2]) ? $parts[2] : false; - - $cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa"; - - if (strlen($key) <= 4) { - return false; - } - extract(unpack('Nlength', $this->_string_shift($key, 4))); - $publicExponent = new BigInteger($this->_string_shift($key, $length), -256); - if (strlen($key) <= 4) { - return false; - } - extract(unpack('Nlength', $this->_string_shift($key, 4))); - $modulus = new BigInteger($this->_string_shift($key, $length), -256); - - if ($cleanup && strlen($key)) { - if (strlen($key) <= 4) { - return false; - } - extract(unpack('Nlength', $this->_string_shift($key, 4))); - $realModulus = new BigInteger($this->_string_shift($key, $length), -256); - return strlen($key) ? false : array( - 'modulus' => $realModulus, - 'publicExponent' => $modulus, - 'comment' => $comment - ); - } else { - return strlen($key) ? false : array( - 'modulus' => $modulus, - 'publicExponent' => $publicExponent, - 'comment' => $comment - ); - } - // http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue - // http://en.wikipedia.org/wiki/XML_Signature - case self::PRIVATE_FORMAT_XML: - case self::PUBLIC_FORMAT_XML: - $this->components = array(); - - $xml = xml_parser_create('UTF-8'); - xml_set_object($xml, $this); - xml_set_element_handler($xml, '_start_element_handler', '_stop_element_handler'); - xml_set_character_data_handler($xml, '_data_handler'); - // add to account for "dangling" tags like ... that are sometimes added - if (!xml_parse($xml, '' . $key . '')) { - return false; - } - - return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false; - // from PuTTY's SSHPUBK.C - case self::PRIVATE_FORMAT_PUTTY: - $components = array(); - $key = preg_split('#\r\n|\r|\n#', $key); - $type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0])); - if ($type != 'ssh-rsa') { - return false; - } - $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1])); - $comment = trim(preg_replace('#Comment: (.+)#', '$1', $key[2])); - - $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3])); - $public = base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength)))); - $public = substr($public, 11); - extract(unpack('Nlength', $this->_string_shift($public, 4))); - $components['publicExponent'] = new BigInteger($this->_string_shift($public, $length), -256); - extract(unpack('Nlength', $this->_string_shift($public, 4))); - $components['modulus'] = new BigInteger($this->_string_shift($public, $length), -256); - - $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$publicLength + 4])); - $private = base64_decode(implode('', array_map('trim', array_slice($key, $publicLength + 5, $privateLength)))); - - switch ($encryption) { - case 'aes256-cbc': - $symkey = ''; - $sequence = 0; - while (strlen($symkey) < 32) { - $temp = pack('Na*', $sequence++, $this->password); - $symkey.= pack('H*', sha1($temp)); - } - $symkey = substr($symkey, 0, 32); - $crypto = new AES(); - } - - if ($encryption != 'none') { - $crypto->setKey($symkey); - $crypto->disablePadding(); - $private = $crypto->decrypt($private); - if ($private === false) { - return false; - } - } - - extract(unpack('Nlength', $this->_string_shift($private, 4))); - if (strlen($private) < $length) { - return false; - } - $components['privateExponent'] = new BigInteger($this->_string_shift($private, $length), -256); - extract(unpack('Nlength', $this->_string_shift($private, 4))); - if (strlen($private) < $length) { - return false; - } - $components['primes'] = array(1 => new BigInteger($this->_string_shift($private, $length), -256)); - extract(unpack('Nlength', $this->_string_shift($private, 4))); - if (strlen($private) < $length) { - return false; - } - $components['primes'][] = new BigInteger($this->_string_shift($private, $length), -256); - - $temp = $components['primes'][1]->subtract($this->one); - $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp)); - $temp = $components['primes'][2]->subtract($this->one); - $components['exponents'][] = $components['publicExponent']->modInverse($temp); - - extract(unpack('Nlength', $this->_string_shift($private, 4))); - if (strlen($private) < $length) { - return false; - } - $components['coefficients'] = array(2 => new BigInteger($this->_string_shift($private, $length), -256)); - - return $components; - } - } - - /** - * Returns the key size - * - * More specifically, this returns the size of the modulo in bits. - * - * @access public - * @return int - */ - function getSize() - { - return !isset($this->modulus) ? 0 : strlen($this->modulus->toBits()); - } - - /** - * Start Element Handler - * - * Called by xml_set_element_handler() - * - * @access private - * @param resource $parser - * @param string $name - * @param array $attribs - */ - function _start_element_handler($parser, $name, $attribs) - { - //$name = strtoupper($name); - switch ($name) { - case 'MODULUS': - $this->current = &$this->components['modulus']; - break; - case 'EXPONENT': - $this->current = &$this->components['publicExponent']; - break; - case 'P': - $this->current = &$this->components['primes'][1]; - break; - case 'Q': - $this->current = &$this->components['primes'][2]; - break; - case 'DP': - $this->current = &$this->components['exponents'][1]; - break; - case 'DQ': - $this->current = &$this->components['exponents'][2]; - break; - case 'INVERSEQ': - $this->current = &$this->components['coefficients'][2]; - break; - case 'D': - $this->current = &$this->components['privateExponent']; - } - $this->current = ''; - } - - /** - * Stop Element Handler - * - * Called by xml_set_element_handler() - * - * @access private - * @param resource $parser - * @param string $name - */ - function _stop_element_handler($parser, $name) - { - if (isset($this->current)) { - $this->current = new BigInteger(base64_decode($this->current), 256); - unset($this->current); - } - } - - /** - * Data Handler - * - * Called by xml_set_character_data_handler() - * - * @access private - * @param resource $parser - * @param string $data - */ - function _data_handler($parser, $data) - { - if (!isset($this->current) || is_object($this->current)) { - return; - } - $this->current.= trim($data); - } - /** * Loads a public or private key * @@ -1510,7 +652,7 @@ class RSA * @param string $key * @param int $type optional */ - function loadKey($key, $type = false) + function load($key, $type = false) { if ($key instanceof RSA) { $this->privateKeyFormat = $key->privateKeyFormat; @@ -1559,22 +701,30 @@ class RSA return true; } + $components = false; if ($type === false) { - $types = array( - self::PUBLIC_FORMAT_RAW, - self::PRIVATE_FORMAT_PKCS1, - self::PRIVATE_FORMAT_XML, - self::PRIVATE_FORMAT_PUTTY, - self::PUBLIC_FORMAT_OPENSSH - ); - foreach ($types as $type) { - $components = $this->_parseKey($key, $type); + $this->_loadFileFormats(); + foreach (self::$fileFormats as $format) { + try { + $components = $format::load($key, $this->password); + } catch (Exception $e) { + $components = false; + } if ($components !== false) { break; } } } else { - $components = $this->_parseKey($key, $type); + if ($type[0] != '\\') { + $type = '\phpseclib\Crypt\RSA\\' . $type; + } + if (class_exists($type)) { + try { + $components = $type::load($key, $this->password); + } catch (Exception $e) { + $components = false; + } + } } if ($components === false) { @@ -1599,22 +749,55 @@ class RSA $this->publicExponent = false; } - switch ($type) { - case self::PUBLIC_FORMAT_OPENSSH: - case self::PUBLIC_FORMAT_RAW: - $this->setPublicKey(); - break; - case self::PRIVATE_FORMAT_PKCS1: - switch (true) { - case strpos($key, '-BEGIN PUBLIC KEY-') !== false: - case strpos($key, '-BEGIN RSA PUBLIC KEY-') !== false: - $this->setPublicKey(); - } + if ($components['isPublicKey']) { + $this->setPublicKey(); } return true; } + /** + * Returns the private key + * + * The private key is only returned if the currently loaded key contains the constituent prime numbers. + * + * @see getPublicKey() + * @access public + * @param string $type optional + */ + function getPrivateKey($type = 'PKCS1') + { + if ($type[0] != '\\') { + $type = '\phpseclib\Crypt\RSA\\' . $type; + } + if (!class_exists($type) || !method_exists($type, 'savePrivateKey')) { + return false; + } + + if (empty($this->primes)) { + return false; + } + + $oldFormat = $this->privateKeyFormat; + $this->privateKeyFormat = $type; + $temp = $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients, $this->password); + $this->privateKeyFormat = $oldFormat; + return $temp; + } + + /** + * Returns the key size + * + * More specifically, this returns the size of the modulo in bits. + * + * @access public + * @return int + */ + function getSize() + { + return !isset($this->modulus) ? 0 : strlen($this->modulus->toBits()); + } + /** * Sets the password * @@ -1622,7 +805,7 @@ class RSA * Or rather, pass in $password such that empty($password) && !is_string($password) is true. * * @see createKey() - * @see loadKey() + * @see load() * @access public * @param string $password */ @@ -1664,21 +847,33 @@ class RSA return true; } + $components = false; if ($type === false) { - $types = array( - self::PUBLIC_FORMAT_RAW, - self::PUBLIC_FORMAT_PKCS1, - self::PUBLIC_FORMAT_XML, - self::PUBLIC_FORMAT_OPENSSH - ); - foreach ($types as $type) { - $components = $this->_parseKey($key, $type); + $this->_loadFileFormats(); + foreach (self::$fileFormats 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 { - $components = $this->_parseKey($key, $type); + if ($type[0] != '\\') { + $type = '\phpseclib\Crypt\RSA\\' . $type; + } + if (class_exists($type)) { + try { + $components = $type::load($key, $this->password); + } catch (Exception $e) { + $components = false; + } + } } if ($components === false) { @@ -1720,13 +915,13 @@ class RSA } $rsa = new RSA(); - if (!$rsa->loadKey($key, $type)) { + if (!$rsa->load($key, $type)) { return false; } unset($rsa->publicExponent); // don't overwrite the old key if the new key is invalid - $this->loadKey($rsa); + $this->load($rsa); return true; } @@ -1737,20 +932,26 @@ class RSA * 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. * - * @see getPublicKey() + * @see getPrivateKey() * @access public - * @param string $key - * @param int $type optional + * @param string $type optional */ - function getPublicKey($type = self::PUBLIC_FORMAT_PKCS8) + function getPublicKey($type = 'PKCS8') { + if ($type[0] != '\\') { + $type = '\phpseclib\Crypt\RSA\\' . $type; + } + if (!class_exists($type) || !method_exists($type, 'savePublicKey')) { + return false; + } + if (empty($this->modulus) || empty($this->publicExponent)) { return false; } $oldFormat = $this->publicKeyFormat; $this->publicKeyFormat = $type; - $temp = $this->_convertPublicKey($this->modulus, $this->publicExponent); + $temp = $type::savePublicKey($this->modulus, $this->publicExponent); $this->publicKeyFormat = $oldFormat; return $temp; } @@ -1789,29 +990,6 @@ class RSA } } - /** - * Returns the private key - * - * The private key is only returned if the currently loaded key contains the constituent prime numbers. - * - * @see getPublicKey() - * @access public - * @param string $key - * @param int $type optional - */ - function getPrivateKey($type = self::PUBLIC_FORMAT_PKCS1) - { - if (empty($this->primes)) { - return false; - } - - $oldFormat = $this->privateKeyFormat; - $this->privateKeyFormat = $type; - $temp = $this->_convertPrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients); - $this->privateKeyFormat = $oldFormat; - return $temp; - } - /** * Returns a minimalistic private key * @@ -1820,22 +998,29 @@ class RSA * * @see getPrivateKey() * @access private - * @param string $key - * @param int $type optional + * @param string $type optional */ - function _getPrivatePublicKey($mode = self::PUBLIC_FORMAT_PKCS8) + function _getPrivatePublicKey($type = 'PKCS8') { + if ($type[0] != '\\') { + $type = '\phpseclib\Crypt\RSA\\' . $type; + } + if (!class_exists($type) || !method_exists($type, 'savePublicKey')) { + return false; + } + if (empty($this->modulus) || empty($this->exponent)) { return false; } $oldFormat = $this->publicKeyFormat; - $this->publicKeyFormat = $mode; - $temp = $this->_convertPublicKey($this->modulus, $this->exponent); + $this->publicKeyFormat = $type; + $temp = $type::savePublicKey($this->modulus, $this->exponent); $this->publicKeyFormat = $oldFormat; return $temp; } + /** * __toString() magic method * @@ -1859,7 +1044,7 @@ class RSA function __clone() { $key = new RSA(); - $key->loadKey($this); + $key->load($this); return $key; } @@ -1870,7 +1055,7 @@ class RSA * @param int $bits * @return array */ - function _generateMinMax($bits) + static function _generateMinMax($bits) { $bytes = $bits >> 3; $min = str_repeat(chr(0), $bytes); @@ -2051,14 +1236,14 @@ class RSA * @access private * @param \phpseclib\Math\BigInteger $x * @param int $xLen + * @throws \OutOfBoundsException if strlen($x) > $xLen * @return string */ function _i2osp($x, $xLen) { $x = $x->toBytes(); if (strlen($x) > $xLen) { - user_error('Integer too large'); - return false; + throw new \OutOfBoundsException('Integer too large'); } return str_pad($x, $xLen, chr(0), STR_PAD_LEFT); } @@ -2213,13 +1398,13 @@ class RSA * * @access private * @param \phpseclib\Math\BigInteger $m + * @throws \OutOfRangeException if $m < 0 or $m > $this->modulus * @return \phpseclib\Math\BigInteger */ function _rsaep($m) { - if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) { - user_error('Message representative out of range'); - return false; + if ($m->compare(self::$zero) < 0 || $m->compare($this->modulus) > 0) { + throw new \OutOfRangeException('Message representative out of range'); } return $this->_exponentiate($m); } @@ -2231,13 +1416,13 @@ class RSA * * @access private * @param \phpseclib\Math\BigInteger $c + * @throws \OutOfRangeException if $c < 0 or $c > $this->modulus * @return \phpseclib\Math\BigInteger */ function _rsadp($c) { - if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) { - user_error('Ciphertext representative out of range'); - return false; + if ($c->compare(self::$zero) < 0 || $c->compare($this->modulus) > 0) { + throw new \OutOfRangeException('Ciphertext representative out of range'); } return $this->_exponentiate($c); } @@ -2249,13 +1434,13 @@ class RSA * * @access private * @param \phpseclib\Math\BigInteger $m + * @throws \OutOfRangeException if $m < 0 or $m > $this->modulus * @return \phpseclib\Math\BigInteger */ function _rsasp1($m) { - if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) { - user_error('Message representative out of range'); - return false; + if ($m->compare(self::$zero) < 0 || $m->compare($this->modulus) > 0) { + throw new \OutOfRangeException('Message representative out of range'); } return $this->_exponentiate($m); } @@ -2267,13 +1452,13 @@ class RSA * * @access private * @param \phpseclib\Math\BigInteger $s + * @throws \OutOfRangeException if $s < 0 or $s > $this->modulus * @return \phpseclib\Math\BigInteger */ function _rsavp1($s) { - if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) { - user_error('Signature representative out of range'); - return false; + if ($s->compare(self::$zero) < 0 || $s->compare($this->modulus) > 0) { + throw new \OutOfRangeException('Signature representative out of range'); } return $this->_exponentiate($s); } @@ -2311,6 +1496,7 @@ class RSA * @access private * @param string $m * @param string $l + * @throws \OutOfBoundsException if strlen($m) > $this->k - 2 * $this->hLen - 2 * @return string */ function _rsaes_oaep_encrypt($m, $l = '') @@ -2323,8 +1509,7 @@ class RSA // be output. if ($mLen > $this->k - 2 * $this->hLen - 2) { - user_error('Message too long'); - return false; + throw new \OutOfBoundsException('Message too long'); } // EME-OAEP encoding @@ -2374,6 +1559,7 @@ class RSA * @access private * @param string $c * @param string $l + * @throws \RuntimeException on decryption error * @return string */ function _rsaes_oaep_decrypt($c, $l = '') @@ -2384,8 +1570,7 @@ class RSA // be output. if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) { - user_error('Decryption error'); - return false; + throw new \RuntimeException('Decryption error'); } // RSA decryption @@ -2393,8 +1578,7 @@ class RSA $c = $this->_os2ip($c); $m = $this->_rsadp($c); if ($m === false) { - user_error('Decryption error'); - return false; + throw new \RuntimeException('Decryption error'); } $em = $this->_i2osp($m, $this->k); @@ -2411,13 +1595,11 @@ class RSA $lHash2 = substr($db, 0, $this->hLen); $m = substr($db, $this->hLen); if ($lHash != $lHash2) { - user_error('Decryption error'); - return false; + throw new \RuntimeException('Decryption error'); } $m = ltrim($m, chr(0)); if (ord($m[0]) != 1) { - user_error('Decryption error'); - return false; + throw new \RuntimeException('Decryption error'); } // Output the message M @@ -2448,6 +1630,7 @@ class RSA * * @access private * @param string $m + * @throws \OutOfBoundsException if strlen($m) > $this->k - 11 * @return string */ function _rsaes_pkcs1_v1_5_encrypt($m) @@ -2457,8 +1640,7 @@ class RSA // Length checking if ($mLen > $this->k - 11) { - user_error('Message too long'); - return false; + throw new \OutOfBoundsException('Message too long'); } // EME-PKCS1-v1_5 encoding @@ -2507,6 +1689,7 @@ class RSA * * @access private * @param string $c + * @throws \RuntimeException on decryption error * @return string */ function _rsaes_pkcs1_v1_5_decrypt($c) @@ -2514,8 +1697,7 @@ class RSA // Length checking if (strlen($c) != $this->k) { // or if k < 11 - user_error('Decryption error'); - return false; + throw new \RuntimeException('Decryption error'); } // RSA decryption @@ -2524,24 +1706,21 @@ class RSA $m = $this->_rsadp($c); if ($m === false) { - user_error('Decryption error'); - return false; + throw new \RuntimeException('Decryption error'); } $em = $this->_i2osp($m, $this->k); // EME-PKCS1-v1_5 decoding if (ord($em[0]) != 0 || ord($em[1]) > 2) { - user_error('Decryption error'); - return false; + throw new \RuntimeException('Decryption error'); } $ps = substr($em, 2, strpos($em, chr(0), 2) - 2); $m = substr($em, strlen($ps) + 3); if (strlen($ps) < 8) { - user_error('Decryption error'); - return false; + throw new \RuntimeException('Decryption error'); } // Output M @@ -2556,6 +1735,7 @@ class RSA * * @access private * @param string $m + * @throws \RuntimeException on encoding error * @param int $emBits */ function _emsa_pss_encode($m, $emBits) @@ -2568,8 +1748,7 @@ class RSA $mHash = $this->hash->hash($m); if ($emLen < $this->hLen + $sLen + 2) { - user_error('Encoding error'); - return false; + throw new \RuntimeException('Encoding error'); } $salt = Random::string($sLen); @@ -2666,6 +1845,7 @@ class RSA * @access private * @param string $m * @param string $s + * @throws \RuntimeException on invalid signature * @return string */ function _rsassa_pss_verify($m, $s) @@ -2673,8 +1853,7 @@ class RSA // Length checking if (strlen($s) != $this->k) { - user_error('Invalid signature'); - return false; + throw new \RuntimeException('Invalid signature'); } // RSA verification @@ -2684,13 +1863,11 @@ class RSA $s2 = $this->_os2ip($s); $m2 = $this->_rsavp1($s2); if ($m2 === false) { - user_error('Invalid signature'); - return false; + throw new \RuntimeException('Invalid signature'); } $em = $this->_i2osp($m2, $modBits >> 3); if ($em === false) { - user_error('Invalid signature'); - return false; + throw new \RuntimeException('Invalid signature'); } // EMSA-PSS verification @@ -2706,6 +1883,7 @@ class RSA * @access private * @param string $m * @param int $emLen + * @throws \LengthException if the intended encoded message length is too short * @return string */ function _emsa_pkcs1_v1_5_encode($m, $emLen) @@ -2739,8 +1917,7 @@ class RSA $tLen = strlen($t); if ($emLen < $tLen + 11) { - user_error('Intended encoded message length too short'); - return false; + throw new \LengthException('Intended encoded message length too short'); } $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3); @@ -2757,6 +1934,7 @@ class RSA * * @access private * @param string $m + * @throws \LengthException if the RSA modulus is too short * @return string */ function _rsassa_pkcs1_v1_5_sign($m) @@ -2765,8 +1943,7 @@ class RSA $em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); if ($em === false) { - user_error('RSA modulus too short'); - return false; + throw new \LengthException('RSA modulus too short'); } // RSA signature @@ -2787,6 +1964,8 @@ class RSA * * @access private * @param string $m + * @throws \RuntimeException if the signature is invalid + * @throws \LengthException if the RSA modulus is too short * @return string */ function _rsassa_pkcs1_v1_5_verify($m, $s) @@ -2794,8 +1973,7 @@ class RSA // Length checking if (strlen($s) != $this->k) { - user_error('Invalid signature'); - return false; + throw new \RuntimeException('Invalid signature'); } // RSA verification @@ -2803,21 +1981,18 @@ class RSA $s = $this->_os2ip($s); $m2 = $this->_rsavp1($s); if ($m2 === false) { - user_error('Invalid signature'); - return false; + throw new \RuntimeException('Invalid signature'); } $em = $this->_i2osp($m2, $this->k); if ($em === false) { - user_error('Invalid signature'); - return false; + throw new \RuntimeException('Invalid signature'); } // EMSA-PKCS1-v1_5 encoding $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); if ($em2 === false) { - user_error('RSA modulus too short'); - return false; + throw new \LengthException('RSA modulus too short'); } // Compare @@ -3010,31 +2185,4 @@ class RSA return $this->_rsassa_pss_verify($message, $signature); } } - - /** - * Extract raw BER from Base64 encoding - * - * @access private - * @param string $str - * @return string - */ - function _extractBER($str) - { - /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them - * above and beyond the ceritificate. - * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line: - * - * Bag Attributes - * localKeyID: 01 00 00 00 - * subject=/O=organization/OU=org unit/CN=common name - * issuer=/O=organization/CN=common name - */ - $temp = preg_replace('#.*?^-+[^-]+-+#ms', '', $str, 1); - // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff - $temp = preg_replace('#-+[^-]+-+#', '', $temp); - // remove new lines - $temp = str_replace(array("\r", "\n", ' '), '', $temp); - $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; - return $temp != false ? $temp : $str; - } } diff --git a/phpseclib/Crypt/RSA/OpenSSH.php b/phpseclib/Crypt/RSA/OpenSSH.php new file mode 100644 index 00000000..a52f7bab --- /dev/null +++ b/phpseclib/Crypt/RSA/OpenSSH.php @@ -0,0 +1,142 @@ + + * @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\Math\BigInteger; + +/** + * XML Formatted RSA Key Handler + * + * @package RSA + * @author Jim Wigginton + * @access public + */ +class OpenSSH +{ + /** + * Default comment + * + * @var string + * @access private + */ + static $comment = 'phpseclib-generated-key'; + + /** + * Sets the default comment + * + * @access public + * @param string $comment + */ + static function setEncryptionAlgorithm($comment) + { + self::$comment = $comment; + } + + /** + * Break a public or private key down into its constituent components + * + * @access public + * @param string $key + * @param string $password optional + * @return array + */ + static function load($key, $password = '') + { + $parts = explode(' ', $key, 3); + + $key = isset($parts[1]) ? base64_decode($parts[1]) : false; + if ($key === false) { + return false; + } + + $comment = isset($parts[2]) ? $parts[2] : false; + + $cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa"; + + if (strlen($key) <= 4) { + return false; + } + extract(unpack('Nlength', self::_string_shift($key, 4))); + $publicExponent = new BigInteger(self::_string_shift($key, $length), -256); + if (strlen($key) <= 4) { + return false; + } + extract(unpack('Nlength', self::_string_shift($key, 4))); + $modulus = new BigInteger(self::_string_shift($key, $length), -256); + + if ($cleanup && strlen($key)) { + if (strlen($key) <= 4) { + return false; + } + extract(unpack('Nlength', self::_string_shift($key, 4))); + $realModulus = new BigInteger(self::_string_shift($key, $length), -256); + return strlen($key) ? false : array( + 'isPublicKey' => true, + 'modulus' => $realModulus, + 'publicExponent' => $modulus, + 'comment' => $comment + ); + } else { + return strlen($key) ? false : array( + 'isPublicKey' => true, + 'modulus' => $modulus, + 'publicExponent' => $publicExponent, + 'comment' => $comment + ); + } + } + + /** + * Convert a public key to the appropriate format + * + * @access public + * @param \phpseclib\Math\BigInteger $n + * @param \phpseclib\Math\BigInteger $e + * @return string + */ + function savePublicKey(BigInteger $n, BigInteger $e) + { + $publicExponent = $e->toBytes(true); + $modulus = $n->toBytes(true); + + // from : + // string "ssh-rsa" + // mpint e + // mpint n + $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus); + $RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . self::$comment; + + return $RSAPublicKey; + } + + /** + * String Shift + * + * Inspired by array_shift + * + * @param string $string + * @param int $index + * @return string + * @access private + */ + static function _string_shift(&$string, $index = 1) + { + $substr = substr($string, 0, $index); + $string = substr($string, $index); + return $substr; + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/RSA/PKCS.php b/phpseclib/Crypt/RSA/PKCS.php new file mode 100644 index 00000000..eaf8e797 --- /dev/null +++ b/phpseclib/Crypt/RSA/PKCS.php @@ -0,0 +1,415 @@ + + * @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\Base; +use phpseclib\Crypt\AES; +use phpseclib\Crypt\TripleDES; +use phpseclib\Crypt\DES; +use phpseclib\Math\BigInteger; + +/** + * PKCS Formatted RSA Key Handler + * + * @package RSA + * @author Jim Wigginton + * @access public + */ +abstract class PKCS +{ + /**#@+ + * @access private + * @see \phpseclib\Crypt\RSA::createKey() + */ + /** + * ASN1 Integer + */ + const ASN1_INTEGER = 2; + /** + * ASN1 Bit String + */ + const ASN1_BITSTRING = 3; + /** + * ASN1 Octet String + */ + const ASN1_OCTETSTRING = 4; + /** + * ASN1 Object Identifier + */ + const ASN1_OBJECT = 6; + /** + * ASN1 Sequence (with the constucted bit set) + */ + const ASN1_SEQUENCE = 48; + /**#@-*/ + + /** + * Returns the mode constant corresponding to the mode string + * + * @access public + * @param string $mode + * @return int + * @throws \UnexpectedValueException if the block cipher mode is unsupported + */ + static function getEncryptionMode($mode) + { + switch ($mode) { + case 'CBC': + return Base::MODE_CBC; + case 'ECB': + return Base::MODE_ECB; + case 'CFB': + return Base::MODE_CFB; + case 'OFB': + return Base::MODE_OFB; + case 'CTR': + return Base::MODE_CTR; + } + throw new \UnexpectedValueException('Unsupported block cipher mode of operation'); + } + + /** + * Returns a cipher object corresponding to a string + * + * @access public + * @param string $algo + * @return string + * @throws \UnexpectedValueException if the encryption algorithm is unsupported + */ + static function getEncryptionObject($algo) + { + $modes = '(CBC|ECB|CFB|OFB|CTR)'; + switch (true) { + case preg_match("#^AES-(128|192|256)-$modes$#", $algo, $matches): + $cipher = new AES(self::getEncryptionMode($matches[2])); + $cipher->setKeyLength($matches[1]); + return $cipher; + case preg_match("#^DES-EDE3-$modes$#", $algo, $matches): + return new TripleDES(self::getEncryptionMode($matches[1])); + case preg_match("#^DES-$modes$#", $algo, $matches): + return new DES(self::getEncryptionMode($matches[1])); + default: + throw new \UnexpectedValueException('Unsupported encryption algorithmn'); + } + } + + /** + * Generate a symmetric key for PKCS#1 keys + * + * @access public + * @param string $password + * @param string $iv + * @param int $length + * @return string + */ + static function generateSymmetricKey($password, $iv, $length) + { + $symkey = ''; + $iv = substr($iv, 0, 8); + while (strlen($symkey) < $length) { + $symkey.= pack('H*', md5($symkey . $password . $iv)); + } + return substr($symkey, 0, $length); + } + + /** + * Break a public or private key down into its constituent components + * + * @access public + * @param string $key + * @param string $password optional + * @return array + */ + static function load($key, $password = '') + { + $components = array('isPublicKey' => strpos($key, 'PUBLIC') !== false); + + /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is + "outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to + protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding + two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here: + + http://tools.ietf.org/html/rfc1421#section-4.6.1.1 + http://tools.ietf.org/html/rfc1421#section-4.6.1.3 + + DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell. + DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation + function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's + own implementation. ie. the implementation *is* the standard and any bugs that may exist in that + implementation are part of the standard, as well. + + * OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */ + if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) { + $iv = pack('H*', trim($matches[2])); + // remove the Proc-Type / DEK-Info sections as they're no longer needed + $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key); + $ciphertext = self::_extractBER($key); + if ($ciphertext === false) { + $ciphertext = $key; + } + $crypto = self::getEncryptionObject($matches[1]); + $crypto->setKey(self::generateSymmetricKey($password, $iv, $crypto->getKeyLength() >> 3)); + $crypto->setIV($iv); + $decoded = $crypto->decrypt($ciphertext); + } else { + $decoded = self::_extractBER($key); + } + + if ($decoded !== false) { + $key = $decoded; + } + + if (ord(self::_string_shift($key)) != self::ASN1_SEQUENCE) { + return false; + } + if (self::_decodeLength($key) != strlen($key)) { + return false; + } + + $tag = ord(self::_string_shift($key)); + /* intended for keys for which OpenSSL's asn1parse returns the following: + + 0:d=0 hl=4 l= 631 cons: SEQUENCE + 4:d=1 hl=2 l= 1 prim: INTEGER :00 + 7:d=1 hl=2 l= 13 cons: SEQUENCE + 9:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 20:d=2 hl=2 l= 0 prim: NULL + 22:d=1 hl=4 l= 609 prim: OCTET STRING + + ie. PKCS8 keys */ + + if ($tag == self::ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") { + self::_string_shift($key, 3); + $tag = self::ASN1_SEQUENCE; + } + + if ($tag == self::ASN1_SEQUENCE) { + $temp = self::_string_shift($key, self::_decodeLength($key)); + if (ord(self::_string_shift($temp)) != self::ASN1_OBJECT) { + return false; + } + $length = self::_decodeLength($temp); + switch (self::_string_shift($temp, $length)) { + case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption + break; + case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC + /* + PBEParameter ::= SEQUENCE { + salt OCTET STRING (SIZE(8)), + iterationCount INTEGER } + */ + if (ord(self::_string_shift($temp)) != self::ASN1_SEQUENCE) { + return false; + } + if (self::_decodeLength($temp) != strlen($temp)) { + return false; + } + self::_string_shift($temp); // assume it's an octet string + $salt = self::_string_shift($temp, self::_decodeLength($temp)); + if (ord(self::_string_shift($temp)) != self::ASN1_INTEGER) { + return false; + } + self::_decodeLength($temp); + list(, $iterationCount) = unpack('N', str_pad($temp, 4, chr(0), STR_PAD_LEFT)); + self::_string_shift($key); // assume it's an octet string + $length = self::_decodeLength($key); + if (strlen($key) != $length) { + return false; + } + + $crypto = new DES(); + $crypto->setPassword($password, 'pbkdf1', 'md5', $salt, $iterationCount); + $key = $crypto->decrypt($key); + if ($key === false) { + return false; + } + return self::load($key); + default: + return false; + } + /* intended for keys for which OpenSSL's asn1parse returns the following: + + 0:d=0 hl=4 l= 290 cons: SEQUENCE + 4:d=1 hl=2 l= 13 cons: SEQUENCE + 6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 17:d=2 hl=2 l= 0 prim: NULL + 19:d=1 hl=4 l= 271 prim: BIT STRING */ + $tag = ord(self::_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag + self::_decodeLength($key); // skip over the BIT STRING / OCTET STRING length + // "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of + // unused bits in the final subsequent octet. The number shall be in the range zero to seven." + // -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2) + if ($tag == self::ASN1_BITSTRING) { + self::_string_shift($key); + } + if (ord(self::_string_shift($key)) != self::ASN1_SEQUENCE) { + return false; + } + if (self::_decodeLength($key) != strlen($key)) { + return false; + } + $tag = ord(self::_string_shift($key)); + } + if ($tag != self::ASN1_INTEGER) { + return false; + } + + $length = self::_decodeLength($key); + $temp = self::_string_shift($key, $length); + if (strlen($temp) != 1 || ord($temp) > 2) { + $components['modulus'] = new BigInteger($temp, 256); + self::_string_shift($key); // skip over self::ASN1_INTEGER + $length = self::_decodeLength($key); + $components[$components['isPublicKey'] ? 'publicExponent' : 'privateExponent'] = new BigInteger(self::_string_shift($key, $length), 256); + + return $components; + } + if (ord(self::_string_shift($key)) != self::ASN1_INTEGER) { + return false; + } + $length = self::_decodeLength($key); + $components['modulus'] = new BigInteger(self::_string_shift($key, $length), 256); + self::_string_shift($key); + $length = self::_decodeLength($key); + $components['publicExponent'] = new BigInteger(self::_string_shift($key, $length), 256); + self::_string_shift($key); + $length = self::_decodeLength($key); + $components['privateExponent'] = new BigInteger(self::_string_shift($key, $length), 256); + self::_string_shift($key); + $length = self::_decodeLength($key); + $components['primes'] = array(1 => new BigInteger(self::_string_shift($key, $length), 256)); + self::_string_shift($key); + $length = self::_decodeLength($key); + $components['primes'][] = new BigInteger(self::_string_shift($key, $length), 256); + self::_string_shift($key); + $length = self::_decodeLength($key); + $components['exponents'] = array(1 => new BigInteger(self::_string_shift($key, $length), 256)); + self::_string_shift($key); + $length = self::_decodeLength($key); + $components['exponents'][] = new BigInteger(self::_string_shift($key, $length), 256); + self::_string_shift($key); + $length = self::_decodeLength($key); + $components['coefficients'] = array(2 => new BigInteger(self::_string_shift($key, $length), 256)); + + if (!empty($key)) { + if (ord(self::_string_shift($key)) != self::ASN1_SEQUENCE) { + return false; + } + self::_decodeLength($key); + while (!empty($key)) { + if (ord(self::_string_shift($key)) != self::ASN1_SEQUENCE) { + return false; + } + self::_decodeLength($key); + $key = substr($key, 1); + $length = self::_decodeLength($key); + $components['primes'][] = new BigInteger(self::_string_shift($key, $length), 256); + self::_string_shift($key); + $length = self::_decodeLength($key); + $components['exponents'][] = new BigInteger(self::_string_shift($key, $length), 256); + self::_string_shift($key); + $length = self::_decodeLength($key); + $components['coefficients'][] = new BigInteger(self::_string_shift($key, $length), 256); + } + } + + return $components; + } + + /** + * DER-decode the length + * + * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See + * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. + * + * @access private + * @param string $string + * @return int + */ + static function _decodeLength(&$string) + { + $length = ord(self::_string_shift($string)); + if ($length & 0x80) { // definite length, long form + $length&= 0x7F; + $temp = self::_string_shift($string, $length); + list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4)); + } + return $length; + } + + /** + * DER-encode the length + * + * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See + * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. + * + * @access private + * @param int $length + * @return string + */ + static function _encodeLength($length) + { + if ($length <= 0x7F) { + return chr($length); + } + + $temp = ltrim(pack('N', $length), chr(0)); + return pack('Ca*', 0x80 | strlen($temp), $temp); + } + + /** + * String Shift + * + * Inspired by array_shift + * + * @param string $string + * @param int $index + * @return string + * @access private + */ + static function _string_shift(&$string, $index = 1) + { + $substr = substr($string, 0, $index); + $string = substr($string, $index); + return $substr; + } + + /** + * Extract raw BER from Base64 encoding + * + * @access private + * @param string $str + * @return string + */ + static function _extractBER($str) + { + /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them + * above and beyond the ceritificate. + * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line: + * + * Bag Attributes + * localKeyID: 01 00 00 00 + * subject=/O=organization/OU=org unit/CN=common name + * issuer=/O=organization/CN=common name + */ + $temp = preg_replace('#.*?^-+[^-]+-+#ms', '', $str, 1); + // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff + $temp = preg_replace('#-+[^-]+-+#', '', $temp); + // remove new lines + $temp = str_replace(array("\r", "\n", ' '), '', $temp); + $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; + return $temp != false ? $temp : $str; + } +} diff --git a/phpseclib/Crypt/RSA/PKCS1.php b/phpseclib/Crypt/RSA/PKCS1.php new file mode 100644 index 00000000..454c3dfa --- /dev/null +++ b/phpseclib/Crypt/RSA/PKCS1.php @@ -0,0 +1,173 @@ + + * @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\Math\BigInteger; +use phpseclib\Crypt\RSA\PKCS; +use phpseclib\Crypt\Random; +use phpseclib\Crypt\AES; +use phpseclib\Crypt\TripleDES; +use phpseclib\Crypt\DES; + +/** + * PKCS#1 Formatted RSA Key Handler + * + * @package RSA + * @author Jim Wigginton + * @access public + */ +class PKCS1 extends PKCS +{ + /** + * Default encryption algorithm + * + * @var string + * @access private + */ + static $defaultEncryptionAlgorithm = 'DES-EDE3-CBC'; + + /** + * Sets the default encryption algorithm + * + * @access public + * @param string $algo + */ + static function setEncryptionAlgorithm($algo) + { + self::$defaultEncryptionAlgorithm = $algo; + } + + /** + * Convert a private key to the appropriate format. + * + * @access public + * @param \phpseclib\Math\BigInteger $n + * @param \phpseclib\Math\BigInteger $e + * @param \phpseclib\Math\BigInteger $d + * @param array $primes + * @param array $exponents + * @param array $coefficients + * @param string $password optional + * @return string + */ + static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, $primes, $exponents, $coefficients, $password = '') + { + $num_primes = count($primes); + $raw = array( + 'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi + 'modulus' => $n->toBytes(true), + 'publicExponent' => $e->toBytes(true), + 'privateExponent' => $d->toBytes(true), + 'prime1' => $primes[1]->toBytes(true), + 'prime2' => $primes[2]->toBytes(true), + 'exponent1' => $exponents[1]->toBytes(true), + 'exponent2' => $exponents[2]->toBytes(true), + 'coefficient' => $coefficients[2]->toBytes(true) + ); + + $components = array(); + foreach ($raw as $name => $value) { + $components[$name] = pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($value)), $value); + } + + $RSAPrivateKey = implode('', $components); + + if ($num_primes > 2) { + $OtherPrimeInfos = ''; + for ($i = 3; $i <= $num_primes; $i++) { + // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo + // + // OtherPrimeInfo ::= SEQUENCE { + // prime INTEGER, -- ri + // exponent INTEGER, -- di + // coefficient INTEGER -- ti + // } + $OtherPrimeInfo = pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true)); + $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true)); + $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true)); + $OtherPrimeInfos.= pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo); + } + $RSAPrivateKey.= pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos); + } + + $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); + + if (!empty($password) || is_string($password)) { + $cipher = self::getEncryptionObject(self::$defaultEncryptionAlgorithm); + $iv = Random::string($cipher->getBlockLength() >> 3); + $cipher->setKey(self::generateSymmetricKey($password, $iv, $cipher->getKeyLength())); + $cipher->setIV($iv); + $iv = strtoupper(bin2hex($iv)); + $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" . + "Proc-Type: 4,ENCRYPTED\r\n" . + "DEK-Info: " . self::$defaultEncryptionAlgorithm . ",$iv\r\n" . + "\r\n" . + chunk_split(base64_encode($cipher->encrypt($RSAPrivateKey)), 64) . + '-----END RSA PRIVATE KEY-----'; + } else { + $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" . + chunk_split(base64_encode($RSAPrivateKey), 64) . + '-----END RSA PRIVATE KEY-----'; + } + + return $RSAPrivateKey; + } + + /** + * Convert a public key to the appropriate format + * + * @access public + * @param \phpseclib\Math\BigInteger $n + * @param \phpseclib\Math\BigInteger $e + * @return string + */ + static function savePublicKey(BigInteger $n, BigInteger $e) + { + $modulus = $n->toBytes(true); + $publicExponent = $e->toBytes(true); + + // from : + // RSAPublicKey ::= SEQUENCE { + // modulus INTEGER, -- n + // publicExponent INTEGER -- e + // } + $components = array( + 'modulus' => pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($modulus)), $modulus), + 'publicExponent' => pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($publicExponent)), $publicExponent) + ); + + $RSAPublicKey = pack( + 'Ca*a*a*', + self::ASN1_SEQUENCE, + self::_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])), + $components['modulus'], + $components['publicExponent'] + ); + + $RSAPublicKey = "-----BEGIN RSA PUBLIC KEY-----\r\n" . + chunk_split(base64_encode($RSAPublicKey), 64) . + '-----END RSA PUBLIC KEY-----'; + + return $RSAPublicKey; + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/RSA/PKCS8.php b/phpseclib/Crypt/RSA/PKCS8.php new file mode 100644 index 00000000..aa35409a --- /dev/null +++ b/phpseclib/Crypt/RSA/PKCS8.php @@ -0,0 +1,209 @@ + + * @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\Math\BigInteger; +use phpseclib\Crypt\RSA\PKCS; +use phpseclib\Crypt\Random; +use phpseclib\Crypt\DES; + +/** + * PKCS#8 Formatted RSA Key Handler + * + * @package RSA + * @author Jim Wigginton + * @access public + */ +class PKCS8 extends PKCS +{ + /** + * Convert a private key to the appropriate format. + * + * @access public + * @param \phpseclib\Math\BigInteger $n + * @param \phpseclib\Math\BigInteger $e + * @param \phpseclib\Math\BigInteger $d + * @param array $primes + * @param array $exponents + * @param array $coefficients + * @param string $password optional + * @return string + */ + static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, $primes, $exponents, $coefficients, $password = '') + { + $num_primes = count($primes); + $raw = array( + 'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi + 'modulus' => $n->toBytes(true), + 'publicExponent' => $e->toBytes(true), + 'privateExponent' => $d->toBytes(true), + 'prime1' => $primes[1]->toBytes(true), + 'prime2' => $primes[2]->toBytes(true), + 'exponent1' => $exponents[1]->toBytes(true), + 'exponent2' => $exponents[2]->toBytes(true), + 'coefficient' => $coefficients[2]->toBytes(true) + ); + + $components = array(); + foreach ($raw as $name => $value) { + $components[$name] = pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($value)), $value); + } + + $RSAPrivateKey = implode('', $components); + + if ($num_primes > 2) { + $OtherPrimeInfos = ''; + for ($i = 3; $i <= $num_primes; $i++) { + // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo + // + // OtherPrimeInfo ::= SEQUENCE { + // prime INTEGER, -- ri + // exponent INTEGER, -- di + // coefficient INTEGER -- ti + // } + $OtherPrimeInfo = pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true)); + $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true)); + $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true)); + $OtherPrimeInfos.= pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo); + } + $RSAPrivateKey.= pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos); + } + + $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); + + $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA + $RSAPrivateKey = pack( + 'Ca*a*Ca*a*', + self::ASN1_INTEGER, + "\01\00", + $rsaOID, + 4, + self::_encodeLength(strlen($RSAPrivateKey)), + $RSAPrivateKey + ); + $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); + if (!empty($password) || is_string($password)) { + $salt = Random::string(8); + $iterationCount = 2048; + + $crypto = new DES(); + $crypto->setPassword($password, 'pbkdf1', 'md5', $salt, $iterationCount); + $RSAPrivateKey = $crypto->encrypt($RSAPrivateKey); + + $parameters = pack( + 'Ca*a*Ca*N', + self::ASN1_OCTETSTRING, + self::_encodeLength(strlen($salt)), + $salt, + self::ASN1_INTEGER, + self::_encodeLength(4), + $iterationCount + ); + $pbeWithMD5AndDES_CBC = "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03"; + + $encryptionAlgorithm = pack( + 'Ca*a*Ca*a*', + self::ASN1_OBJECT, + self::_encodeLength(strlen($pbeWithMD5AndDES_CBC)), + $pbeWithMD5AndDES_CBC, + self::ASN1_SEQUENCE, + self::_encodeLength(strlen($parameters)), + $parameters + ); + + $RSAPrivateKey = pack( + 'Ca*a*Ca*a*', + self::ASN1_SEQUENCE, + self::_encodeLength(strlen($encryptionAlgorithm)), + $encryptionAlgorithm, + self::ASN1_OCTETSTRING, + self::_encodeLength(strlen($RSAPrivateKey)), + $RSAPrivateKey + ); + + $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); + + $RSAPrivateKey = "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" . + chunk_split(base64_encode($RSAPrivateKey), 64) . + '-----END ENCRYPTED PRIVATE KEY-----'; + } else { + $RSAPrivateKey = "-----BEGIN PRIVATE KEY-----\r\n" . + chunk_split(base64_encode($RSAPrivateKey), 64) . + '-----END PRIVATE KEY-----'; + } + + return $RSAPrivateKey; + } + + /** + * Convert a public key to the appropriate format + * + * @access public + * @param \phpseclib\Math\BigInteger $n + * @param \phpseclib\Math\BigInteger $e + * @return string + */ + static function savePublicKey(BigInteger $n, BigInteger $e) + { + $modulus = $n->toBytes(true); + $publicExponent = $e->toBytes(true); + + // from : + // RSAPublicKey ::= SEQUENCE { + // modulus INTEGER, -- n + // publicExponent INTEGER -- e + // } + $components = array( + 'modulus' => pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($modulus)), $modulus), + 'publicExponent' => pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($publicExponent)), $publicExponent) + ); + + $RSAPublicKey = pack( + 'Ca*a*a*', + self::ASN1_SEQUENCE, + self::_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])), + $components['modulus'], + $components['publicExponent'] + ); + + // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption. + $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA + $RSAPublicKey = chr(0) . $RSAPublicKey; + $RSAPublicKey = chr(3) . self::_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey; + + $RSAPublicKey = pack( + 'Ca*a*', + self::ASN1_SEQUENCE, + self::_encodeLength(strlen($rsaOID . $RSAPublicKey)), + $rsaOID . $RSAPublicKey + ); + + $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" . + chunk_split(base64_encode($RSAPublicKey), 64) . + '-----END PUBLIC KEY-----'; + + return $RSAPublicKey; + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/RSA/PuTTY.php b/phpseclib/Crypt/RSA/PuTTY.php new file mode 100644 index 00000000..95734fc1 --- /dev/null +++ b/phpseclib/Crypt/RSA/PuTTY.php @@ -0,0 +1,252 @@ + + * @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\Math\BigInteger; +use phpseclib\Crypt\AES; + +/** + * PuTTY Formatted RSA Key Handler + * + * @package RSA + * @author Jim Wigginton + * @access public + */ +class PuTTY +{ + /** + * Default comment + * + * @var string + * @access private + */ + static $comment = 'phpseclib-generated-key'; + + /** + * Sets the default comment + * + * @access public + * @param string $comment + */ + static function setEncryptionAlgorithm($comment) + { + self::$comment = $comment; + } + + /** + * Generate a symmetric key for PuTTY keys + * + * @access public + * @param string $password + * @param string $iv + * @param int $length + * @return string + */ + static function generateSymmetricKey($password, $length) + { + $symkey = ''; + $sequence = 0; + while (strlen($symkey) < $length) { + $temp = pack('Na*', $sequence++, $password); + $symkey.= pack('H*', sha1($temp)); + } + return substr($symkey, 0, $length); + } + + /** + * Break a public or private key down into its constituent components + * + * @access public + * @param string $key + * @param string $password optional + * @return array + */ + static function load($key, $password = '') + { + $components = array('isPublicKey' => false); + $key = preg_split('#\r\n|\r|\n#', $key); + $type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0])); + if ($type != 'ssh-rsa') { + return false; + } + $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1])); + $comment = trim(preg_replace('#Comment: (.+)#', '$1', $key[2])); + + $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3])); + $public = base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength)))); + $public = substr($public, 11); + extract(unpack('Nlength', self::_string_shift($public, 4))); + $components['publicExponent'] = new BigInteger(self::_string_shift($public, $length), -256); + extract(unpack('Nlength', self::_string_shift($public, 4))); + $components['modulus'] = new BigInteger(self::_string_shift($public, $length), -256); + + $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$publicLength + 4])); + $private = base64_decode(implode('', array_map('trim', array_slice($key, $publicLength + 5, $privateLength)))); + + switch ($encryption) { + case 'aes256-cbc': + $symkey = static::generateSymmetricKey($password, 32); + $crypto = new AES(); + } + + if ($encryption != 'none') { + $crypto->setKey($symkey); + $crypto->disablePadding(); + $private = $crypto->decrypt($private); + if ($private === false) { + return false; + } + } + + extract(unpack('Nlength', self::_string_shift($private, 4))); + if (strlen($private) < $length) { + return false; + } + $components['privateExponent'] = new BigInteger(self::_string_shift($private, $length), -256); + extract(unpack('Nlength', self::_string_shift($private, 4))); + if (strlen($private) < $length) { + return false; + } + $components['primes'] = array(1 => new BigInteger(self::_string_shift($private, $length), -256)); + extract(unpack('Nlength', self::_string_shift($private, 4))); + if (strlen($private) < $length) { + return false; + } + $components['primes'][] = new BigInteger(self::_string_shift($private, $length), -256); + + $temp = $components['primes'][1]->subtract(self::$one); + $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp)); + $temp = $components['primes'][2]->subtract(self::$one); + $components['exponents'][] = $components['publicExponent']->modInverse($temp); + + extract(unpack('Nlength', self::_string_shift($private, 4))); + if (strlen($private) < $length) { + return false; + } + $components['coefficients'] = array(2 => new BigInteger(self::_string_shift($private, $length), -256)); + + return $components; + } + + /** + * String Shift + * + * Inspired by array_shift + * + * @param string $string + * @param int $index + * @return string + * @access private + */ + static function _string_shift(&$string, $index = 1) + { + $substr = substr($string, 0, $index); + $string = substr($string, $index); + return $substr; + } + + /** + * Convert a private key to the appropriate format. + * + * @access public + * @param \phpseclib\Math\BigInteger $n + * @param \phpseclib\Math\BigInteger $e + * @param \phpseclib\Math\BigInteger $d + * @param array $primes + * @param array $exponents + * @param array $coefficients + * @param string $password optional + * @return string + */ + static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, $primes, $exponents, $coefficients, $password = '') + { + if (count($primes) != 2) { + return false; + } + + $raw = array( + 'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi + 'modulus' => $n->toBytes(true), + 'publicExponent' => $e->toBytes(true), + 'privateExponent' => $d->toBytes(true), + 'prime1' => $primes[1]->toBytes(true), + 'prime2' => $primes[2]->toBytes(true), + 'exponent1' => $exponents[1]->toBytes(true), + 'exponent2' => $exponents[2]->toBytes(true), + 'coefficient' => $coefficients[2]->toBytes(true) + ); + + $key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: "; + $encryption = (!empty($password) || is_string($password)) ? 'aes256-cbc' : 'none'; + $key.= $encryption; + $key.= "\r\nComment: " . self::$comment . "\r\n"; + $public = pack( + 'Na*Na*Na*', + strlen('ssh-rsa'), + 'ssh-rsa', + strlen($raw['publicExponent']), + $raw['publicExponent'], + strlen($raw['modulus']), + $raw['modulus'] + ); + $source = pack( + 'Na*Na*Na*Na*', + strlen('ssh-rsa'), + 'ssh-rsa', + strlen($encryption), + $encryption, + strlen(self::$comment), + self::$comment, + strlen($public), + $public + ); + $public = base64_encode($public); + $key.= "Public-Lines: " . ((strlen($public) + 63) >> 6) . "\r\n"; + $key.= chunk_split($public, 64); + $private = pack( + 'Na*Na*Na*Na*', + strlen($raw['privateExponent']), + $raw['privateExponent'], + strlen($raw['prime1']), + $raw['prime1'], + strlen($raw['prime2']), + $raw['prime2'], + strlen($raw['coefficient']), + $raw['coefficient'] + ); + if (empty($password) && !is_string($password)) { + $source.= pack('Na*', strlen($private), $private); + $hashkey = 'putty-private-key-file-mac-key'; + } else { + $private.= Random::string(16 - (strlen($private) & 15)); + $source.= pack('Na*', strlen($private), $private); + $crypto = new AES(); + + $crypto->setKey(static::generateSymmetricKey($password, 32)); + $crypto->disablePadding(); + $private = $crypto->encrypt($private); + $hashkey = 'putty-private-key-file-mac-key' . $password; + } + + $private = base64_encode($private); + $key.= 'Private-Lines: ' . ((strlen($private) + 63) >> 6) . "\r\n"; + $key.= chunk_split($private, 64); + $hash = new Hash('sha1'); + $hash->setKey(pack('H*', sha1($hashkey))); + $key.= 'Private-MAC: ' . bin2hex($hash->hash($source)) . "\r\n"; + + return $key; + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/RSA/Raw.php b/phpseclib/Crypt/RSA/Raw.php new file mode 100644 index 00000000..51cfca6b --- /dev/null +++ b/phpseclib/Crypt/RSA/Raw.php @@ -0,0 +1,93 @@ + + * @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\Math\BigInteger; + +/** + * Raw RSA Key Handler + * + * @package RSA + * @author Jim Wigginton + * @access public + */ +class Raw +{ + /** + * Break a public or private key down into its constituent components + * + * @access public + * @param string $key + * @param string $password optional + * @return array + */ + static function load($key, $password = '') + { + if (!is_array($key)) { + return false; + } + $components = array('isPublicKey' => true); + switch (true) { + case isset($key['e']): + $components['publicExponent'] = $key['e']->copy(); + break; + case isset($key['exponent']): + $components['publicExponent'] = $key['exponent']->copy(); + break; + case isset($key['publicExponent']): + $components['publicExponent'] = $key['publicExponent']->copy(); + break; + case isset($key[0]): + $components['publicExponent'] = $key[0]->copy(); + } + switch (true) { + case isset($key['n']): + $components['modulus'] = $key['n']->copy(); + break; + case isset($key['modulo']): + $components['modulus'] = $key['modulo']->copy(); + break; + case isset($key['modulus']): + $components['modulus'] = $key['modulus']->copy(); + break; + case isset($key[1]): + $components['modulus'] = $key[1]->copy(); + } + return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false; + } + + /** + * Convert a public key to the appropriate format + * + * @access public + * @param \phpseclib\Math\BigInteger $n + * @param \phpseclib\Math\BigInteger $e + * @return string + */ + static function savePublicKey(BigInteger $n, BigInteger $e) + { + return array('e' => clone $e, 'n' => clone $n); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/RSA/XML.php b/phpseclib/Crypt/RSA/XML.php new file mode 100644 index 00000000..f3a57ea7 --- /dev/null +++ b/phpseclib/Crypt/RSA/XML.php @@ -0,0 +1,142 @@ + + * @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\Math\BigInteger; + +/** + * XML Formatted RSA Key Handler + * + * @package RSA + * @author Jim Wigginton + * @access public + */ +class XML +{ + /** + * Break a public or private key down into its constituent components + * + * @access public + * @param string $key + * @param string $password optional + * @return array + */ + static function load($key, $password = '') + { + $components = array( + 'isPublicKey' => false, + 'primes' => array(), + 'exponents' => array(), + 'coefficients' => array() + ); + + $use_errors = libxml_use_internal_errors(true); + + $dom = new \DOMDocument(); + if (!$dom->loadXML('' . $key . '')) { + return false; + } + $xpath = new \DOMXPath($dom); + $keys = array('modulus', 'exponent', 'p', 'q', 'dp', 'dq', 'inverseq', 'd'); + foreach ($keys as $key) { + // $dom->getElementsByTagName($key) is case-sensitive + $temp = $xpath->query("//*[translate(local-name(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='$key']"); + if (!$temp->length) { + continue; + } + $value = new BigInteger(base64_decode($temp->item(0)->nodeValue), 256); + switch ($key) { + case 'modulus': + $components['modulus'] = $value; + break; + case 'exponent': + $components['publicExponent'] = $value; + break; + case 'p': + $components['primes'][1] = $value; + break; + case 'q': + $components['primes'][2] = $value; + break; + case 'dp': + $components['exponents'][1] = $value; + break; + case 'dq': + $components['exponents'][2] = $value; + break; + case 'inverseq': + $components['coefficients'][2] = $value; + break; + case 'd': + $components['privateExponent'] = $value; + } + } + + libxml_use_internal_errors($use_errors); + + return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false; + } + + /** + * Convert a private key to the appropriate format. + * + * @access public + * @param \phpseclib\Math\BigInteger $n + * @param \phpseclib\Math\BigInteger $e + * @param \phpseclib\Math\BigInteger $d + * @param array $primes + * @param array $exponents + * @param array $coefficients + * @param string $password optional + * @return string + */ + static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, $primes, $exponents, $coefficients, $password = '') + { + if (count($primes) != 2) { + return false; + } + return "\r\n" . + ' ' . base64_encode($n->toBytes()) . "\r\n" . + ' ' . base64_encode($e->toBytes()) . "\r\n" . + '

' . base64_encode($primes[1]->toBytes()) . "

\r\n" . + ' ' . base64_encode($primes[2]->toBytes()) . "\r\n" . + ' ' . base64_encode($exponents[1]->toBytes()) . "\r\n" . + ' ' . base64_encode($exponents[2]->toBytes()) . "\r\n" . + ' ' . base64_encode($coefficients[2]->toBytes()) . "\r\n" . + ' ' . base64_encode($d->toBytes()) . "\r\n" . + '
'; + } + + /** + * Convert a public key to the appropriate format + * + * @access public + * @param \phpseclib\Math\BigInteger $n + * @param \phpseclib\Math\BigInteger $e + * @return string + */ + static function savePublicKey(BigInteger $n, BigInteger $e) + { + return "\r\n" . + ' ' . base64_encode($n->toBytes()) . "\r\n" . + ' ' . base64_encode($e->toBytes()) . "\r\n" . + ''; + } +} \ No newline at end of file diff --git a/phpseclib/File/X509.php b/phpseclib/File/X509.php index 557f7ccd..55d25706 100644 --- a/phpseclib/File/X509.php +++ b/phpseclib/File/X509.php @@ -2122,7 +2122,7 @@ class X509 switch ($publicKeyAlgorithm) { case 'rsaEncryption': $rsa = new RSA(); - $rsa->loadKey($publicKey); + $rsa->load($publicKey); switch ($signatureAlgorithm) { case 'md2WithRSAEncryption': @@ -2800,7 +2800,7 @@ class X509 switch ($keyinfo['algorithm']['algorithm']) { case 'rsaEncryption': $publicKey = new RSA(); - $publicKey->loadKey($key); + $publicKey->load($key); $publicKey->setPublicKey(); break; default: @@ -2870,7 +2870,7 @@ class X509 switch ($algorithm) { case 'rsaEncryption': $this->publicKey = new RSA(); - $this->publicKey->loadKey($key); + $this->publicKey->load($key); $this->publicKey->setPublicKey(); break; default: @@ -2993,7 +2993,7 @@ class X509 switch ($algorithm) { case 'rsaEncryption': $this->publicKey = new RSA(); - $this->publicKey->loadKey($key); + $this->publicKey->load($key); $this->publicKey->setPublicKey(); break; default: @@ -3382,7 +3382,7 @@ class X509 $origPublicKey = $this->publicKey; $class = get_class($this->privateKey); $this->publicKey = new $class(); - $this->publicKey->loadKey($this->privateKey->getPublicKey()); + $this->publicKey->load($this->privateKey->getPublicKey()); $this->publicKey->setPublicKey(); if (!($publicKey = $this->_formatSubjectPublicKey())) { return false; @@ -3440,7 +3440,7 @@ class X509 $origPublicKey = $this->publicKey; $class = get_class($this->privateKey); $this->publicKey = new $class(); - $this->publicKey->loadKey($this->privateKey->getPublicKey()); + $this->publicKey->load($this->privateKey->getPublicKey()); $this->publicKey->setPublicKey(); $publicKey = $this->_formatSubjectPublicKey(); if (!$publicKey) { @@ -4206,7 +4206,7 @@ class X509 $raw = base64_decode($raw); // If the key is private, compute identifier from its corresponding public key. $key = new RSA(); - if (!$key->loadKey($raw)) { + if (!$key->load($raw)) { return false; // Not an unencrypted RSA key. } if ($key->getPrivateKey() !== false) { // If private. @@ -4226,7 +4226,7 @@ class X509 } return false; default: // Should be a key object (i.e.: \phpseclib\Crypt\RSA). - $key = $key->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1); + $key = $key->getPublicKey('PKCS1'); break; } @@ -4259,7 +4259,7 @@ class X509 //return new Element(base64_decode(preg_replace('#-.+-|[\r\n]#', '', $this->publicKey->getPublicKey()))); return array( 'algorithm' => array('algorithm' => 'rsaEncryption'), - 'subjectPublicKey' => $this->publicKey->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1) + 'subjectPublicKey' => $this->publicKey->getPublicKey('PKCS1') ); } diff --git a/phpseclib/Net/SSH2.php b/phpseclib/Net/SSH2.php index b8219fab..a779dc7c 100644 --- a/phpseclib/Net/SSH2.php +++ b/phpseclib/Net/SSH2.php @@ -2239,7 +2239,7 @@ class SSH2 function _privatekey_login($username, $privatekey) { // see http://tools.ietf.org/html/rfc4253#page-15 - $publickey = $privatekey->getPublicKey(RSA::PUBLIC_FORMAT_RAW); + $publickey = $privatekey->getPublicKey('Raw'); if ($publickey === false) { return false; } @@ -4042,7 +4042,7 @@ class SSH2 $rsa = new RSA(); $rsa->setSignatureMode(RSA::SIGNATURE_PKCS1); - $rsa->loadKey(array('e' => $e, 'n' => $n), RSA::PUBLIC_FORMAT_RAW); + $rsa->loadKey(array('e' => $e, 'n' => $n), 'Raw'); if (!$rsa->verify($this->exchange_hash, $signature)) { user_error('Bad server signature'); return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); diff --git a/phpseclib/System/SSH/Agent.php b/phpseclib/System/SSH/Agent.php index 23ef69e5..af055f52 100644 --- a/phpseclib/System/SSH/Agent.php +++ b/phpseclib/System/SSH/Agent.php @@ -175,7 +175,7 @@ class Agent switch ($key_type) { case 'ssh-rsa': $key = new RSA(); - $key->loadKey('ssh-rsa ' . base64_encode($key_blob) . ' ' . $key_comment); + $key->load('ssh-rsa ' . base64_encode($key_blob) . ' ' . $key_comment); break; case 'ssh-dss': // not currently supported diff --git a/tests/Unit/Crypt/RSA/CreateKeyTest.php b/tests/Unit/Crypt/RSA/CreateKeyTest.php new file mode 100644 index 00000000..4b9a89fd --- /dev/null +++ b/tests/Unit/Crypt/RSA/CreateKeyTest.php @@ -0,0 +1,34 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + */ + +use phpseclib\Crypt\RSA; + +class Unit_Crypt_RSA_CreateKeyTest extends PhpseclibTestCase +{ + public function testCreateKey() + { + extract(RSA::createKey(512)); + $this->assertInstanceOf('\phpseclib\Crypt\RSA', $privatekey); + $this->assertInstanceOf('\phpseclib\Crypt\RSA', $publickey); + $this->assertNotEmpty("$privatekey"); + $this->assertNotEmpty("$publickey"); + + return array($publickey, $privatekey); + } + + /** + * @depends testCreateKey + */ + public function testEncryptDecrypt($args) + { + list($publickey, $privatekey) = $args; + $ciphertext = $publickey->encrypt('zzz'); + $this->assertInternalType('string', $ciphertext); + $plaintext = $privatekey->decrypt($ciphertext); + $this->assertSame($plaintext, 'zzz'); + } +} diff --git a/tests/Unit/Crypt/RSA/LoadKeyTest.php b/tests/Unit/Crypt/RSA/LoadKeyTest.php index a80b8d2f..a176092d 100644 --- a/tests/Unit/Crypt/RSA/LoadKeyTest.php +++ b/tests/Unit/Crypt/RSA/LoadKeyTest.php @@ -15,7 +15,7 @@ class Unit_Crypt_RSA_LoadKeyTest extends PhpseclibTestCase $key = 'zzzzzzzzzzzzzz'; - $this->assertFalse($rsa->loadKey($key)); + $this->assertFalse($rsa->load($key)); } public function testPKCS1Key() @@ -36,7 +36,7 @@ U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ 37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0= -----END RSA PRIVATE KEY-----'; - $this->assertTrue($rsa->loadKey($key)); + $this->assertTrue($rsa->load($key)); $this->assertInternalType('string', $rsa->getPrivateKey()); } @@ -59,7 +59,7 @@ U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ -----END RSA PRIVATE KEY-----'; $key = str_replace(array("\r", "\n", "\r\n"), ' ', $key); - $this->assertTrue($rsa->loadKey($key)); + $this->assertTrue($rsa->load($key)); $this->assertInternalType('string', $rsa->getPrivateKey()); } @@ -79,7 +79,7 @@ X6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2epl U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ 37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0='; - $this->assertTrue($rsa->loadKey($key)); + $this->assertTrue($rsa->load($key)); $this->assertInternalType('string', $rsa->getPrivateKey()); } @@ -99,7 +99,7 @@ U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ 'U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ' . '37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0='; - $this->assertTrue($rsa->loadKey($key)); + $this->assertTrue($rsa->load($key)); $this->assertInternalType('string', $rsa->getPrivateKey()); } @@ -120,7 +120,7 @@ U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ '37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0='; $key = base64_decode($key); - $this->assertTrue($rsa->loadKey($key)); + $this->assertTrue($rsa->load($key)); $this->assertInternalType('string', $rsa->getPrivateKey()); } @@ -159,7 +159,7 @@ GF/qoZyC1mbqdtyyeWgHtVbJVUORmpbNnXOII9duEqBUNDiO9VSZNn/8h/VsYeAB xryZaRDVmtMuf/OZBQ== -----END ENCRYPTED PRIVATE KEY-----'; - $this->assertTrue($rsa->loadKey($key)); + $this->assertTrue($rsa->load($key)); $this->assertInternalType('string', $rsa->getPrivateKey()); } @@ -182,12 +182,12 @@ U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ -----END RSA PRIVATE KEY-----'; $rsa->setPassword('password'); - $this->assertTrue($rsa->loadKey($key)); + $this->assertTrue($rsa->load($key)); - $key = $rsa->getPrivateKey(RSA::PRIVATE_FORMAT_PKCS8); + $key = $rsa->getPrivateKey('PKCS8'); $this->assertInternalType('string', $key); - $this->assertTrue($rsa->loadKey($key)); + $this->assertTrue($rsa->load($key)); } public function testPubKey1() @@ -203,7 +203,7 @@ gPiUWOPatVkt7+Bs3h5Ramxh7XjBOXeulmCpGSynXNcpZ/06+vofGi/2MlpQZNhH Ao8eayMp6FcvNucIpUndo1X8dKMv3Y26ZQIDAQAB -----END RSA PUBLIC KEY-----'; - $this->assertTrue($rsa->loadKey($key)); + $this->assertTrue($rsa->load($key)); $this->assertInternalType('string', $rsa->getPublicKey()); $this->assertFalse($rsa->getPrivateKey()); } @@ -222,7 +222,7 @@ lmCpGSynXNcpZ/06+vofGi/2MlpQZNhHAo8eayMp6FcvNucIpUndo1X8dKMv3Y26 ZQIDAQAB -----END PUBLIC KEY-----'; - $this->assertTrue($rsa->loadKey($key)); + $this->assertTrue($rsa->load($key)); $this->assertInternalType('string', $rsa->getPublicKey()); $this->assertFalse($rsa->getPrivateKey()); } @@ -236,7 +236,7 @@ ZQIDAQAB 'GkVb1/3j+skZ6UtW+5u09lHNsj6tQ51s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZw== ' . 'phpseclib-generated-key'; - $this->assertTrue($rsa->loadKey($key)); + $this->assertTrue($rsa->load($key)); $this->assertInternalType('string', $rsa->getPublicKey()); $this->assertFalse($rsa->getPrivateKey()); } @@ -252,7 +252,7 @@ ZQIDAQAB 'b6wYtY/q/WtUFr3nK+x0lgOtokhnJfRR/6fnmC1CztPnIT4BWK81VGKWONAxuhMyQ5XChyu6S9'. 'mWG5tUlUI/5'; - $this->assertTrue($rsa->loadKey($key)); + $this->assertTrue($rsa->load($key)); $this->assertSame($rsa->getPublicKeyFingerprint('md5'), 'bd:2c:2f:31:b9:ef:b8:f8:ad:fc:40:a6:94:4f:28:82'); $this->assertSame($rsa->getPublicKeyFingerprint('sha256'), 'N9sV2uSNZEe8TITODku0pRI27l+Zk0IY0TrRTw3ozwM'); } @@ -270,7 +270,7 @@ gPiUWOPatVkt7+Bs3h5Ramxh7XjBOXeulmCpGSynXNcpZ/06+vofGi/2MlpQZNhH Ao8eayMp6FcvNucIpUndo1X8dKMv3Y26ZQIDAQAB -----END RSA PUBLIC KEY-----'; - $this->assertTrue($rsa->loadKey($key)); + $this->assertTrue($rsa->load($key)); $this->assertTrue($rsa->setPrivateKey()); $this->assertGreaterThanOrEqual(1, strlen("$rsa")); $this->assertFalse($rsa->getPublicKey()); @@ -290,11 +290,11 @@ Ao8eayMp6FcvNucIpUndo1X8dKMv3Y26ZQIDAQAB AQAB '; - $rsa->loadKey($key); + $rsa->load($key); $rsa->setPublicKey(); - $newkey = $rsa->getPublicKey(RSA::PUBLIC_FORMAT_XML); + $newkey = $rsa->getPublicKey('XML'); - $this->assertSame(preg_replace('#\s#', '', $key), preg_replace('#\s#', '', $newkey)); + $this->assertSame(strtolower(preg_replace('#\s#', '', $key)), strtolower(preg_replace('#\s#', '', $newkey))); } /** @@ -311,7 +311,7 @@ JWrQdxx/WNN+ABG426rgYYbeGcIlWLZCw6Bx/1HtN5ef6nVEoiGNChYKIRB4QFOi 01smFxps1w8ZIQnD6wIDAQAB -----END PUBLIC KEY-----'; - $rsa->loadKey($key); + $rsa->load($key); $rsa->setPublicKey(); $newkey = $rsa->getPublicKey(); diff --git a/tests/Unit/Crypt/RSA/ModeTest.php b/tests/Unit/Crypt/RSA/ModeTest.php index b11ac05b..e1bb5bdc 100644 --- a/tests/Unit/Crypt/RSA/ModeTest.php +++ b/tests/Unit/Crypt/RSA/ModeTest.php @@ -28,8 +28,8 @@ X6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2epl U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ 37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0= -----END RSA PRIVATE KEY-----'; - $rsa->loadKey($privatekey); - $rsa->loadKey($rsa->getPublicKey()); + $rsa->load($privatekey); + $rsa->load($rsa->getPublicKey()); $rsa->setEncryptionMode(RSA::ENCRYPTION_NONE); $expected = '105b92f59a87a8ad4da52c128b8c99491790ef5a54770119e0819060032fb9e772ed6772828329567f3d7e9472154c1530f8156ba7fd732f52ca1c06' . @@ -40,7 +40,7 @@ U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ $this->assertEquals($result, $expected); - $rsa->loadKey($privatekey); + $rsa->load($privatekey); $this->assertEquals(trim($rsa->decrypt($result), "\0"), $plaintext); } @@ -50,7 +50,7 @@ U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ public function testPSSSigs() { $rsa = new RSA(); - $rsa->loadKey('-----BEGIN PUBLIC KEY----- + $rsa->load('-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqGKukO1De7zhZj6+H0qtjTkVx wTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUpwmJG8wVQZKjeGcjDOL5UlsuusFnc CzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ51s1SPrCBkedbNf0T diff --git a/tests/Unit/File/X509/SPKACTest.php b/tests/Unit/File/X509/SPKACTest.php index d31a3440..41ff2a0d 100644 --- a/tests/Unit/File/X509/SPKACTest.php +++ b/tests/Unit/File/X509/SPKACTest.php @@ -48,10 +48,9 @@ class Unit_File_X509_SPKACTest extends PhpseclibTestCase { $privKey = new RSA(); extract($privKey->createKey()); - $privKey->loadKey($privatekey); $x509 = new X509(); - $x509->setPrivateKey($privKey); + $x509->setPrivateKey($privatekey); $x509->setChallenge('...'); $spkac = $x509->signSPKAC(); diff --git a/tests/Unit/File/X509/X509Test.php b/tests/Unit/File/X509/X509Test.php index acbb44a8..c172b05a 100644 --- a/tests/Unit/File/X509/X509Test.php +++ b/tests/Unit/File/X509/X509Test.php @@ -154,7 +154,7 @@ IOkKcGQRCMha8X2e7GmlpdWC1ycenlbN0nbVeSv3JUMcafC4+Q== public function testSaveNullRSAParam() { $privKey = new RSA(); - $privKey->loadKey('-----BEGIN RSA PRIVATE KEY----- + $privKey->load('-----BEGIN RSA PRIVATE KEY----- MIICXQIBAAKBgQDMswfEpAgnUDWA74zZw5XcPsWh1ly1Vk99tsqwoFDkLF7jvXy1 dDLHYfuquvfxCgcp8k/4fQhx4ubR8bbGgEq9B05YRnViK0R0iBB5Ui4IaxWYYhKE 8xqAEH2fL+/7nsqqNFKkEN9KeFwc7WbMY49U2adlMrpBdRjk1DqIEW3QTwIDAQAB @@ -171,7 +171,7 @@ aBtsWpliLSex/HHhtRW9AkBGcq67zKmEpJ9kXcYLEjJii3flFS+Ct/rNm+Hhm1l7 -----END RSA PRIVATE KEY-----'); $pubKey = new RSA(); - $pubKey->loadKey($privKey->getPublicKey()); + $pubKey->load($privKey->getPublicKey()); $pubKey->setPublicKey(); $subject = new X509();