[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/vendor/phpseclib/phpseclib/phpseclib/Crypt/ -> RSA.php (source)

   1  <?php
   2  
   3  /**
   4   * Pure-PHP PKCS#1 (v2.1) compliant implementation of RSA.
   5   *
   6   * PHP version 5
   7   *
   8   * Here's an example of how to encrypt and decrypt text with this library:
   9   * <code>
  10   * <?php
  11   *    include 'vendor/autoload.php';
  12   *
  13   *    $rsa = new \phpseclib\Crypt\RSA();
  14   *    extract($rsa->createKey());
  15   *
  16   *    $plaintext = 'terrafrost';
  17   *
  18   *    $rsa->loadKey($privatekey);
  19   *    $ciphertext = $rsa->encrypt($plaintext);
  20   *
  21   *    $rsa->loadKey($publickey);
  22   *    echo $rsa->decrypt($ciphertext);
  23   * ?>
  24   * </code>
  25   *
  26   * Here's an example of how to create signatures and verify signatures with this library:
  27   * <code>
  28   * <?php
  29   *    include 'vendor/autoload.php';
  30   *
  31   *    $rsa = new \phpseclib\Crypt\RSA();
  32   *    extract($rsa->createKey());
  33   *
  34   *    $plaintext = 'terrafrost';
  35   *
  36   *    $rsa->loadKey($privatekey);
  37   *    $signature = $rsa->sign($plaintext);
  38   *
  39   *    $rsa->loadKey($publickey);
  40   *    echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified';
  41   * ?>
  42   * </code>
  43   *
  44   * @category  Crypt
  45   * @package   RSA
  46   * @author    Jim Wigginton <terrafrost@php.net>
  47   * @copyright 2009 Jim Wigginton
  48   * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
  49   * @link      http://phpseclib.sourceforge.net
  50   */
  51  
  52  namespace phpseclib\Crypt;
  53  
  54  use phpseclib\Math\BigInteger;
  55  
  56  /**
  57   * Pure-PHP PKCS#1 compliant implementation of RSA.
  58   *
  59   * @package RSA
  60   * @author  Jim Wigginton <terrafrost@php.net>
  61   * @access  public
  62   */
  63  class RSA
  64  {
  65      /**#@+
  66       * @access public
  67       * @see self::encrypt()
  68       * @see self::decrypt()
  69       */
  70      /**
  71       * Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding}
  72       * (OAEP) for encryption / decryption.
  73       *
  74       * Uses sha1 by default.
  75       *
  76       * @see self::setHash()
  77       * @see self::setMGFHash()
  78       */
  79      const ENCRYPTION_OAEP = 1;
  80      /**
  81       * Use PKCS#1 padding.
  82       *
  83       * Although self::ENCRYPTION_OAEP offers more security, including PKCS#1 padding is necessary for purposes of backwards
  84       * compatibility with protocols (like SSH-1) written before OAEP's introduction.
  85       */
  86      const ENCRYPTION_PKCS1 = 2;
  87      /**
  88       * Do not use any padding
  89       *
  90       * Although this method is not recommended it can none-the-less sometimes be useful if you're trying to decrypt some legacy
  91       * stuff, if you're trying to diagnose why an encrypted message isn't decrypting, etc.
  92       */
  93      const ENCRYPTION_NONE = 3;
  94      /**#@-*/
  95  
  96      /**#@+
  97       * @access public
  98       * @see self::sign()
  99       * @see self::verify()
 100       * @see self::setHash()
 101      */
 102      /**
 103       * Use the Probabilistic Signature Scheme for signing
 104       *
 105       * Uses sha1 by default.
 106       *
 107       * @see self::setSaltLength()
 108       * @see self::setMGFHash()
 109       */
 110      const SIGNATURE_PSS = 1;
 111      /**
 112       * Use the PKCS#1 scheme by default.
 113       *
 114       * Although self::SIGNATURE_PSS offers more security, including PKCS#1 signing is necessary for purposes of backwards
 115       * compatibility with protocols (like SSH-2) written before PSS's introduction.
 116       */
 117      const SIGNATURE_PKCS1 = 2;
 118      /**#@-*/
 119  
 120      /**#@+
 121       * @access private
 122       * @see \phpseclib\Crypt\RSA::createKey()
 123      */
 124      /**
 125       * ASN1 Integer
 126       */
 127      const ASN1_INTEGER = 2;
 128      /**
 129       * ASN1 Bit String
 130       */
 131      const ASN1_BITSTRING = 3;
 132      /**
 133       * ASN1 Octet String
 134       */
 135      const ASN1_OCTETSTRING = 4;
 136      /**
 137       * ASN1 Object Identifier
 138       */
 139      const ASN1_OBJECT = 6;
 140      /**
 141       * ASN1 Sequence (with the constucted bit set)
 142       */
 143      const ASN1_SEQUENCE = 48;
 144      /**#@-*/
 145  
 146      /**#@+
 147       * @access private
 148       * @see \phpseclib\Crypt\RSA::__construct()
 149      */
 150      /**
 151       * To use the pure-PHP implementation
 152       */
 153      const MODE_INTERNAL = 1;
 154      /**
 155       * To use the OpenSSL library
 156       *
 157       * (if enabled; otherwise, the internal implementation will be used)
 158       */
 159      const MODE_OPENSSL = 2;
 160      /**#@-*/
 161  
 162      /**#@+
 163       * @access public
 164       * @see \phpseclib\Crypt\RSA::createKey()
 165       * @see \phpseclib\Crypt\RSA::setPrivateKeyFormat()
 166      */
 167      /**
 168       * PKCS#1 formatted private key
 169       *
 170       * Used by OpenSSH
 171       */
 172      const PRIVATE_FORMAT_PKCS1 = 0;
 173      /**
 174       * PuTTY formatted private key
 175       */
 176      const PRIVATE_FORMAT_PUTTY = 1;
 177      /**
 178       * XML formatted private key
 179       */
 180      const PRIVATE_FORMAT_XML = 2;
 181      /**
 182       * PKCS#8 formatted private key
 183       */
 184      const PRIVATE_FORMAT_PKCS8 = 8;
 185      /**
 186       * OpenSSH formatted private key
 187       */
 188      const PRIVATE_FORMAT_OPENSSH = 9;
 189      /**#@-*/
 190  
 191      /**#@+
 192       * @access public
 193       * @see \phpseclib\Crypt\RSA::createKey()
 194       * @see \phpseclib\Crypt\RSA::setPublicKeyFormat()
 195      */
 196      /**
 197       * Raw public key
 198       *
 199       * An array containing two \phpseclib\Math\BigInteger objects.
 200       *
 201       * The exponent can be indexed with any of the following:
 202       *
 203       * 0, e, exponent, publicExponent
 204       *
 205       * The modulus can be indexed with any of the following:
 206       *
 207       * 1, n, modulo, modulus
 208       */
 209      const PUBLIC_FORMAT_RAW = 3;
 210      /**
 211       * PKCS#1 formatted public key (raw)
 212       *
 213       * Used by File/X509.php
 214       *
 215       * Has the following header:
 216       *
 217       * -----BEGIN RSA PUBLIC KEY-----
 218       *
 219       * Analogous to ssh-keygen's pem format (as specified by -m)
 220       */
 221      const PUBLIC_FORMAT_PKCS1 = 4;
 222      const PUBLIC_FORMAT_PKCS1_RAW = 4;
 223      /**
 224       * XML formatted public key
 225       */
 226      const PUBLIC_FORMAT_XML = 5;
 227      /**
 228       * OpenSSH formatted public key
 229       *
 230       * Place in $HOME/.ssh/authorized_keys
 231       */
 232      const PUBLIC_FORMAT_OPENSSH = 6;
 233      /**
 234       * PKCS#1 formatted public key (encapsulated)
 235       *
 236       * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set)
 237       *
 238       * Has the following header:
 239       *
 240       * -----BEGIN PUBLIC KEY-----
 241       *
 242       * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
 243       * is specific to private keys it's basically creating a DER-encoded wrapper
 244       * for keys. This just extends that same concept to public keys (much like ssh-keygen)
 245       */
 246      const PUBLIC_FORMAT_PKCS8 = 7;
 247      /**#@-*/
 248  
 249      /**
 250       * Precomputed Zero
 251       *
 252       * @var \phpseclib\Math\BigInteger
 253       * @access private
 254       */
 255      var $zero;
 256  
 257      /**
 258       * Precomputed One
 259       *
 260       * @var \phpseclib\Math\BigInteger
 261       * @access private
 262       */
 263      var $one;
 264  
 265      /**
 266       * Private Key Format
 267       *
 268       * @var int
 269       * @access private
 270       */
 271      var $privateKeyFormat = self::PRIVATE_FORMAT_PKCS1;
 272  
 273      /**
 274       * Public Key Format
 275       *
 276       * @var int
 277       * @access public
 278       */
 279      var $publicKeyFormat = self::PUBLIC_FORMAT_PKCS8;
 280  
 281      /**
 282       * Modulus (ie. n)
 283       *
 284       * @var \phpseclib\Math\BigInteger
 285       * @access private
 286       */
 287      var $modulus;
 288  
 289      /**
 290       * Modulus length
 291       *
 292       * @var \phpseclib\Math\BigInteger
 293       * @access private
 294       */
 295      var $k;
 296  
 297      /**
 298       * Exponent (ie. e or d)
 299       *
 300       * @var \phpseclib\Math\BigInteger
 301       * @access private
 302       */
 303      var $exponent;
 304  
 305      /**
 306       * Primes for Chinese Remainder Theorem (ie. p and q)
 307       *
 308       * @var array
 309       * @access private
 310       */
 311      var $primes;
 312  
 313      /**
 314       * Exponents for Chinese Remainder Theorem (ie. dP and dQ)
 315       *
 316       * @var array
 317       * @access private
 318       */
 319      var $exponents;
 320  
 321      /**
 322       * Coefficients for Chinese Remainder Theorem (ie. qInv)
 323       *
 324       * @var array
 325       * @access private
 326       */
 327      var $coefficients;
 328  
 329      /**
 330       * Hash name
 331       *
 332       * @var string
 333       * @access private
 334       */
 335      var $hashName;
 336  
 337      /**
 338       * Hash function
 339       *
 340       * @var \phpseclib\Crypt\Hash
 341       * @access private
 342       */
 343      var $hash;
 344  
 345      /**
 346       * Length of hash function output
 347       *
 348       * @var int
 349       * @access private
 350       */
 351      var $hLen;
 352  
 353      /**
 354       * Length of salt
 355       *
 356       * @var int
 357       * @access private
 358       */
 359      var $sLen;
 360  
 361      /**
 362       * Hash function for the Mask Generation Function
 363       *
 364       * @var \phpseclib\Crypt\Hash
 365       * @access private
 366       */
 367      var $mgfHash;
 368  
 369      /**
 370       * Length of MGF hash function output
 371       *
 372       * @var int
 373       * @access private
 374       */
 375      var $mgfHLen;
 376  
 377      /**
 378       * Encryption mode
 379       *
 380       * @var int
 381       * @access private
 382       */
 383      var $encryptionMode = self::ENCRYPTION_OAEP;
 384  
 385      /**
 386       * Signature mode
 387       *
 388       * @var int
 389       * @access private
 390       */
 391      var $signatureMode = self::SIGNATURE_PSS;
 392  
 393      /**
 394       * Public Exponent
 395       *
 396       * @var mixed
 397       * @access private
 398       */
 399      var $publicExponent = false;
 400  
 401      /**
 402       * Password
 403       *
 404       * @var string
 405       * @access private
 406       */
 407      var $password = false;
 408  
 409      /**
 410       * Components
 411       *
 412       * For use with parsing XML formatted keys.  PHP's XML Parser functions use utilized - instead of PHP's DOM functions -
 413       * because PHP's XML Parser functions work on PHP4 whereas PHP's DOM functions - although surperior - don't.
 414       *
 415       * @see self::_start_element_handler()
 416       * @var array
 417       * @access private
 418       */
 419      var $components = array();
 420  
 421      /**
 422       * Current String
 423       *
 424       * For use with parsing XML formatted keys.
 425       *
 426       * @see self::_character_handler()
 427       * @see self::_stop_element_handler()
 428       * @var mixed
 429       * @access private
 430       */
 431      var $current;
 432  
 433      /**
 434       * OpenSSL configuration file name.
 435       *
 436       * Set to null to use system configuration file.
 437       * @see self::createKey()
 438       * @var mixed
 439       * @Access public
 440       */
 441      var $configFile;
 442  
 443      /**
 444       * Public key comment field.
 445       *
 446       * @var string
 447       * @access private
 448       */
 449      var $comment = 'phpseclib-generated-key';
 450  
 451      /**
 452       * The constructor
 453       *
 454       * If you want to make use of the openssl extension, you'll need to set the mode manually, yourself.  The reason
 455       * \phpseclib\Crypt\RSA doesn't do it is because OpenSSL doesn't fail gracefully.  openssl_pkey_new(), in particular, requires
 456       * openssl.cnf be present somewhere and, unfortunately, the only real way to find out is too late.
 457       *
 458       * @return \phpseclib\Crypt\RSA
 459       * @access public
 460       */
 461      function __construct()
 462      {
 463          $this->configFile = dirname(__FILE__) . '/../openssl.cnf';
 464  
 465          if (!defined('CRYPT_RSA_MODE')) {
 466              switch (true) {
 467                  // Math/BigInteger's openssl requirements are a little less stringent than Crypt/RSA's. in particular,
 468                  // Math/BigInteger doesn't require an openssl.cfg file whereas Crypt/RSA does. so if Math/BigInteger
 469                  // can't use OpenSSL it can be pretty trivially assumed, then, that Crypt/RSA can't either.
 470                  case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'):
 471                      define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
 472                      break;
 473                  case function_exists('phpinfo') && extension_loaded('openssl') && file_exists($this->configFile):
 474                      // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
 475                      $versions = array();
 476  
 477                      // avoid generating errors (even with suppression) when phpinfo() is disabled (common in production systems)
 478                      if (strpos(ini_get('disable_functions'), 'phpinfo') === false) {
 479                          ob_start();
 480                          @phpinfo();
 481                          $content = ob_get_contents();
 482                          ob_end_clean();
 483  
 484                          preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches);
 485  
 486                          if (!empty($matches[1])) {
 487                              for ($i = 0; $i < count($matches[1]); $i++) {
 488                                  $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i])));
 489  
 490                                  // Remove letter part in OpenSSL version
 491                                  if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) {
 492                                      $versions[$matches[1][$i]] = $fullVersion;
 493                                  } else {
 494                                      $versions[$matches[1][$i]] = $m[0];
 495                                  }
 496                              }
 497                          }
 498                      }
 499  
 500                      // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+
 501                      switch (true) {
 502                          case !isset($versions['Header']):
 503                          case !isset($versions['Library']):
 504                          case $versions['Header'] == $versions['Library']:
 505                          case version_compare($versions['Header'], '1.0.0') >= 0 && version_compare($versions['Library'], '1.0.0') >= 0:
 506                              define('CRYPT_RSA_MODE', self::MODE_OPENSSL);
 507                              break;
 508                          default:
 509                              define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
 510                              define('MATH_BIGINTEGER_OPENSSL_DISABLE', true);
 511                      }
 512                      break;
 513                  default:
 514                      define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
 515              }
 516          }
 517  
 518          $this->zero = new BigInteger();
 519          $this->one = new BigInteger(1);
 520  
 521          $this->hash = new Hash('sha1');
 522          $this->hLen = $this->hash->getLength();
 523          $this->hashName = 'sha1';
 524          $this->mgfHash = new Hash('sha1');
 525          $this->mgfHLen = $this->mgfHash->getLength();
 526      }
 527  
 528      /**
 529       * Create public / private key pair
 530       *
 531       * Returns an array with the following three elements:
 532       *  - 'privatekey': The private key.
 533       *  - 'publickey':  The public key.
 534       *  - 'partialkey': A partially computed key (if the execution time exceeded $timeout).
 535       *                  Will need to be passed back to \phpseclib\Crypt\RSA::createKey() as the third parameter for further processing.
 536       *
 537       * @access public
 538       * @param int $bits
 539       * @param int $timeout
 540       * @param array $partial
 541       */
 542      function createKey($bits = 1024, $timeout = false, $partial = array())
 543      {
 544          if (!defined('CRYPT_RSA_EXPONENT')) {
 545              // http://en.wikipedia.org/wiki/65537_%28number%29
 546              define('CRYPT_RSA_EXPONENT', '65537');
 547          }
 548          // per <http://cseweb.ucsd.edu/~hovav/dist/survey.pdf#page=5>, this number ought not result in primes smaller
 549          // than 256 bits. as a consequence if the key you're trying to create is 1024 bits and you've set CRYPT_RSA_SMALLEST_PRIME
 550          // to 384 bits then you're going to get a 384 bit prime and a 640 bit prime (384 + 1024 % 384). at least if
 551          // CRYPT_RSA_MODE is set to self::MODE_INTERNAL. if CRYPT_RSA_MODE is set to self::MODE_OPENSSL then
 552          // CRYPT_RSA_SMALLEST_PRIME is ignored (ie. multi-prime RSA support is more intended as a way to speed up RSA key
 553          // generation when there's a chance neither gmp nor OpenSSL are installed)
 554          if (!defined('CRYPT_RSA_SMALLEST_PRIME')) {
 555              define('CRYPT_RSA_SMALLEST_PRIME', 4096);
 556          }
 557  
 558          // OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum
 559          if (CRYPT_RSA_MODE == self::MODE_OPENSSL && $bits >= 384 && CRYPT_RSA_EXPONENT == 65537) {
 560              $config = array();
 561              if (isset($this->configFile)) {
 562                  $config['config'] = $this->configFile;
 563              }
 564              $rsa = openssl_pkey_new(array('private_key_bits' => $bits) + $config);
 565              openssl_pkey_export($rsa, $privatekey, null, $config);
 566              $publickey = openssl_pkey_get_details($rsa);
 567              $publickey = $publickey['key'];
 568  
 569              $privatekey = call_user_func_array(array($this, '_convertPrivateKey'), array_values($this->_parseKey($privatekey, self::PRIVATE_FORMAT_PKCS1)));
 570              $publickey = call_user_func_array(array($this, '_convertPublicKey'), array_values($this->_parseKey($publickey, self::PUBLIC_FORMAT_PKCS1)));
 571  
 572              // clear the buffer of error strings stemming from a minimalistic openssl.cnf
 573              while (openssl_error_string() !== false) {
 574              }
 575  
 576              return array(
 577                  'privatekey' => $privatekey,
 578                  'publickey' => $publickey,
 579                  'partialkey' => false
 580              );
 581          }
 582  
 583          static $e;
 584          if (!isset($e)) {
 585              $e = new BigInteger(CRYPT_RSA_EXPONENT);
 586          }
 587  
 588          extract($this->_generateMinMax($bits));
 589          $absoluteMin = $min;
 590          $temp = $bits >> 1; // divide by two to see how many bits P and Q would be
 591          if ($temp > CRYPT_RSA_SMALLEST_PRIME) {
 592              $num_primes = floor($bits / CRYPT_RSA_SMALLEST_PRIME);
 593              $temp = CRYPT_RSA_SMALLEST_PRIME;
 594          } else {
 595              $num_primes = 2;
 596          }
 597          extract($this->_generateMinMax($temp + $bits % $temp));
 598          $finalMax = $max;
 599          extract($this->_generateMinMax($temp));
 600  
 601          $generator = new BigInteger();
 602  
 603          $n = $this->one->copy();
 604          if (!empty($partial)) {
 605              extract(unserialize($partial));
 606          } else {
 607              $exponents = $coefficients = $primes = array();
 608              $lcm = array(
 609                  'top' => $this->one->copy(),
 610                  'bottom' => false
 611              );
 612          }
 613  
 614          $start = time();
 615          $i0 = count($primes) + 1;
 616  
 617          do {
 618              for ($i = $i0; $i <= $num_primes; $i++) {
 619                  if ($timeout !== false) {
 620                      $timeout-= time() - $start;
 621                      $start = time();
 622                      if ($timeout <= 0) {
 623                          return array(
 624                              'privatekey' => '',
 625                              'publickey'  => '',
 626                              'partialkey' => serialize(array(
 627                                  'primes' => $primes,
 628                                  'coefficients' => $coefficients,
 629                                  'lcm' => $lcm,
 630                                  'exponents' => $exponents
 631                              ))
 632                          );
 633                      }
 634                  }
 635  
 636                  if ($i == $num_primes) {
 637                      list($min, $temp) = $absoluteMin->divide($n);
 638                      if (!$temp->equals($this->zero)) {
 639                          $min = $min->add($this->one); // ie. ceil()
 640                      }
 641                      $primes[$i] = $generator->randomPrime($min, $finalMax, $timeout);
 642                  } else {
 643                      $primes[$i] = $generator->randomPrime($min, $max, $timeout);
 644                  }
 645  
 646                  if ($primes[$i] === false) { // if we've reached the timeout
 647                      if (count($primes) > 1) {
 648                          $partialkey = '';
 649                      } else {
 650                          array_pop($primes);
 651                          $partialkey = serialize(array(
 652                              'primes' => $primes,
 653                              'coefficients' => $coefficients,
 654                              'lcm' => $lcm,
 655                              'exponents' => $exponents
 656                          ));
 657                      }
 658  
 659                      return array(
 660                          'privatekey' => '',
 661                          'publickey'  => '',
 662                          'partialkey' => $partialkey
 663                      );
 664                  }
 665  
 666                  // the first coefficient is calculated differently from the rest
 667                  // ie. instead of being $primes[1]->modInverse($primes[2]), it's $primes[2]->modInverse($primes[1])
 668                  if ($i > 2) {
 669                      $coefficients[$i] = $n->modInverse($primes[$i]);
 670                  }
 671  
 672                  $n = $n->multiply($primes[$i]);
 673  
 674                  $temp = $primes[$i]->subtract($this->one);
 675  
 676                  // textbook RSA implementations use Euler's totient function instead of the least common multiple.
 677                  // see http://en.wikipedia.org/wiki/Euler%27s_totient_function
 678                  $lcm['top'] = $lcm['top']->multiply($temp);
 679                  $lcm['bottom'] = $lcm['bottom'] === false ? $temp : $lcm['bottom']->gcd($temp);
 680  
 681                  $exponents[$i] = $e->modInverse($temp);
 682              }
 683  
 684              list($temp) = $lcm['top']->divide($lcm['bottom']);
 685              $gcd = $temp->gcd($e);
 686              $i0 = 1;
 687          } while (!$gcd->equals($this->one));
 688  
 689          $d = $e->modInverse($temp);
 690  
 691          $coefficients[2] = $primes[2]->modInverse($primes[1]);
 692  
 693          // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.2>:
 694          // RSAPrivateKey ::= SEQUENCE {
 695          //     version           Version,
 696          //     modulus           INTEGER,  -- n
 697          //     publicExponent    INTEGER,  -- e
 698          //     privateExponent   INTEGER,  -- d
 699          //     prime1            INTEGER,  -- p
 700          //     prime2            INTEGER,  -- q
 701          //     exponent1         INTEGER,  -- d mod (p-1)
 702          //     exponent2         INTEGER,  -- d mod (q-1)
 703          //     coefficient       INTEGER,  -- (inverse of q) mod p
 704          //     otherPrimeInfos   OtherPrimeInfos OPTIONAL
 705          // }
 706  
 707          return array(
 708              'privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients),
 709              'publickey'  => $this->_convertPublicKey($n, $e),
 710              'partialkey' => false
 711          );
 712      }
 713  
 714      /**
 715       * Convert a private key to the appropriate format.
 716       *
 717       * @access private
 718       * @see self::setPrivateKeyFormat()
 719       * @param Math_BigInteger $n
 720       * @param Math_BigInteger $e
 721       * @param Math_BigInteger $d
 722       * @param array<int,Math_BigInteger> $primes
 723       * @param array<int,Math_BigInteger> $exponents
 724       * @param array<int,Math_BigInteger> $coefficients
 725       * @return string
 726       */
 727      function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients)
 728      {
 729          $signed = $this->privateKeyFormat != self::PRIVATE_FORMAT_XML;
 730          $num_primes = count($primes);
 731          $raw = array(
 732              'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi
 733              'modulus' => $n->toBytes($signed),
 734              'publicExponent' => $e->toBytes($signed),
 735              'privateExponent' => $d->toBytes($signed),
 736              'prime1' => $primes[1]->toBytes($signed),
 737              'prime2' => $primes[2]->toBytes($signed),
 738              'exponent1' => $exponents[1]->toBytes($signed),
 739              'exponent2' => $exponents[2]->toBytes($signed),
 740              'coefficient' => $coefficients[2]->toBytes($signed)
 741          );
 742  
 743          // if the format in question does not support multi-prime rsa and multi-prime rsa was used,
 744          // call _convertPublicKey() instead.
 745          switch ($this->privateKeyFormat) {
 746              case self::PRIVATE_FORMAT_XML:
 747                  if ($num_primes != 2) {
 748                      return false;
 749                  }
 750                  return "<RSAKeyValue>\r\n" .
 751                         '  <Modulus>' . base64_encode($raw['modulus']) . "</Modulus>\r\n" .
 752                         '  <Exponent>' . base64_encode($raw['publicExponent']) . "</Exponent>\r\n" .
 753                         '  <P>' . base64_encode($raw['prime1']) . "</P>\r\n" .
 754                         '  <Q>' . base64_encode($raw['prime2']) . "</Q>\r\n" .
 755                         '  <DP>' . base64_encode($raw['exponent1']) . "</DP>\r\n" .
 756                         '  <DQ>' . base64_encode($raw['exponent2']) . "</DQ>\r\n" .
 757                         '  <InverseQ>' . base64_encode($raw['coefficient']) . "</InverseQ>\r\n" .
 758                         '  <D>' . base64_encode($raw['privateExponent']) . "</D>\r\n" .
 759                         '</RSAKeyValue>';
 760                  break;
 761              case self::PRIVATE_FORMAT_PUTTY:
 762                  if ($num_primes != 2) {
 763                      return false;
 764                  }
 765                  $key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: ";
 766                  $encryption = (!empty($this->password) || is_string($this->password)) ? 'aes256-cbc' : 'none';
 767                  $key.= $encryption;
 768                  $key.= "\r\nComment: " . $this->comment . "\r\n";
 769                  $public = pack(
 770                      'Na*Na*Na*',
 771                      strlen('ssh-rsa'),
 772                      'ssh-rsa',
 773                      strlen($raw['publicExponent']),
 774                      $raw['publicExponent'],
 775                      strlen($raw['modulus']),
 776                      $raw['modulus']
 777                  );
 778                  $source = pack(
 779                      'Na*Na*Na*Na*',
 780                      strlen('ssh-rsa'),
 781                      'ssh-rsa',
 782                      strlen($encryption),
 783                      $encryption,
 784                      strlen($this->comment),
 785                      $this->comment,
 786                      strlen($public),
 787                      $public
 788                  );
 789                  $public = base64_encode($public);
 790                  $key.= "Public-Lines: " . ((strlen($public) + 63) >> 6) . "\r\n";
 791                  $key.= chunk_split($public, 64);
 792                  $private = pack(
 793                      'Na*Na*Na*Na*',
 794                      strlen($raw['privateExponent']),
 795                      $raw['privateExponent'],
 796                      strlen($raw['prime1']),
 797                      $raw['prime1'],
 798                      strlen($raw['prime2']),
 799                      $raw['prime2'],
 800                      strlen($raw['coefficient']),
 801                      $raw['coefficient']
 802                  );
 803                  if (empty($this->password) && !is_string($this->password)) {
 804                      $source.= pack('Na*', strlen($private), $private);
 805                      $hashkey = 'putty-private-key-file-mac-key';
 806                  } else {
 807                      $private.= Random::string(16 - (strlen($private) & 15));
 808                      $source.= pack('Na*', strlen($private), $private);
 809                      $sequence = 0;
 810                      $symkey = '';
 811                      while (strlen($symkey) < 32) {
 812                          $temp = pack('Na*', $sequence++, $this->password);
 813                          $symkey.= pack('H*', sha1($temp));
 814                      }
 815                      $symkey = substr($symkey, 0, 32);
 816                      $crypto = new AES();
 817  
 818                      $crypto->setKey($symkey);
 819                      $crypto->disablePadding();
 820                      $private = $crypto->encrypt($private);
 821                      $hashkey = 'putty-private-key-file-mac-key' . $this->password;
 822                  }
 823  
 824                  $private = base64_encode($private);
 825                  $key.= 'Private-Lines: ' . ((strlen($private) + 63) >> 6) . "\r\n";
 826                  $key.= chunk_split($private, 64);
 827                  $hash = new Hash('sha1');
 828                  $hash->setKey(pack('H*', sha1($hashkey)));
 829                  $key.= 'Private-MAC: ' . bin2hex($hash->hash($source)) . "\r\n";
 830  
 831                  return $key;
 832              case self::PRIVATE_FORMAT_OPENSSH:
 833                  if ($num_primes != 2) {
 834                      return false;
 835                  }
 836                  $publicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($raw['publicExponent']), $raw['publicExponent'], strlen($raw['modulus']), $raw['modulus']);
 837                  $privateKey = pack(
 838                      'Na*Na*Na*Na*Na*Na*Na*',
 839                      strlen('ssh-rsa'),
 840                      'ssh-rsa',
 841                      strlen($raw['modulus']),
 842                      $raw['modulus'],
 843                      strlen($raw['publicExponent']),
 844                      $raw['publicExponent'],
 845                      strlen($raw['privateExponent']),
 846                      $raw['privateExponent'],
 847                      strlen($raw['coefficient']),
 848                      $raw['coefficient'],
 849                      strlen($raw['prime1']),
 850                      $raw['prime1'],
 851                      strlen($raw['prime2']),
 852                      $raw['prime2']
 853                  );
 854                  $checkint = Random::string(4);
 855                  $paddedKey = pack(
 856                      'a*Na*',
 857                      $checkint . $checkint . $privateKey,
 858                      strlen($this->comment),
 859                      $this->comment
 860                  );
 861                  $paddingLength = (7 * strlen($paddedKey)) % 8;
 862                  for ($i = 1; $i <= $paddingLength; $i++) {
 863                      $paddedKey.= chr($i);
 864                  }
 865                  $key = pack(
 866                      'Na*Na*Na*NNa*Na*',
 867                      strlen('none'),
 868                      'none',
 869                      strlen('none'),
 870                      'none',
 871                      0,
 872                      '',
 873                      1,
 874                      strlen($publicKey),
 875                      $publicKey,
 876                      strlen($paddedKey),
 877                      $paddedKey
 878                  );
 879                  $key = "openssh-key-v1\0$key";
 880  
 881                  return "-----BEGIN OPENSSH PRIVATE KEY-----\n" .
 882                         chunk_split(base64_encode($key), 70, "\n") .
 883                         "-----END OPENSSH PRIVATE KEY-----\n";
 884              default: // eg. self::PRIVATE_FORMAT_PKCS1
 885                  $components = array();
 886                  foreach ($raw as $name => $value) {
 887                      $components[$name] = pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($value)), $value);
 888                  }
 889  
 890                  $RSAPrivateKey = implode('', $components);
 891  
 892                  if ($num_primes > 2) {
 893                      $OtherPrimeInfos = '';
 894                      for ($i = 3; $i <= $num_primes; $i++) {
 895                          // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
 896                          //
 897                          // OtherPrimeInfo ::= SEQUENCE {
 898                          //     prime             INTEGER,  -- ri
 899                          //     exponent          INTEGER,  -- di
 900                          //     coefficient       INTEGER   -- ti
 901                          // }
 902                          $OtherPrimeInfo = pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true));
 903                          $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true));
 904                          $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true));
 905                          $OtherPrimeInfos.= pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo);
 906                      }
 907                      $RSAPrivateKey.= pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos);
 908                  }
 909  
 910                  $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
 911  
 912                  if ($this->privateKeyFormat == self::PRIVATE_FORMAT_PKCS8) {
 913                      $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
 914                      $RSAPrivateKey = pack(
 915                          'Ca*a*Ca*a*',
 916                          self::ASN1_INTEGER,
 917                          "\01\00",
 918                          $rsaOID,
 919                          4,
 920                          $this->_encodeLength(strlen($RSAPrivateKey)),
 921                          $RSAPrivateKey
 922                      );
 923                      $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
 924                      if (!empty($this->password) || is_string($this->password)) {
 925                          $salt = Random::string(8);
 926                          $iterationCount = 2048;
 927  
 928                          $crypto = new DES();
 929                          $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount);
 930                          $RSAPrivateKey = $crypto->encrypt($RSAPrivateKey);
 931  
 932                          $parameters = pack(
 933                              'Ca*a*Ca*N',
 934                              self::ASN1_OCTETSTRING,
 935                              $this->_encodeLength(strlen($salt)),
 936                              $salt,
 937                              self::ASN1_INTEGER,
 938                              $this->_encodeLength(4),
 939                              $iterationCount
 940                          );
 941                          $pbeWithMD5AndDES_CBC = "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03";
 942  
 943                          $encryptionAlgorithm = pack(
 944                              'Ca*a*Ca*a*',
 945                              self::ASN1_OBJECT,
 946                              $this->_encodeLength(strlen($pbeWithMD5AndDES_CBC)),
 947                              $pbeWithMD5AndDES_CBC,
 948                              self::ASN1_SEQUENCE,
 949                              $this->_encodeLength(strlen($parameters)),
 950                              $parameters
 951                          );
 952  
 953                          $RSAPrivateKey = pack(
 954                              'Ca*a*Ca*a*',
 955                              self::ASN1_SEQUENCE,
 956                              $this->_encodeLength(strlen($encryptionAlgorithm)),
 957                              $encryptionAlgorithm,
 958                              self::ASN1_OCTETSTRING,
 959                              $this->_encodeLength(strlen($RSAPrivateKey)),
 960                              $RSAPrivateKey
 961                          );
 962  
 963                          $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
 964  
 965                          $RSAPrivateKey = "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" .
 966                                           chunk_split(base64_encode($RSAPrivateKey), 64) .
 967                                           '-----END ENCRYPTED PRIVATE KEY-----';
 968                      } else {
 969                          $RSAPrivateKey = "-----BEGIN PRIVATE KEY-----\r\n" .
 970                                           chunk_split(base64_encode($RSAPrivateKey), 64) .
 971                                           '-----END PRIVATE KEY-----';
 972                      }
 973                      return $RSAPrivateKey;
 974                  }
 975  
 976                  if (!empty($this->password) || is_string($this->password)) {
 977                      $iv = Random::string(8);
 978                      $symkey = pack('H*', md5($this->password . $iv)); // symkey is short for symmetric key
 979                      $symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8);
 980                      $des = new TripleDES();
 981                      $des->setKey($symkey);
 982                      $des->setIV($iv);
 983                      $iv = strtoupper(bin2hex($iv));
 984                      $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
 985                                       "Proc-Type: 4,ENCRYPTED\r\n" .
 986                                       "DEK-Info: DES-EDE3-CBC,$iv\r\n" .
 987                                       "\r\n" .
 988                                       chunk_split(base64_encode($des->encrypt($RSAPrivateKey)), 64) .
 989                                       '-----END RSA PRIVATE KEY-----';
 990                  } else {
 991                      $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
 992                                       chunk_split(base64_encode($RSAPrivateKey), 64) .
 993                                       '-----END RSA PRIVATE KEY-----';
 994                  }
 995  
 996                  return $RSAPrivateKey;
 997          }
 998      }
 999  
1000      /**
1001       * Convert a public key to the appropriate format
1002       *
1003       * @access private
1004       * @see self::setPublicKeyFormat()
1005       * @param Math_BigInteger $n
1006       * @param Math_BigInteger $e
1007       * @return string|array<string,Math_BigInteger>
1008       */
1009      function _convertPublicKey($n, $e)
1010      {
1011          $signed = $this->publicKeyFormat != self::PUBLIC_FORMAT_XML;
1012  
1013          $modulus = $n->toBytes($signed);
1014          $publicExponent = $e->toBytes($signed);
1015  
1016          switch ($this->publicKeyFormat) {
1017              case self::PUBLIC_FORMAT_RAW:
1018                  return array('e' => $e->copy(), 'n' => $n->copy());
1019              case self::PUBLIC_FORMAT_XML:
1020                  return "<RSAKeyValue>\r\n" .
1021                         '  <Modulus>' . base64_encode($modulus) . "</Modulus>\r\n" .
1022                         '  <Exponent>' . base64_encode($publicExponent) . "</Exponent>\r\n" .
1023                         '</RSAKeyValue>';
1024                  break;
1025              case self::PUBLIC_FORMAT_OPENSSH:
1026                  // from <http://tools.ietf.org/html/rfc4253#page-15>:
1027                  // string    "ssh-rsa"
1028                  // mpint     e
1029                  // mpint     n
1030                  $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus);
1031                  $RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment;
1032  
1033                  return $RSAPublicKey;
1034              default: // eg. self::PUBLIC_FORMAT_PKCS1_RAW or self::PUBLIC_FORMAT_PKCS1
1035                  // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.1>:
1036                  // RSAPublicKey ::= SEQUENCE {
1037                  //     modulus           INTEGER,  -- n
1038                  //     publicExponent    INTEGER   -- e
1039                  // }
1040                  $components = array(
1041                      'modulus' => pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus),
1042                      'publicExponent' => pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($publicExponent)), $publicExponent)
1043                  );
1044  
1045                  $RSAPublicKey = pack(
1046                      'Ca*a*a*',
1047                      self::ASN1_SEQUENCE,
1048                      $this->_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])),
1049                      $components['modulus'],
1050                      $components['publicExponent']
1051                  );
1052  
1053                  if ($this->publicKeyFormat == self::PUBLIC_FORMAT_PKCS1_RAW) {
1054                      $RSAPublicKey = "-----BEGIN RSA PUBLIC KEY-----\r\n" .
1055                                      chunk_split(base64_encode($RSAPublicKey), 64) .
1056                                      '-----END RSA PUBLIC KEY-----';
1057                  } else {
1058                      // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
1059                      $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
1060                      $RSAPublicKey = chr(0) . $RSAPublicKey;
1061                      $RSAPublicKey = chr(3) . $this->_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey;
1062  
1063                      $RSAPublicKey = pack(
1064                          'Ca*a*',
1065                          self::ASN1_SEQUENCE,
1066                          $this->_encodeLength(strlen($rsaOID . $RSAPublicKey)),
1067                          $rsaOID . $RSAPublicKey
1068                      );
1069  
1070                      $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
1071                                       chunk_split(base64_encode($RSAPublicKey), 64) .
1072                                       '-----END PUBLIC KEY-----';
1073                  }
1074  
1075                  return $RSAPublicKey;
1076          }
1077      }
1078  
1079      /**
1080       * Break a public or private key down into its constituant components
1081       *
1082       * @access private
1083       * @see self::_convertPublicKey()
1084       * @see self::_convertPrivateKey()
1085       * @param string|array $key
1086       * @param int $type
1087       * @return array|bool
1088       */
1089      function _parseKey($key, $type)
1090      {
1091          if ($type != self::PUBLIC_FORMAT_RAW && !is_string($key)) {
1092              return false;
1093          }
1094  
1095          switch ($type) {
1096              case self::PUBLIC_FORMAT_RAW:
1097                  if (!is_array($key)) {
1098                      return false;
1099                  }
1100                  $components = array();
1101                  switch (true) {
1102                      case isset($key['e']):
1103                          $components['publicExponent'] = $key['e']->copy();
1104                          break;
1105                      case isset($key['exponent']):
1106                          $components['publicExponent'] = $key['exponent']->copy();
1107                          break;
1108                      case isset($key['publicExponent']):
1109                          $components['publicExponent'] = $key['publicExponent']->copy();
1110                          break;
1111                      case isset($key[0]):
1112                          $components['publicExponent'] = $key[0]->copy();
1113                  }
1114                  switch (true) {
1115                      case isset($key['n']):
1116                          $components['modulus'] = $key['n']->copy();
1117                          break;
1118                      case isset($key['modulo']):
1119                          $components['modulus'] = $key['modulo']->copy();
1120                          break;
1121                      case isset($key['modulus']):
1122                          $components['modulus'] = $key['modulus']->copy();
1123                          break;
1124                      case isset($key[1]):
1125                          $components['modulus'] = $key[1]->copy();
1126                  }
1127                  return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false;
1128              case self::PRIVATE_FORMAT_PKCS1:
1129              case self::PRIVATE_FORMAT_PKCS8:
1130              case self::PUBLIC_FORMAT_PKCS1:
1131                  /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
1132                     "outside the scope" of PKCS#1.  PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
1133                     protect private keys, however, that's not what OpenSSL* does.  OpenSSL protects private keys by adding
1134                     two new "fields" to the key - DEK-Info and Proc-Type.  These fields are discussed here:
1135  
1136                     http://tools.ietf.org/html/rfc1421#section-4.6.1.1
1137                     http://tools.ietf.org/html/rfc1421#section-4.6.1.3
1138  
1139                     DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell.
1140                     DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation
1141                     function.  As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's
1142                     own implementation.  ie. the implementation *is* the standard and any bugs that may exist in that
1143                     implementation are part of the standard, as well.
1144  
1145                     * OpenSSL is the de facto standard.  It's utilized by OpenSSH and other projects */
1146                  if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
1147                      $iv = pack('H*', trim($matches[2]));
1148                      $symkey = pack('H*', md5($this->password . substr($iv, 0, 8))); // symkey is short for symmetric key
1149                      $symkey.= pack('H*', md5($symkey . $this->password . substr($iv, 0, 8)));
1150                      // remove the Proc-Type / DEK-Info sections as they're no longer needed
1151                      $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key);
1152                      $ciphertext = $this->_extractBER($key);
1153                      if ($ciphertext === false) {
1154                          $ciphertext = $key;
1155                      }
1156                      switch ($matches[1]) {
1157                          case 'AES-256-CBC':
1158                              $crypto = new AES();
1159                              break;
1160                          case 'AES-128-CBC':
1161                              $symkey = substr($symkey, 0, 16);
1162                              $crypto = new AES();
1163                              break;
1164                          case 'DES-EDE3-CFB':
1165                              $crypto = new TripleDES(Base::MODE_CFB);
1166                              break;
1167                          case 'DES-EDE3-CBC':
1168                              $symkey = substr($symkey, 0, 24);
1169                              $crypto = new TripleDES();
1170                              break;
1171                          case 'DES-CBC':
1172                              $crypto = new DES();
1173                              break;
1174                          default:
1175                              return false;
1176                      }
1177                      $crypto->setKey($symkey);
1178                      $crypto->setIV($iv);
1179                      $decoded = $crypto->decrypt($ciphertext);
1180                  } else {
1181                      $decoded = $this->_extractBER($key);
1182                  }
1183  
1184                  if ($decoded !== false) {
1185                      $key = $decoded;
1186                  }
1187  
1188                  $components = array();
1189  
1190                  if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
1191                      return false;
1192                  }
1193                  if ($this->_decodeLength($key) != strlen($key)) {
1194                      return false;
1195                  }
1196  
1197                  $tag = ord($this->_string_shift($key));
1198                  /* intended for keys for which OpenSSL's asn1parse returns the following:
1199  
1200                      0:d=0  hl=4 l= 631 cons: SEQUENCE
1201                      4:d=1  hl=2 l=   1 prim:  INTEGER           :00
1202                      7:d=1  hl=2 l=  13 cons:  SEQUENCE
1203                      9:d=2  hl=2 l=   9 prim:   OBJECT            :rsaEncryption
1204                     20:d=2  hl=2 l=   0 prim:   NULL
1205                     22:d=1  hl=4 l= 609 prim:  OCTET STRING
1206  
1207                     ie. PKCS8 keys*/
1208  
1209                  if ($tag == self::ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") {
1210                      $this->_string_shift($key, 3);
1211                      $tag = self::ASN1_SEQUENCE;
1212                  }
1213  
1214                  if ($tag == self::ASN1_SEQUENCE) {
1215                      $temp = $this->_string_shift($key, $this->_decodeLength($key));
1216                      if (ord($this->_string_shift($temp)) != self::ASN1_OBJECT) {
1217                          return false;
1218                      }
1219                      $length = $this->_decodeLength($temp);
1220                      switch ($this->_string_shift($temp, $length)) {
1221                          case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption
1222                          case "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x0A": // rsaPSS
1223                              break;
1224                          case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC
1225                              /*
1226                                 PBEParameter ::= SEQUENCE {
1227                                     salt OCTET STRING (SIZE(8)),
1228                                     iterationCount INTEGER }
1229                              */
1230                              if (ord($this->_string_shift($temp)) != self::ASN1_SEQUENCE) {
1231                                  return false;
1232                              }
1233                              if ($this->_decodeLength($temp) != strlen($temp)) {
1234                                  return false;
1235                              }
1236                              $this->_string_shift($temp); // assume it's an octet string
1237                              $salt = $this->_string_shift($temp, $this->_decodeLength($temp));
1238                              if (ord($this->_string_shift($temp)) != self::ASN1_INTEGER) {
1239                                  return false;
1240                              }
1241                              $this->_decodeLength($temp);
1242                              list(, $iterationCount) = unpack('N', str_pad($temp, 4, chr(0), STR_PAD_LEFT));
1243                              $this->_string_shift($key); // assume it's an octet string
1244                              $length = $this->_decodeLength($key);
1245                              if (strlen($key) != $length) {
1246                                  return false;
1247                              }
1248  
1249                              $crypto = new DES();
1250                              $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount);
1251                              $key = $crypto->decrypt($key);
1252                              if ($key === false) {
1253                                  return false;
1254                              }
1255                              return $this->_parseKey($key, self::PRIVATE_FORMAT_PKCS1);
1256                          default:
1257                              return false;
1258                      }
1259                      /* intended for keys for which OpenSSL's asn1parse returns the following:
1260  
1261                          0:d=0  hl=4 l= 290 cons: SEQUENCE
1262                          4:d=1  hl=2 l=  13 cons:  SEQUENCE
1263                          6:d=2  hl=2 l=   9 prim:   OBJECT            :rsaEncryption
1264                         17:d=2  hl=2 l=   0 prim:   NULL
1265                         19:d=1  hl=4 l= 271 prim:  BIT STRING */
1266                      $tag = ord($this->_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag
1267                      $this->_decodeLength($key); // skip over the BIT STRING / OCTET STRING length
1268                      // "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of
1269                      //  unused bits in the final subsequent octet. The number shall be in the range zero to seven."
1270                      //  -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2)
1271                      if ($tag == self::ASN1_BITSTRING) {
1272                          $this->_string_shift($key);
1273                      }
1274                      if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
1275                          return false;
1276                      }
1277                      if ($this->_decodeLength($key) != strlen($key)) {
1278                          return false;
1279                      }
1280                      $tag = ord($this->_string_shift($key));
1281                  }
1282                  if ($tag != self::ASN1_INTEGER) {
1283                      return false;
1284                  }
1285  
1286                  $length = $this->_decodeLength($key);
1287                  $temp = $this->_string_shift($key, $length);
1288                  if (strlen($temp) != 1 || ord($temp) > 2) {
1289                      $components['modulus'] = new BigInteger($temp, 256);
1290                      $this->_string_shift($key); // skip over self::ASN1_INTEGER
1291                      $length = $this->_decodeLength($key);
1292                      $components[$type == self::PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = new BigInteger($this->_string_shift($key, $length), 256);
1293  
1294                      return $components;
1295                  }
1296                  if (ord($this->_string_shift($key)) != self::ASN1_INTEGER) {
1297                      return false;
1298                  }
1299                  $length = $this->_decodeLength($key);
1300                  $components['modulus'] = new BigInteger($this->_string_shift($key, $length), 256);
1301                  $this->_string_shift($key);
1302                  $length = $this->_decodeLength($key);
1303                  $components['publicExponent'] = new BigInteger($this->_string_shift($key, $length), 256);
1304                  $this->_string_shift($key);
1305                  $length = $this->_decodeLength($key);
1306                  $components['privateExponent'] = new BigInteger($this->_string_shift($key, $length), 256);
1307                  $this->_string_shift($key);
1308                  $length = $this->_decodeLength($key);
1309                  $components['primes'] = array(1 => new BigInteger($this->_string_shift($key, $length), 256));
1310                  $this->_string_shift($key);
1311                  $length = $this->_decodeLength($key);
1312                  $components['primes'][] = new BigInteger($this->_string_shift($key, $length), 256);
1313                  $this->_string_shift($key);
1314                  $length = $this->_decodeLength($key);
1315                  $components['exponents'] = array(1 => new BigInteger($this->_string_shift($key, $length), 256));
1316                  $this->_string_shift($key);
1317                  $length = $this->_decodeLength($key);
1318                  $components['exponents'][] = new BigInteger($this->_string_shift($key, $length), 256);
1319                  $this->_string_shift($key);
1320                  $length = $this->_decodeLength($key);
1321                  $components['coefficients'] = array(2 => new BigInteger($this->_string_shift($key, $length), 256));
1322  
1323                  if (!empty($key)) {
1324                      if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
1325                          return false;
1326                      }
1327                      $this->_decodeLength($key);
1328                      while (!empty($key)) {
1329                          if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
1330                              return false;
1331                          }
1332                          $this->_decodeLength($key);
1333                          $key = substr($key, 1);
1334                          $length = $this->_decodeLength($key);
1335                          $components['primes'][] = new BigInteger($this->_string_shift($key, $length), 256);
1336                          $this->_string_shift($key);
1337                          $length = $this->_decodeLength($key);
1338                          $components['exponents'][] = new BigInteger($this->_string_shift($key, $length), 256);
1339                          $this->_string_shift($key);
1340                          $length = $this->_decodeLength($key);
1341                          $components['coefficients'][] = new BigInteger($this->_string_shift($key, $length), 256);
1342                      }
1343                  }
1344  
1345                  return $components;
1346              case self::PUBLIC_FORMAT_OPENSSH:
1347                  $parts = explode(' ', $key, 3);
1348  
1349                  $key = isset($parts[1]) ? base64_decode($parts[1]) : false;
1350                  if ($key === false) {
1351                      return false;
1352                  }
1353  
1354                  $comment = isset($parts[2]) ? $parts[2] : false;
1355  
1356                  $cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa";
1357  
1358                  if (strlen($key) <= 4) {
1359                      return false;
1360                  }
1361                  extract(unpack('Nlength', $this->_string_shift($key, 4)));
1362                  $publicExponent = new BigInteger($this->_string_shift($key, $length), -256);
1363                  if (strlen($key) <= 4) {
1364                      return false;
1365                  }
1366                  extract(unpack('Nlength', $this->_string_shift($key, 4)));
1367                  $modulus = new BigInteger($this->_string_shift($key, $length), -256);
1368  
1369                  if ($cleanup && strlen($key)) {
1370                      if (strlen($key) <= 4) {
1371                          return false;
1372                      }
1373                      extract(unpack('Nlength', $this->_string_shift($key, 4)));
1374                      $realModulus = new BigInteger($this->_string_shift($key, $length), -256);
1375                      return strlen($key) ? false : array(
1376                          'modulus' => $realModulus,
1377                          'publicExponent' => $modulus,
1378                          'comment' => $comment
1379                      );
1380                  } else {
1381                      return strlen($key) ? false : array(
1382                          'modulus' => $modulus,
1383                          'publicExponent' => $publicExponent,
1384                          'comment' => $comment
1385                      );
1386                  }
1387              // http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue
1388              // http://en.wikipedia.org/wiki/XML_Signature
1389              case self::PRIVATE_FORMAT_XML:
1390              case self::PUBLIC_FORMAT_XML:
1391                  if (!extension_loaded('xml')) {
1392                      return false;
1393                  }
1394  
1395                  $this->components = array();
1396  
1397                  $xml = xml_parser_create('UTF-8');
1398                  xml_set_object($xml, $this);
1399                  xml_set_element_handler($xml, '_start_element_handler', '_stop_element_handler');
1400                  xml_set_character_data_handler($xml, '_data_handler');
1401                  // add <xml></xml> to account for "dangling" tags like <BitStrength>...</BitStrength> that are sometimes added
1402                  if (!xml_parse($xml, '<xml>' . $key . '</xml>')) {
1403                      xml_parser_free($xml);
1404                      unset($xml);
1405                      return false;
1406                  }
1407  
1408                  xml_parser_free($xml);
1409                  unset($xml);
1410  
1411                  return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false;
1412              // see PuTTY's SSHPUBK.C and https://tartarus.org/~simon/putty-snapshots/htmldoc/AppendixC.html
1413              case self::PRIVATE_FORMAT_PUTTY:
1414                  $components = array();
1415                  $key = preg_split('#\r\n|\r|\n#', $key);
1416                  if ($this->_string_shift($key[0], strlen('PuTTY-User-Key-File-')) != 'PuTTY-User-Key-File-') {
1417                      return false;
1418                  }
1419                  $version = (int) $this->_string_shift($key[0], 3); // should be either "2: " or "3: 0" prior to int casting
1420                  if ($version != 2 && $version != 3) {
1421                      return false;
1422                  }
1423                  $type = rtrim($key[0]);
1424                  if ($type != 'ssh-rsa') {
1425                      return false;
1426                  }
1427                  $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1]));
1428                  $comment = trim(preg_replace('#Comment: (.+)#', '$1', $key[2]));
1429  
1430                  $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3]));
1431                  $public = base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength))));
1432                  $public = substr($public, 11);
1433                  extract(unpack('Nlength', $this->_string_shift($public, 4)));
1434                  $components['publicExponent'] = new BigInteger($this->_string_shift($public, $length), -256);
1435                  extract(unpack('Nlength', $this->_string_shift($public, 4)));
1436                  $components['modulus'] = new BigInteger($this->_string_shift($public, $length), -256);
1437  
1438                  $offset = $publicLength + 4;
1439                  switch ($encryption) {
1440                      case 'aes256-cbc':
1441                          $crypto = new AES();
1442                          switch ($version) {
1443                              case 3:
1444                                  if (!function_exists('sodium_crypto_pwhash')) {
1445                                      return false;
1446                                  }
1447                                  $flavour = trim(preg_replace('#Key-Derivation: (.*)#', '$1', $key[$offset++]));
1448                                  switch ($flavour) {
1449                                      case 'Argon2i':
1450                                          $flavour = SODIUM_CRYPTO_PWHASH_ALG_ARGON2I13;
1451                                          break;
1452                                      case 'Argon2id':
1453                                          $flavour = SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13;
1454                                          break;
1455                                      default:
1456                                          return false;
1457                                  }
1458                                  $memory = trim(preg_replace('#Argon2-Memory: (\d+)#', '$1', $key[$offset++]));
1459                                  $passes = trim(preg_replace('#Argon2-Passes: (\d+)#', '$1', $key[$offset++]));
1460                                  $parallelism = trim(preg_replace('#Argon2-Parallelism: (\d+)#', '$1', $key[$offset++]));
1461                                  $salt = pack('H*', trim(preg_replace('#Argon2-Salt: ([0-9a-f]+)#', '$1', $key[$offset++])));
1462  
1463                                  $length = 80; // keylen + ivlen + mac_keylen
1464                                  $temp = sodium_crypto_pwhash($length, $this->password, $salt, $passes, $memory << 10, $flavour);
1465  
1466                                  $symkey = substr($temp, 0, 32);
1467                                  $symiv = substr($temp, 32, 16);
1468                                  break;
1469                              case 2:
1470                                  $symkey = '';
1471                                  $sequence = 0;
1472                                  while (strlen($symkey) < 32) {
1473                                      $temp = pack('Na*', $sequence++, $this->password);
1474                                      $symkey.= pack('H*', sha1($temp));
1475                                  }
1476                                  $symkey = substr($symkey, 0, 32);
1477                                  $symiv = str_repeat("\0", 16);
1478                          }
1479                  }
1480  
1481                  $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$offset++]));
1482                  $private = base64_decode(implode('', array_map('trim', array_slice($key, $offset, $privateLength))));
1483  
1484                  if ($encryption != 'none') {
1485                      $crypto->setKey($symkey);
1486                      $crypto->setIV($symiv);
1487                      $crypto->disablePadding();
1488                      $private = $crypto->decrypt($private);
1489                      if ($private === false) {
1490                          return false;
1491                      }
1492                  }
1493  
1494                  extract(unpack('Nlength', $this->_string_shift($private, 4)));
1495                  if (strlen($private) < $length) {
1496                      return false;
1497                  }
1498                  $components['privateExponent'] = new BigInteger($this->_string_shift($private, $length), -256);
1499                  extract(unpack('Nlength', $this->_string_shift($private, 4)));
1500                  if (strlen($private) < $length) {
1501                      return false;
1502                  }
1503                  $components['primes'] = array(1 => new BigInteger($this->_string_shift($private, $length), -256));
1504                  extract(unpack('Nlength', $this->_string_shift($private, 4)));
1505                  if (strlen($private) < $length) {
1506                      return false;
1507                  }
1508                  $components['primes'][] = new BigInteger($this->_string_shift($private, $length), -256);
1509  
1510                  $temp = $components['primes'][1]->subtract($this->one);
1511                  $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp));
1512                  $temp = $components['primes'][2]->subtract($this->one);
1513                  $components['exponents'][] = $components['publicExponent']->modInverse($temp);
1514  
1515                  extract(unpack('Nlength', $this->_string_shift($private, 4)));
1516                  if (strlen($private) < $length) {
1517                      return false;
1518                  }
1519                  $components['coefficients'] = array(2 => new BigInteger($this->_string_shift($private, $length), -256));
1520  
1521                  return $components;
1522              case self::PRIVATE_FORMAT_OPENSSH:
1523                  $components = array();
1524                  $decoded = $this->_extractBER($key);
1525                  $magic = $this->_string_shift($decoded, 15);
1526                  if ($magic !== "openssh-key-v1\0") {
1527                      return false;
1528                  }
1529                  extract(unpack('Nlength', $this->_string_shift($decoded, 4)));
1530                  if (strlen($decoded) < $length) {
1531                      return false;
1532                  }
1533                  $ciphername = $this->_string_shift($decoded, $length);
1534                  extract(unpack('Nlength', $this->_string_shift($decoded, 4)));
1535                  if (strlen($decoded) < $length) {
1536                      return false;
1537                  }
1538                  $kdfname = $this->_string_shift($decoded, $length);
1539                  extract(unpack('Nlength', $this->_string_shift($decoded, 4)));
1540                  if (strlen($decoded) < $length) {
1541                      return false;
1542                  }
1543                  $kdfoptions = $this->_string_shift($decoded, $length);
1544                  extract(unpack('Nnumkeys', $this->_string_shift($decoded, 4)));
1545                  if ($numkeys != 1 || ($ciphername != 'none' && $kdfname != 'bcrypt')) {
1546                      return false;
1547                  }
1548                  switch ($ciphername) {
1549                      case 'none':
1550                          break;
1551                      case 'aes256-ctr':
1552                          extract(unpack('Nlength', $this->_string_shift($kdfoptions, 4)));
1553                          if (strlen($kdfoptions) < $length) {
1554                              return false;
1555                          }
1556                          $salt = $this->_string_shift($kdfoptions, $length);
1557                          extract(unpack('Nrounds', $this->_string_shift($kdfoptions, 4)));
1558                          $crypto = new AES(AES::MODE_CTR);
1559                          $crypto->disablePadding();
1560                          if (!$crypto->setPassword($this->password, 'bcrypt', $salt, $rounds, 32)) {
1561                              return false;
1562                          }
1563                          break;
1564                      default:
1565                          return false;
1566                  }
1567                  extract(unpack('Nlength', $this->_string_shift($decoded, 4)));
1568                  if (strlen($decoded) < $length) {
1569                      return false;
1570                  }
1571                  $publicKey = $this->_string_shift($decoded, $length);
1572                  extract(unpack('Nlength', $this->_string_shift($decoded, 4)));
1573                  if (strlen($decoded) < $length) {
1574                      return false;
1575                  }
1576  
1577                  if ($this->_string_shift($publicKey, 11) !== "\0\0\0\7ssh-rsa") {
1578                      return false;
1579                  }
1580  
1581                  $paddedKey = $this->_string_shift($decoded, $length);
1582                  if (isset($crypto)) {
1583                      $paddedKey = $crypto->decrypt($paddedKey);
1584                  }
1585  
1586                  $checkint1 = $this->_string_shift($paddedKey, 4);
1587                  $checkint2 = $this->_string_shift($paddedKey, 4);
1588                  if (strlen($checkint1) != 4 || $checkint1 !== $checkint2) {
1589                      return false;
1590                  }
1591  
1592                  if ($this->_string_shift($paddedKey, 11) !== "\0\0\0\7ssh-rsa") {
1593                      return false;
1594                  }
1595  
1596                  $values = array(
1597                      &$components['modulus'],
1598                      &$components['publicExponent'],
1599                      &$components['privateExponent'],
1600                      &$components['coefficients'][2],
1601                      &$components['primes'][1],
1602                      &$components['primes'][2]
1603                  );
1604  
1605                  foreach ($values as &$value) {
1606                      extract(unpack('Nlength', $this->_string_shift($paddedKey, 4)));
1607                      if (strlen($paddedKey) < $length) {
1608                          return false;
1609                      }
1610                      $value = new BigInteger($this->_string_shift($paddedKey, $length), -256);
1611                  }
1612  
1613                  extract(unpack('Nlength', $this->_string_shift($paddedKey, 4)));
1614                  if (strlen($paddedKey) < $length) {
1615                      return false;
1616                  }
1617                  $components['comment'] = $this->_string_shift($decoded, $length);
1618  
1619                  $temp = $components['primes'][1]->subtract($this->one);
1620                  $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp));
1621                  $temp = $components['primes'][2]->subtract($this->one);
1622                  $components['exponents'][] = $components['publicExponent']->modInverse($temp);
1623  
1624                  return $components;
1625          }
1626  
1627          return false;
1628      }
1629  
1630      /**
1631       * Returns the key size
1632       *
1633       * More specifically, this returns the size of the modulo in bits.
1634       *
1635       * @access public
1636       * @return int
1637       */
1638      function getSize()
1639      {
1640          return !isset($this->modulus) ? 0 : strlen($this->modulus->toBits());
1641      }
1642  
1643      /**
1644       * Start Element Handler
1645       *
1646       * Called by xml_set_element_handler()
1647       *
1648       * @access private
1649       * @param resource $parser
1650       * @param string $name
1651       * @param array $attribs
1652       */
1653      function _start_element_handler($parser, $name, $attribs)
1654      {
1655          //$name = strtoupper($name);
1656          switch ($name) {
1657              case 'MODULUS':
1658                  $this->current = &$this->components['modulus'];
1659                  break;
1660              case 'EXPONENT':
1661                  $this->current = &$this->components['publicExponent'];
1662                  break;
1663              case 'P':
1664                  $this->current = &$this->components['primes'][1];
1665                  break;
1666              case 'Q':
1667                  $this->current = &$this->components['primes'][2];
1668                  break;
1669              case 'DP':
1670                  $this->current = &$this->components['exponents'][1];
1671                  break;
1672              case 'DQ':
1673                  $this->current = &$this->components['exponents'][2];
1674                  break;
1675              case 'INVERSEQ':
1676                  $this->current = &$this->components['coefficients'][2];
1677                  break;
1678              case 'D':
1679                  $this->current = &$this->components['privateExponent'];
1680          }
1681          $this->current = '';
1682      }
1683  
1684      /**
1685       * Stop Element Handler
1686       *
1687       * Called by xml_set_element_handler()
1688       *
1689       * @access private
1690       * @param resource $parser
1691       * @param string $name
1692       */
1693      function _stop_element_handler($parser, $name)
1694      {
1695          if (isset($this->current)) {
1696              $this->current = new BigInteger(base64_decode($this->current), 256);
1697              unset($this->current);
1698          }
1699      }
1700  
1701      /**
1702       * Data Handler
1703       *
1704       * Called by xml_set_character_data_handler()
1705       *
1706       * @access private
1707       * @param resource $parser
1708       * @param string $data
1709       */
1710      function _data_handler($parser, $data)
1711      {
1712          if (!isset($this->current) || is_object($this->current)) {
1713              return;
1714          }
1715          $this->current.= trim($data);
1716      }
1717  
1718      /**
1719       * Loads a public or private key
1720       *
1721       * Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed)
1722       *
1723       * @access public
1724       * @param string|RSA|array $key
1725       * @param bool|int $type optional
1726       * @return bool
1727       */
1728      function loadKey($key, $type = false)
1729      {
1730          if ($key instanceof RSA) {
1731              $this->privateKeyFormat = $key->privateKeyFormat;
1732              $this->publicKeyFormat = $key->publicKeyFormat;
1733              $this->k = $key->k;
1734              $this->hLen = $key->hLen;
1735              $this->sLen = $key->sLen;
1736              $this->mgfHLen = $key->mgfHLen;
1737              $this->encryptionMode = $key->encryptionMode;
1738              $this->signatureMode = $key->signatureMode;
1739              $this->password = $key->password;
1740              $this->configFile = $key->configFile;
1741              $this->comment = $key->comment;
1742  
1743              if (is_object($key->hash)) {
1744                  $this->hash = new Hash($key->hash->getHash());
1745              }
1746              if (is_object($key->mgfHash)) {
1747                  $this->mgfHash = new Hash($key->mgfHash->getHash());
1748              }
1749  
1750              if (is_object($key->modulus)) {
1751                  $this->modulus = $key->modulus->copy();
1752              }
1753              if (is_object($key->exponent)) {
1754                  $this->exponent = $key->exponent->copy();
1755              }
1756              if (is_object($key->publicExponent)) {
1757                  $this->publicExponent = $key->publicExponent->copy();
1758              }
1759  
1760              $this->primes = array();
1761              $this->exponents = array();
1762              $this->coefficients = array();
1763  
1764              foreach ($this->primes as $prime) {
1765                  $this->primes[] = $prime->copy();
1766              }
1767              foreach ($this->exponents as $exponent) {
1768                  $this->exponents[] = $exponent->copy();
1769              }
1770              foreach ($this->coefficients as $coefficient) {
1771                  $this->coefficients[] = $coefficient->copy();
1772              }
1773  
1774              return true;
1775          }
1776  
1777          if ($type === false) {
1778              $types = array(
1779                  self::PUBLIC_FORMAT_RAW,
1780                  self::PRIVATE_FORMAT_PKCS1,
1781                  self::PRIVATE_FORMAT_XML,
1782                  self::PRIVATE_FORMAT_PUTTY,
1783                  self::PUBLIC_FORMAT_OPENSSH,
1784                  self::PRIVATE_FORMAT_OPENSSH
1785              );
1786              foreach ($types as $type) {
1787                  $components = $this->_parseKey($key, $type);
1788                  if ($components !== false) {
1789                      break;
1790                  }
1791              }
1792          } else {
1793              $components = $this->_parseKey($key, $type);
1794          }
1795  
1796          if ($components === false) {
1797              $this->comment = null;
1798              $this->modulus = null;
1799              $this->k = null;
1800              $this->exponent = null;
1801              $this->primes = null;
1802              $this->exponents = null;
1803              $this->coefficients = null;
1804              $this->publicExponent = null;
1805  
1806              return false;
1807          }
1808  
1809          if (isset($components['comment']) && $components['comment'] !== false) {
1810              $this->comment = $components['comment'];
1811          }
1812          $this->modulus = $components['modulus'];
1813          $this->k = strlen($this->modulus->toBytes());
1814          $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent'];
1815          if (isset($components['primes'])) {
1816              $this->primes = $components['primes'];
1817              $this->exponents = $components['exponents'];
1818              $this->coefficients = $components['coefficients'];
1819              $this->publicExponent = $components['publicExponent'];
1820          } else {
1821              $this->primes = array();
1822              $this->exponents = array();
1823              $this->coefficients = array();
1824              $this->publicExponent = false;
1825          }
1826  
1827          switch ($type) {
1828              case self::PUBLIC_FORMAT_OPENSSH:
1829              case self::PUBLIC_FORMAT_RAW:
1830                  $this->setPublicKey();
1831                  break;
1832              case self::PRIVATE_FORMAT_PKCS1:
1833                  switch (true) {
1834                      case strpos($key, '-BEGIN PUBLIC KEY-') !== false:
1835                      case strpos($key, '-BEGIN RSA PUBLIC KEY-') !== false:
1836                          $this->setPublicKey();
1837                  }
1838          }
1839  
1840          return true;
1841      }
1842  
1843      /**
1844       * Sets the password
1845       *
1846       * Private keys can be encrypted with a password.  To unset the password, pass in the empty string or false.
1847       * Or rather, pass in $password such that empty($password) && !is_string($password) is true.
1848       *
1849       * @see self::createKey()
1850       * @see self::loadKey()
1851       * @access public
1852       * @param string $password
1853       */
1854      function setPassword($password = false)
1855      {
1856          $this->password = $password;
1857      }
1858  
1859      /**
1860       * Defines the public key
1861       *
1862       * Some private key formats define the public exponent and some don't.  Those that don't define it are problematic when
1863       * used in certain contexts.  For example, in SSH-2, RSA authentication works by sending the public key along with a
1864       * message signed by the private key to the server.  The SSH-2 server looks the public key up in an index of public keys
1865       * and if it's present then proceeds to verify the signature.  Problem is, if your private key doesn't include the public
1866       * exponent this won't work unless you manually add the public exponent. phpseclib tries to guess if the key being used
1867       * is the public key but in the event that it guesses incorrectly you might still want to explicitly set the key as being
1868       * public.
1869       *
1870       * Do note that when a new key is loaded the index will be cleared.
1871       *
1872       * Returns true on success, false on failure
1873       *
1874       * @see self::getPublicKey()
1875       * @access public
1876       * @param string $key optional
1877       * @param int $type optional
1878       * @return bool
1879       */
1880      function setPublicKey($key = false, $type = false)
1881      {
1882          // if a public key has already been loaded return false
1883          if (!empty($this->publicExponent)) {
1884              return false;
1885          }
1886  
1887          if ($key === false && !empty($this->modulus)) {
1888              $this->publicExponent = $this->exponent;
1889              return true;
1890          }
1891  
1892          if ($type === false) {
1893              $types = array(
1894                  self::PUBLIC_FORMAT_RAW,
1895                  self::PUBLIC_FORMAT_PKCS1,
1896                  self::PUBLIC_FORMAT_XML,
1897                  self::PUBLIC_FORMAT_OPENSSH
1898              );
1899              foreach ($types as $type) {
1900                  $components = $this->_parseKey($key, $type);
1901                  if ($components !== false) {
1902                      break;
1903                  }
1904              }
1905          } else {
1906              $components = $this->_parseKey($key, $type);
1907          }
1908  
1909          if ($components === false) {
1910              return false;
1911          }
1912  
1913          if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) {
1914              $this->modulus = $components['modulus'];
1915              $this->exponent = $this->publicExponent = $components['publicExponent'];
1916              return true;
1917          }
1918  
1919          $this->publicExponent = $components['publicExponent'];
1920  
1921          return true;
1922      }
1923  
1924      /**
1925       * Defines the private key
1926       *
1927       * If phpseclib guessed a private key was a public key and loaded it as such it might be desirable to force
1928       * phpseclib to treat the key as a private key. This function will do that.
1929       *
1930       * Do note that when a new key is loaded the index will be cleared.
1931       *
1932       * Returns true on success, false on failure
1933       *
1934       * @see self::getPublicKey()
1935       * @access public
1936       * @param string $key optional
1937       * @param int $type optional
1938       * @return bool
1939       */
1940      function setPrivateKey($key = false, $type = false)
1941      {
1942          if ($key === false && !empty($this->publicExponent)) {
1943              $this->publicExponent = false;
1944              return true;
1945          }
1946  
1947          $rsa = new RSA();
1948          if (!$rsa->loadKey($key, $type)) {
1949              return false;
1950          }
1951          $rsa->publicExponent = false;
1952  
1953          // don't overwrite the old key if the new key is invalid
1954          $this->loadKey($rsa);
1955          return true;
1956      }
1957  
1958      /**
1959       * Returns the public key
1960       *
1961       * The public key is only returned under two circumstances - if the private key had the public key embedded within it
1962       * or if the public key was set via setPublicKey().  If the currently loaded key is supposed to be the public key this
1963       * function won't return it since this library, for the most part, doesn't distinguish between public and private keys.
1964       *
1965       * @see self::getPublicKey()
1966       * @access public
1967       * @param int $type optional
1968       */
1969      function getPublicKey($type = self::PUBLIC_FORMAT_PKCS8)
1970      {
1971          if (empty($this->modulus) || empty($this->publicExponent)) {
1972              return false;
1973          }
1974  
1975          $oldFormat = $this->publicKeyFormat;
1976          $this->publicKeyFormat = $type;
1977          $temp = $this->_convertPublicKey($this->modulus, $this->publicExponent);
1978          $this->publicKeyFormat = $oldFormat;
1979          return $temp;
1980      }
1981  
1982      /**
1983       * Returns the public key's fingerprint
1984       *
1985       * The public key's fingerprint is returned, which is equivalent to running `ssh-keygen -lf rsa.pub`. If there is
1986       * no public key currently loaded, false is returned.
1987       * Example output (md5): "c1:b1:30:29:d7:b8:de:6c:97:77:10:d7:46:41:63:87" (as specified by RFC 4716)
1988       *
1989       * @access public
1990       * @param string $algorithm The hashing algorithm to be used. Valid options are 'md5' and 'sha256'. False is returned
1991       * for invalid values.
1992       * @return mixed
1993       */
1994      function getPublicKeyFingerprint($algorithm = 'md5')
1995      {
1996          if (empty($this->modulus) || empty($this->publicExponent)) {
1997              return false;
1998          }
1999  
2000          $modulus = $this->modulus->toBytes(true);
2001          $publicExponent = $this->publicExponent->toBytes(true);
2002  
2003          $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus);
2004  
2005          switch ($algorithm) {
2006              case 'sha256':
2007                  $hash = new Hash('sha256');
2008                  $base = base64_encode($hash->hash($RSAPublicKey));
2009                  return substr($base, 0, strlen($base) - 1);
2010              case 'md5':
2011                  return substr(chunk_split(md5($RSAPublicKey), 2, ':'), 0, -1);
2012              default:
2013                  return false;
2014          }
2015      }
2016  
2017      /**
2018       * Returns the private key
2019       *
2020       * The private key is only returned if the currently loaded key contains the constituent prime numbers.
2021       *
2022       * @see self::getPublicKey()
2023       * @access public
2024       * @param int $type optional
2025       * @return mixed
2026       */
2027      function getPrivateKey($type = self::PUBLIC_FORMAT_PKCS1)
2028      {
2029          if (empty($this->primes)) {
2030              return false;
2031          }
2032  
2033          $oldFormat = $this->privateKeyFormat;
2034          $this->privateKeyFormat = $type;
2035          $temp = $this->_convertPrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients);
2036          $this->privateKeyFormat = $oldFormat;
2037          return $temp;
2038      }
2039  
2040      /**
2041       * Returns a minimalistic private key
2042       *
2043       * Returns the private key without the prime number constituants.  Structurally identical to a public key that
2044       * hasn't been set as the public key
2045       *
2046       * @see self::getPrivateKey()
2047       * @access private
2048       * @param int $mode optional
2049       */
2050      function _getPrivatePublicKey($mode = self::PUBLIC_FORMAT_PKCS8)
2051      {
2052          if (empty($this->modulus) || empty($this->exponent)) {
2053              return false;
2054          }
2055  
2056          $oldFormat = $this->publicKeyFormat;
2057          $this->publicKeyFormat = $mode;
2058          $temp = $this->_convertPublicKey($this->modulus, $this->exponent);
2059          $this->publicKeyFormat = $oldFormat;
2060          return $temp;
2061      }
2062  
2063      /**
2064       *  __toString() magic method
2065       *
2066       * @access public
2067       * @return string
2068       */
2069      function __toString()
2070      {
2071          $key = $this->getPrivateKey($this->privateKeyFormat);
2072          if ($key !== false) {
2073              return $key;
2074          }
2075          $key = $this->_getPrivatePublicKey($this->publicKeyFormat);
2076          return $key !== false ? $key : '';
2077      }
2078  
2079      /**
2080       *  __clone() magic method
2081       *
2082       * @access public
2083       * @return Crypt_RSA
2084       */
2085      function __clone()
2086      {
2087          $key = new RSA();
2088          $key->loadKey($this);
2089          return $key;
2090      }
2091  
2092      /**
2093       * Generates the smallest and largest numbers requiring $bits bits
2094       *
2095       * @access private
2096       * @param int $bits
2097       * @return array
2098       */
2099      function _generateMinMax($bits)
2100      {
2101          $bytes = $bits >> 3;
2102          $min = str_repeat(chr(0), $bytes);
2103          $max = str_repeat(chr(0xFF), $bytes);
2104          $msb = $bits & 7;
2105          if ($msb) {
2106              $min = chr(1 << ($msb - 1)) . $min;
2107              $max = chr((1 << $msb) - 1) . $max;
2108          } else {
2109              $min[0] = chr(0x80);
2110          }
2111  
2112          return array(
2113              'min' => new BigInteger($min, 256),
2114              'max' => new BigInteger($max, 256)
2115          );
2116      }
2117  
2118      /**
2119       * DER-decode the length
2120       *
2121       * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4.  See
2122       * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
2123       *
2124       * @access private
2125       * @param string $string
2126       * @return int
2127       */
2128      function _decodeLength(&$string)
2129      {
2130          $length = ord($this->_string_shift($string));
2131          if ($length & 0x80) { // definite length, long form
2132              $length&= 0x7F;
2133              $temp = $this->_string_shift($string, $length);
2134              list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4));
2135          }
2136          return $length;
2137      }
2138  
2139      /**
2140       * DER-encode the length
2141       *
2142       * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4.  See
2143       * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
2144       *
2145       * @access private
2146       * @param int $length
2147       * @return string
2148       */
2149      function _encodeLength($length)
2150      {
2151          if ($length <= 0x7F) {
2152              return chr($length);
2153          }
2154  
2155          $temp = ltrim(pack('N', $length), chr(0));
2156          return pack('Ca*', 0x80 | strlen($temp), $temp);
2157      }
2158  
2159      /**
2160       * String Shift
2161       *
2162       * Inspired by array_shift
2163       *
2164       * @param string $string
2165       * @param int $index
2166       * @return string
2167       * @access private
2168       */
2169      function _string_shift(&$string, $index = 1)
2170      {
2171          $substr = substr($string, 0, $index);
2172          $string = substr($string, $index);
2173          return $substr;
2174      }
2175  
2176      /**
2177       * Determines the private key format
2178       *
2179       * @see self::createKey()
2180       * @access public
2181       * @param int $format
2182       */
2183      function setPrivateKeyFormat($format)
2184      {
2185          $this->privateKeyFormat = $format;
2186      }
2187  
2188      /**
2189       * Determines the public key format
2190       *
2191       * @see self::createKey()
2192       * @access public
2193       * @param int $format
2194       */
2195      function setPublicKeyFormat($format)
2196      {
2197          $this->publicKeyFormat = $format;
2198      }
2199  
2200      /**
2201       * Determines which hashing function should be used
2202       *
2203       * Used with signature production / verification and (if the encryption mode is self::ENCRYPTION_OAEP) encryption and
2204       * decryption.  If $hash isn't supported, sha1 is used.
2205       *
2206       * @access public
2207       * @param string $hash
2208       */
2209      function setHash($hash)
2210      {
2211          // \phpseclib\Crypt\Hash supports algorithms that PKCS#1 doesn't support.  md5-96 and sha1-96, for example.
2212          switch ($hash) {
2213              case 'md2':
2214              case 'md5':
2215              case 'sha1':
2216              case 'sha256':
2217              case 'sha384':
2218              case 'sha512':
2219                  $this->hash = new Hash($hash);
2220                  $this->hashName = $hash;
2221                  break;
2222              default:
2223                  $this->hash = new Hash('sha1');
2224                  $this->hashName = 'sha1';
2225          }
2226          $this->hLen = $this->hash->getLength();
2227      }
2228  
2229      /**
2230       * Determines which hashing function should be used for the mask generation function
2231       *
2232       * The mask generation function is used by self::ENCRYPTION_OAEP and self::SIGNATURE_PSS and although it's
2233       * best if Hash and MGFHash are set to the same thing this is not a requirement.
2234       *
2235       * @access public
2236       * @param string $hash
2237       */
2238      function setMGFHash($hash)
2239      {
2240          // \phpseclib\Crypt\Hash supports algorithms that PKCS#1 doesn't support.  md5-96 and sha1-96, for example.
2241          switch ($hash) {
2242              case 'md2':
2243              case 'md5':
2244              case 'sha1':
2245              case 'sha256':
2246              case 'sha384':
2247              case 'sha512':
2248                  $this->mgfHash = new Hash($hash);
2249                  break;
2250              default:
2251                  $this->mgfHash = new Hash('sha1');
2252          }
2253          $this->mgfHLen = $this->mgfHash->getLength();
2254      }
2255  
2256      /**
2257       * Determines the salt length
2258       *
2259       * To quote from {@link http://tools.ietf.org/html/rfc3447#page-38 RFC3447#page-38}:
2260       *
2261       *    Typical salt lengths in octets are hLen (the length of the output
2262       *    of the hash function Hash) and 0.
2263       *
2264       * @access public
2265       * @param int $sLen
2266       */
2267      function setSaltLength($sLen)
2268      {
2269          $this->sLen = $sLen;
2270      }
2271  
2272      /**
2273       * Integer-to-Octet-String primitive
2274       *
2275       * See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}.
2276       *
2277       * @access private
2278       * @param \phpseclib\Math\BigInteger $x
2279       * @param int $xLen
2280       * @return string
2281       */
2282      function _i2osp($x, $xLen)
2283      {
2284          $x = $x->toBytes();
2285          if (strlen($x) > $xLen) {
2286              user_error('Integer too large');
2287              return false;
2288          }
2289          return str_pad($x, $xLen, chr(0), STR_PAD_LEFT);
2290      }
2291  
2292      /**
2293       * Octet-String-to-Integer primitive
2294       *
2295       * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}.
2296       *
2297       * @access private
2298       * @param int|string|resource $x
2299       * @return \phpseclib\Math\BigInteger
2300       */
2301      function _os2ip($x)
2302      {
2303          return new BigInteger($x, 256);
2304      }
2305  
2306      /**
2307       * Exponentiate with or without Chinese Remainder Theorem
2308       *
2309       * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.2}.
2310       *
2311       * @access private
2312       * @param \phpseclib\Math\BigInteger $x
2313       * @return \phpseclib\Math\BigInteger
2314       */
2315      function _exponentiate($x)
2316      {
2317          switch (true) {
2318              case empty($this->primes):
2319              case $this->primes[1]->equals($this->zero):
2320              case empty($this->coefficients):
2321              case $this->coefficients[2]->equals($this->zero):
2322              case empty($this->exponents):
2323              case $this->exponents[1]->equals($this->zero):
2324                  return $x->modPow($this->exponent, $this->modulus);
2325          }
2326  
2327          $num_primes = count($this->primes);
2328  
2329          if (defined('CRYPT_RSA_DISABLE_BLINDING')) {
2330              $m_i = array(
2331                  1 => $x->modPow($this->exponents[1], $this->primes[1]),
2332                  2 => $x->modPow($this->exponents[2], $this->primes[2])
2333              );
2334              $h = $m_i[1]->subtract($m_i[2]);
2335              $h = $h->multiply($this->coefficients[2]);
2336              list(, $h) = $h->divide($this->primes[1]);
2337              $m = $m_i[2]->add($h->multiply($this->primes[2]));
2338  
2339              $r = $this->primes[1];
2340              for ($i = 3; $i <= $num_primes; $i++) {
2341                  $m_i = $x->modPow($this->exponents[$i], $this->primes[$i]);
2342  
2343                  $r = $r->multiply($this->primes[$i - 1]);
2344  
2345                  $h = $m_i->subtract($m);
2346                  $h = $h->multiply($this->coefficients[$i]);
2347                  list(, $h) = $h->divide($this->primes[$i]);
2348  
2349                  $m = $m->add($r->multiply($h));
2350              }
2351          } else {
2352              $smallest = $this->primes[1];
2353              for ($i = 2; $i <= $num_primes; $i++) {
2354                  if ($smallest->compare($this->primes[$i]) > 0) {
2355                      $smallest = $this->primes[$i];
2356                  }
2357              }
2358  
2359              $one = new BigInteger(1);
2360  
2361              $r = $one->random($one, $smallest->subtract($one));
2362  
2363              $m_i = array(
2364                  1 => $this->_blind($x, $r, 1),
2365                  2 => $this->_blind($x, $r, 2)
2366              );
2367              $h = $m_i[1]->subtract($m_i[2]);
2368              $h = $h->multiply($this->coefficients[2]);
2369              list(, $h) = $h->divide($this->primes[1]);
2370              $m = $m_i[2]->add($h->multiply($this->primes[2]));
2371  
2372              $r = $this->primes[1];
2373              for ($i = 3; $i <= $num_primes; $i++) {
2374                  $m_i = $this->_blind($x, $r, $i);
2375  
2376                  $r = $r->multiply($this->primes[$i - 1]);
2377  
2378                  $h = $m_i->subtract($m);
2379                  $h = $h->multiply($this->coefficients[$i]);
2380                  list(, $h) = $h->divide($this->primes[$i]);
2381  
2382                  $m = $m->add($r->multiply($h));
2383              }
2384          }
2385  
2386          return $m;
2387      }
2388  
2389      /**
2390       * Performs RSA Blinding
2391       *
2392       * Protects against timing attacks by employing RSA Blinding.
2393       * Returns $x->modPow($this->exponents[$i], $this->primes[$i])
2394       *
2395       * @access private
2396       * @param \phpseclib\Math\BigInteger $x
2397       * @param \phpseclib\Math\BigInteger $r
2398       * @param int $i
2399       * @return \phpseclib\Math\BigInteger
2400       */
2401      function _blind($x, $r, $i)
2402      {
2403          $x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i]));
2404          $x = $x->modPow($this->exponents[$i], $this->primes[$i]);
2405  
2406          $r = $r->modInverse($this->primes[$i]);
2407          $x = $x->multiply($r);
2408          list(, $x) = $x->divide($this->primes[$i]);
2409  
2410          return $x;
2411      }
2412  
2413      /**
2414       * Performs blinded RSA equality testing
2415       *
2416       * Protects against a particular type of timing attack described.
2417       *
2418       * See {@link http://codahale.com/a-lesson-in-timing-attacks/ A Lesson In Timing Attacks (or, Don't use MessageDigest.isEquals)}
2419       *
2420       * Thanks for the heads up singpolyma!
2421       *
2422       * @access private
2423       * @param string $x
2424       * @param string $y
2425       * @return bool
2426       */
2427      function _equals($x, $y)
2428      {
2429          if (function_exists('hash_equals')) {
2430              return hash_equals($x, $y);
2431          }
2432  
2433          if (strlen($x) != strlen($y)) {
2434              return false;
2435          }
2436  
2437          $result = "\0";
2438          $x^= $y;
2439          for ($i = 0; $i < strlen($x); $i++) {
2440              $result|= $x[$i];
2441          }
2442  
2443          return $result === "\0";
2444      }
2445  
2446      /**
2447       * RSAEP
2448       *
2449       * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}.
2450       *
2451       * @access private
2452       * @param \phpseclib\Math\BigInteger $m
2453       * @return \phpseclib\Math\BigInteger
2454       */
2455      function _rsaep($m)
2456      {
2457          if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
2458              user_error('Message representative out of range');
2459              return false;
2460          }
2461          return $this->_exponentiate($m);
2462      }
2463  
2464      /**
2465       * RSADP
2466       *
2467       * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}.
2468       *
2469       * @access private
2470       * @param \phpseclib\Math\BigInteger $c
2471       * @return \phpseclib\Math\BigInteger
2472       */
2473      function _rsadp($c)
2474      {
2475          if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) {
2476              user_error('Ciphertext representative out of range');
2477              return false;
2478          }
2479          return $this->_exponentiate($c);
2480      }
2481  
2482      /**
2483       * RSASP1
2484       *
2485       * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}.
2486       *
2487       * @access private
2488       * @param \phpseclib\Math\BigInteger $m
2489       * @return \phpseclib\Math\BigInteger
2490       */
2491      function _rsasp1($m)
2492      {
2493          if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
2494              user_error('Message representative out of range');
2495              return false;
2496          }
2497          return $this->_exponentiate($m);
2498      }
2499  
2500      /**
2501       * RSAVP1
2502       *
2503       * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}.
2504       *
2505       * @access private
2506       * @param \phpseclib\Math\BigInteger $s
2507       * @return \phpseclib\Math\BigInteger
2508       */
2509      function _rsavp1($s)
2510      {
2511          if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) {
2512              user_error('Signature representative out of range');
2513              return false;
2514          }
2515          return $this->_exponentiate($s);
2516      }
2517  
2518      /**
2519       * MGF1
2520       *
2521       * See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}.
2522       *
2523       * @access private
2524       * @param string $mgfSeed
2525       * @param int $maskLen
2526       * @return string
2527       */
2528      function _mgf1($mgfSeed, $maskLen)
2529      {
2530          // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output.
2531  
2532          $t = '';
2533          $count = ceil($maskLen / $this->mgfHLen);
2534          for ($i = 0; $i < $count; $i++) {
2535              $c = pack('N', $i);
2536              $t.= $this->mgfHash->hash($mgfSeed . $c);
2537          }
2538  
2539          return substr($t, 0, $maskLen);
2540      }
2541  
2542      /**
2543       * RSAES-OAEP-ENCRYPT
2544       *
2545       * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1 RFC3447#section-7.1.1} and
2546       * {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}.
2547       *
2548       * @access private
2549       * @param string $m
2550       * @param string $l
2551       * @return string
2552       */
2553      function _rsaes_oaep_encrypt($m, $l = '')
2554      {
2555          $mLen = strlen($m);
2556  
2557          // Length checking
2558  
2559          // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
2560          // be output.
2561  
2562          if ($mLen > $this->k - 2 * $this->hLen - 2) {
2563              user_error('Message too long');
2564              return false;
2565          }
2566  
2567          // EME-OAEP encoding
2568  
2569          $lHash = $this->hash->hash($l);
2570          $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2);
2571          $db = $lHash . $ps . chr(1) . $m;
2572          $seed = Random::string($this->hLen);
2573          $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
2574          $maskedDB = $db ^ $dbMask;
2575          $seedMask = $this->_mgf1($maskedDB, $this->hLen);
2576          $maskedSeed = $seed ^ $seedMask;
2577          $em = chr(0) . $maskedSeed . $maskedDB;
2578  
2579          // RSA encryption
2580  
2581          $m = $this->_os2ip($em);
2582          $c = $this->_rsaep($m);
2583          $c = $this->_i2osp($c, $this->k);
2584  
2585          // Output the ciphertext C
2586  
2587          return $c;
2588      }
2589  
2590      /**
2591       * RSAES-OAEP-DECRYPT
2592       *
2593       * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}.  The fact that the error
2594       * messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2:
2595       *
2596       *    Note.  Care must be taken to ensure that an opponent cannot
2597       *    distinguish the different error conditions in Step 3.g, whether by
2598       *    error message or timing, or, more generally, learn partial
2599       *    information about the encoded message EM.  Otherwise an opponent may
2600       *    be able to obtain useful information about the decryption of the
2601       *    ciphertext C, leading to a chosen-ciphertext attack such as the one
2602       *    observed by Manger [36].
2603       *
2604       * As for $l...  to quote from {@link http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}:
2605       *
2606       *    Both the encryption and the decryption operations of RSAES-OAEP take
2607       *    the value of a label L as input.  In this version of PKCS #1, L is
2608       *    the empty string; other uses of the label are outside the scope of
2609       *    this document.
2610       *
2611       * @access private
2612       * @param string $c
2613       * @param string $l
2614       * @return string
2615       */
2616      function _rsaes_oaep_decrypt($c, $l = '')
2617      {
2618          // Length checking
2619  
2620          // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
2621          // be output.
2622  
2623          if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) {
2624              user_error('Decryption error');
2625              return false;
2626          }
2627  
2628          // RSA decryption
2629  
2630          $c = $this->_os2ip($c);
2631          $m = $this->_rsadp($c);
2632          if ($m === false) {
2633              user_error('Decryption error');
2634              return false;
2635          }
2636          $em = $this->_i2osp($m, $this->k);
2637  
2638          // EME-OAEP decoding
2639  
2640          $lHash = $this->hash->hash($l);
2641          $y = ord($em[0]);
2642          $maskedSeed = substr($em, 1, $this->hLen);
2643          $maskedDB = substr($em, $this->hLen + 1);
2644          $seedMask = $this->_mgf1($maskedDB, $this->hLen);
2645          $seed = $maskedSeed ^ $seedMask;
2646          $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
2647          $db = $maskedDB ^ $dbMask;
2648          $lHash2 = substr($db, 0, $this->hLen);
2649          $m = substr($db, $this->hLen);
2650          $hashesMatch = $this->_equals($lHash, $lHash2);
2651          $leadingZeros = 1;
2652          $patternMatch = 0;
2653          $offset = 0;
2654          for ($i = 0; $i < strlen($m); $i++) {
2655              $patternMatch|= $leadingZeros & ($m[$i] === "\1");
2656              $leadingZeros&= $m[$i] === "\0";
2657              $offset+= $patternMatch ? 0 : 1;
2658          }
2659  
2660          // we do | instead of || to avoid https://en.wikipedia.org/wiki/Short-circuit_evaluation
2661          // to protect against timing attacks
2662          if (!$hashesMatch | !$patternMatch) {
2663              user_error('Decryption error');
2664              return false;
2665          }
2666  
2667          // Output the message M
2668  
2669          return substr($m, $offset + 1);
2670      }
2671  
2672      /**
2673       * Raw Encryption / Decryption
2674       *
2675       * Doesn't use padding and is not recommended.
2676       *
2677       * @access private
2678       * @param string $m
2679       * @return string
2680       */
2681      function _raw_encrypt($m)
2682      {
2683          $temp = $this->_os2ip($m);
2684          $temp = $this->_rsaep($temp);
2685          return  $this->_i2osp($temp, $this->k);
2686      }
2687  
2688      /**
2689       * RSAES-PKCS1-V1_5-ENCRYPT
2690       *
2691       * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}.
2692       *
2693       * @access private
2694       * @param string $m
2695       * @return string
2696       */
2697      function _rsaes_pkcs1_v1_5_encrypt($m)
2698      {
2699          $mLen = strlen($m);
2700  
2701          // Length checking
2702  
2703          if ($mLen > $this->k - 11) {
2704              user_error('Message too long');
2705              return false;
2706          }
2707  
2708          // EME-PKCS1-v1_5 encoding
2709  
2710          $psLen = $this->k - $mLen - 3;
2711          $ps = '';
2712          while (strlen($ps) != $psLen) {
2713              $temp = Random::string($psLen - strlen($ps));
2714              $temp = str_replace("\x00", '', $temp);
2715              $ps.= $temp;
2716          }
2717          $type = 2;
2718          // see the comments of _rsaes_pkcs1_v1_5_decrypt() to understand why this is being done
2719          if (defined('CRYPT_RSA_PKCS15_COMPAT') && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) {
2720              $type = 1;
2721              // "The padding string PS shall consist of k-3-||D|| octets. ... for block type 01, they shall have value FF"
2722              $ps = str_repeat("\xFF", $psLen);
2723          }
2724          $em = chr(0) . chr($type) . $ps . chr(0) . $m;
2725  
2726          // RSA encryption
2727          $m = $this->_os2ip($em);
2728          $c = $this->_rsaep($m);
2729          $c = $this->_i2osp($c, $this->k);
2730  
2731          // Output the ciphertext C
2732  
2733          return $c;
2734      }
2735  
2736      /**
2737       * RSAES-PKCS1-V1_5-DECRYPT
2738       *
2739       * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}.
2740       *
2741       * For compatibility purposes, this function departs slightly from the description given in RFC3447.
2742       * The reason being that RFC2313#section-8.1 (PKCS#1 v1.5) states that ciphertext's encrypted by the
2743       * private key should have the second byte set to either 0 or 1 and that ciphertext's encrypted by the
2744       * public key should have the second byte set to 2.  In RFC3447 (PKCS#1 v2.1), the second byte is supposed
2745       * to be 2 regardless of which key is used.  For compatibility purposes, we'll just check to make sure the
2746       * second byte is 2 or less.  If it is, we'll accept the decrypted string as valid.
2747       *
2748       * As a consequence of this, a private key encrypted ciphertext produced with \phpseclib\Crypt\RSA may not decrypt
2749       * with a strictly PKCS#1 v1.5 compliant RSA implementation.  Public key encrypted ciphertext's should but
2750       * not private key encrypted ciphertext's.
2751       *
2752       * @access private
2753       * @param string $c
2754       * @return string
2755       */
2756      function _rsaes_pkcs1_v1_5_decrypt($c)
2757      {
2758          // Length checking
2759  
2760          if (strlen($c) != $this->k) { // or if k < 11
2761              user_error('Decryption error');
2762              return false;
2763          }
2764  
2765          // RSA decryption
2766  
2767          $c = $this->_os2ip($c);
2768          $m = $this->_rsadp($c);
2769  
2770          if ($m === false) {
2771              user_error('Decryption error');
2772              return false;
2773          }
2774          $em = $this->_i2osp($m, $this->k);
2775  
2776          // EME-PKCS1-v1_5 decoding
2777  
2778          if (ord($em[0]) != 0 || ord($em[1]) > 2) {
2779              user_error('Decryption error');
2780              return false;
2781          }
2782  
2783          $ps = substr($em, 2, strpos($em, chr(0), 2) - 2);
2784          $m = substr($em, strlen($ps) + 3);
2785  
2786          if (strlen($ps) < 8) {
2787              user_error('Decryption error');
2788              return false;
2789          }
2790  
2791          // Output M
2792  
2793          return $m;
2794      }
2795  
2796      /**
2797       * EMSA-PSS-ENCODE
2798       *
2799       * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}.
2800       *
2801       * @access private
2802       * @param string $m
2803       * @param int $emBits
2804       */
2805      function _emsa_pss_encode($m, $emBits)
2806      {
2807          // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
2808          // be output.
2809  
2810          $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8)
2811          $sLen = $this->sLen !== null ? $this->sLen : $this->hLen;
2812  
2813          $mHash = $this->hash->hash($m);
2814          if ($emLen < $this->hLen + $sLen + 2) {
2815              user_error('Encoding error');
2816              return false;
2817          }
2818  
2819          $salt = Random::string($sLen);
2820          $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
2821          $h = $this->hash->hash($m2);
2822          $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2);
2823          $db = $ps . chr(1) . $salt;
2824          $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
2825          $maskedDB = $db ^ $dbMask;
2826          $maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0];
2827          $em = $maskedDB . $h . chr(0xBC);
2828  
2829          return $em;
2830      }
2831  
2832      /**
2833       * EMSA-PSS-VERIFY
2834       *
2835       * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}.
2836       *
2837       * @access private
2838       * @param string $m
2839       * @param string $em
2840       * @param int $emBits
2841       * @return string
2842       */
2843      function _emsa_pss_verify($m, $em, $emBits)
2844      {
2845          // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
2846          // be output.
2847  
2848          $emLen = ($emBits + 7) >> 3; // ie. ceil($emBits / 8);
2849          $sLen = $this->sLen !== null ? $this->sLen : $this->hLen;
2850  
2851          $mHash = $this->hash->hash($m);
2852          if ($emLen < $this->hLen + $sLen + 2) {
2853              return false;
2854          }
2855  
2856          if ($em[strlen($em) - 1] != chr(0xBC)) {
2857              return false;
2858          }
2859  
2860          $maskedDB = substr($em, 0, -$this->hLen - 1);
2861          $h = substr($em, -$this->hLen - 1, $this->hLen);
2862          $temp = chr(0xFF << ($emBits & 7));
2863          if ((~$maskedDB[0] & $temp) != $temp) {
2864              return false;
2865          }
2866          $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
2867          $db = $maskedDB ^ $dbMask;
2868          $db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
2869          $temp = $emLen - $this->hLen - $sLen - 2;
2870          if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) {
2871              return false;
2872          }
2873          $salt = substr($db, $temp + 1); // should be $sLen long
2874          $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
2875          $h2 = $this->hash->hash($m2);
2876          return $this->_equals($h, $h2);
2877      }
2878  
2879      /**
2880       * RSASSA-PSS-SIGN
2881       *
2882       * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}.
2883       *
2884       * @access private
2885       * @param string $m
2886       * @return string
2887       */
2888      function _rsassa_pss_sign($m)
2889      {
2890          // EMSA-PSS encoding
2891  
2892          $em = $this->_emsa_pss_encode($m, 8 * $this->k - 1);
2893  
2894          // RSA signature
2895  
2896          $m = $this->_os2ip($em);
2897          $s = $this->_rsasp1($m);
2898          $s = $this->_i2osp($s, $this->k);
2899  
2900          // Output the signature S
2901  
2902          return $s;
2903      }
2904  
2905      /**
2906       * RSASSA-PSS-VERIFY
2907       *
2908       * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}.
2909       *
2910       * @access private
2911       * @param string $m
2912       * @param string $s
2913       * @return string
2914       */
2915      function _rsassa_pss_verify($m, $s)
2916      {
2917          // Length checking
2918  
2919          if (strlen($s) != $this->k) {
2920              user_error('Invalid signature');
2921              return false;
2922          }
2923  
2924          // RSA verification
2925  
2926          $modBits = strlen($this->modulus->toBits());
2927  
2928          $s2 = $this->_os2ip($s);
2929          $m2 = $this->_rsavp1($s2);
2930          if ($m2 === false) {
2931              user_error('Invalid signature');
2932              return false;
2933          }
2934          $em = $this->_i2osp($m2, $this->k);
2935          if ($em === false) {
2936              user_error('Invalid signature');
2937              return false;
2938          }
2939  
2940          // EMSA-PSS verification
2941  
2942          return $this->_emsa_pss_verify($m, $em, $modBits - 1);
2943      }
2944  
2945      /**
2946       * EMSA-PKCS1-V1_5-ENCODE
2947       *
2948       * See {@link http://tools.ietf.org/html/rfc3447#section-9.2 RFC3447#section-9.2}.
2949       *
2950       * @access private
2951       * @param string $m
2952       * @param int $emLen
2953       * @return string
2954       */
2955      function _emsa_pkcs1_v1_5_encode($m, $emLen)
2956      {
2957          $h = $this->hash->hash($m);
2958          if ($h === false) {
2959              return false;
2960          }
2961  
2962          // see http://tools.ietf.org/html/rfc3447#page-43
2963          switch ($this->hashName) {
2964              case 'md2':
2965                  $t = pack('H*', '3020300c06082a864886f70d020205000410');
2966                  break;
2967              case 'md5':
2968                  $t = pack('H*', '3020300c06082a864886f70d020505000410');
2969                  break;
2970              case 'sha1':
2971                  $t = pack('H*', '3021300906052b0e03021a05000414');
2972                  break;
2973              case 'sha256':
2974                  $t = pack('H*', '3031300d060960864801650304020105000420');
2975                  break;
2976              case 'sha384':
2977                  $t = pack('H*', '3041300d060960864801650304020205000430');
2978                  break;
2979              case 'sha512':
2980                  $t = pack('H*', '3051300d060960864801650304020305000440');
2981          }
2982          $t.= $h;
2983          $tLen = strlen($t);
2984  
2985          if ($emLen < $tLen + 11) {
2986              user_error('Intended encoded message length too short');
2987              return false;
2988          }
2989  
2990          $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
2991  
2992          $em = "\0\1$ps\0$t";
2993  
2994          return $em;
2995      }
2996  
2997      /**
2998       * EMSA-PKCS1-V1_5-ENCODE (without NULL)
2999       *
3000       * Quoting https://tools.ietf.org/html/rfc8017#page-65,
3001       *
3002       * "The parameters field associated with id-sha1, id-sha224, id-sha256,
3003       *  id-sha384, id-sha512, id-sha512/224, and id-sha512/256 should
3004       *  generally be omitted, but if present, it shall have a value of type
3005       *  NULL"
3006       *
3007       * @access private
3008       * @param string $m
3009       * @param int $emLen
3010       * @return string
3011       */
3012      function _emsa_pkcs1_v1_5_encode_without_null($m, $emLen)
3013      {
3014          $h = $this->hash->hash($m);
3015          if ($h === false) {
3016              return false;
3017          }
3018  
3019          switch ($this->hashName) {
3020              case 'sha1':
3021                  $t = pack('H*', '301f300706052b0e03021a0414');
3022                  break;
3023              case 'sha256':
3024                  $t = pack('H*', '302f300b06096086480165030402010420');
3025                  break;
3026              case 'sha384':
3027                  $t = pack('H*', '303f300b06096086480165030402020430');
3028                  break;
3029              case 'sha512':
3030                  $t = pack('H*', '304f300b06096086480165030402030440');
3031                  break;
3032              default:
3033                  return false;
3034          }
3035          $t.= $h;
3036          $tLen = strlen($t);
3037  
3038          if ($emLen < $tLen + 11) {
3039              user_error('Intended encoded message length too short');
3040              return false;
3041          }
3042  
3043          $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
3044  
3045          $em = "\0\1$ps\0$t";
3046  
3047          return $em;
3048      }
3049  
3050      /**
3051       * RSASSA-PKCS1-V1_5-SIGN
3052       *
3053       * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}.
3054       *
3055       * @access private
3056       * @param string $m
3057       * @return string
3058       */
3059      function _rsassa_pkcs1_v1_5_sign($m)
3060      {
3061          // EMSA-PKCS1-v1_5 encoding
3062  
3063          $em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
3064          if ($em === false) {
3065              user_error('RSA modulus too short');
3066              return false;
3067          }
3068  
3069          // RSA signature
3070  
3071          $m = $this->_os2ip($em);
3072          $s = $this->_rsasp1($m);
3073          $s = $this->_i2osp($s, $this->k);
3074  
3075          // Output the signature S
3076  
3077          return $s;
3078      }
3079  
3080      /**
3081       * RSASSA-PKCS1-V1_5-VERIFY
3082       *
3083       * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}.
3084       *
3085       * @access private
3086       * @param string $m
3087       * @param string $s
3088       * @return string
3089       */
3090      function _rsassa_pkcs1_v1_5_verify($m, $s)
3091      {
3092          // Length checking
3093  
3094          if (strlen($s) != $this->k) {
3095              user_error('Invalid signature');
3096              return false;
3097          }
3098  
3099          // RSA verification
3100  
3101          $s = $this->_os2ip($s);
3102          $m2 = $this->_rsavp1($s);
3103          if ($m2 === false) {
3104              user_error('Invalid signature');
3105              return false;
3106          }
3107          $em = $this->_i2osp($m2, $this->k);
3108          if ($em === false) {
3109              user_error('Invalid signature');
3110              return false;
3111          }
3112  
3113          // EMSA-PKCS1-v1_5 encoding
3114  
3115          $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
3116          $em3 = $this->_emsa_pkcs1_v1_5_encode_without_null($m, $this->k);
3117  
3118          if ($em2 === false && $em3 === false) {
3119              user_error('RSA modulus too short');
3120              return false;
3121          }
3122  
3123          // Compare
3124  
3125          return ($em2 !== false && $this->_equals($em, $em2)) ||
3126                 ($em3 !== false && $this->_equals($em, $em3));
3127      }
3128  
3129      /**
3130       * Set Encryption Mode
3131       *
3132       * Valid values include self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1.
3133       *
3134       * @access public
3135       * @param int $mode
3136       */
3137      function setEncryptionMode($mode)
3138      {
3139          $this->encryptionMode = $mode;
3140      }
3141  
3142      /**
3143       * Set Signature Mode
3144       *
3145       * Valid values include self::SIGNATURE_PSS and self::SIGNATURE_PKCS1
3146       *
3147       * @access public
3148       * @param int $mode
3149       */
3150      function setSignatureMode($mode)
3151      {
3152          $this->signatureMode = $mode;
3153      }
3154  
3155      /**
3156       * Set public key comment.
3157       *
3158       * @access public
3159       * @param string $comment
3160       */
3161      function setComment($comment)
3162      {
3163          $this->comment = $comment;
3164      }
3165  
3166      /**
3167       * Get public key comment.
3168       *
3169       * @access public
3170       * @return string
3171       */
3172      function getComment()
3173      {
3174          return $this->comment;
3175      }
3176  
3177      /**
3178       * Encryption
3179       *
3180       * Both self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1 both place limits on how long $plaintext can be.
3181       * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will
3182       * be concatenated together.
3183       *
3184       * @see self::decrypt()
3185       * @access public
3186       * @param string $plaintext
3187       * @return string
3188       */
3189      function encrypt($plaintext)
3190      {
3191          switch ($this->encryptionMode) {
3192              case self::ENCRYPTION_NONE:
3193                  $plaintext = str_split($plaintext, $this->k);
3194                  $ciphertext = '';
3195                  foreach ($plaintext as $m) {
3196                      $ciphertext.= $this->_raw_encrypt($m);
3197                  }
3198                  return $ciphertext;
3199              case self::ENCRYPTION_PKCS1:
3200                  $length = $this->k - 11;
3201                  if ($length <= 0) {
3202                      return false;
3203                  }
3204  
3205                  $plaintext = str_split($plaintext, $length);
3206                  $ciphertext = '';
3207                  foreach ($plaintext as $m) {
3208                      $ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m);
3209                  }
3210                  return $ciphertext;
3211              //case self::ENCRYPTION_OAEP:
3212              default:
3213                  $length = $this->k - 2 * $this->hLen - 2;
3214                  if ($length <= 0) {
3215                      return false;
3216                  }
3217  
3218                  $plaintext = str_split($plaintext, $length);
3219                  $ciphertext = '';
3220                  foreach ($plaintext as $m) {
3221                      $ciphertext.= $this->_rsaes_oaep_encrypt($m);
3222                  }
3223                  return $ciphertext;
3224          }
3225      }
3226  
3227      /**
3228       * Decryption
3229       *
3230       * @see self::encrypt()
3231       * @access public
3232       * @param string $ciphertext
3233       * @return string
3234       */
3235      function decrypt($ciphertext)
3236      {
3237          if ($this->k <= 0) {
3238              return false;
3239          }
3240  
3241          $ciphertext = str_split($ciphertext, $this->k);
3242          $ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], $this->k, chr(0), STR_PAD_LEFT);
3243  
3244          $plaintext = '';
3245  
3246          switch ($this->encryptionMode) {
3247              case self::ENCRYPTION_NONE:
3248                  $decrypt = '_raw_encrypt';
3249                  break;
3250              case self::ENCRYPTION_PKCS1:
3251                  $decrypt = '_rsaes_pkcs1_v1_5_decrypt';
3252                  break;
3253              //case self::ENCRYPTION_OAEP:
3254              default:
3255                  $decrypt = '_rsaes_oaep_decrypt';
3256          }
3257  
3258          foreach ($ciphertext as $c) {
3259              $temp = $this->$decrypt($c);
3260              if ($temp === false) {
3261                  return false;
3262              }
3263              $plaintext.= $temp;
3264          }
3265  
3266          return $plaintext;
3267      }
3268  
3269      /**
3270       * Create a signature
3271       *
3272       * @see self::verify()
3273       * @access public
3274       * @param string $message
3275       * @return string
3276       */
3277      function sign($message)
3278      {
3279          if (empty($this->modulus) || empty($this->exponent)) {
3280              return false;
3281          }
3282  
3283          switch ($this->signatureMode) {
3284              case self::SIGNATURE_PKCS1:
3285                  return $this->_rsassa_pkcs1_v1_5_sign($message);
3286              //case self::SIGNATURE_PSS:
3287              default:
3288                  return $this->_rsassa_pss_sign($message);
3289          }
3290      }
3291  
3292      /**
3293       * Verifies a signature
3294       *
3295       * @see self::sign()
3296       * @access public
3297       * @param string $message
3298       * @param string $signature
3299       * @return bool
3300       */
3301      function verify($message, $signature)
3302      {
3303          if (empty($this->modulus) || empty($this->exponent)) {
3304              return false;
3305          }
3306  
3307          switch ($this->signatureMode) {
3308              case self::SIGNATURE_PKCS1:
3309                  return $this->_rsassa_pkcs1_v1_5_verify($message, $signature);
3310              //case self::SIGNATURE_PSS:
3311              default:
3312                  return $this->_rsassa_pss_verify($message, $signature);
3313          }
3314      }
3315  
3316      /**
3317       * Extract raw BER from Base64 encoding
3318       *
3319       * @access private
3320       * @param string $str
3321       * @return string
3322       */
3323      function _extractBER($str)
3324      {
3325          /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them
3326           * above and beyond the ceritificate.
3327           * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line:
3328           *
3329           * Bag Attributes
3330           *     localKeyID: 01 00 00 00
3331           * subject=/O=organization/OU=org unit/CN=common name
3332           * issuer=/O=organization/CN=common name
3333           */
3334          $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1);
3335          // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff
3336          $temp = preg_replace('#-+[^-]+-+#', '', $temp);
3337          // remove new lines
3338          $temp = str_replace(array("\r", "\n", ' '), '', $temp);
3339          $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
3340          return $temp != false ? $temp : $str;
3341      }
3342  }