PageRenderTime 43ms CodeModel.GetById 3ms app.highlight 28ms RepoModel.GetById 1ms app.codeStats 1ms

/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php

https://bitbucket.org/davide_grobberio/laravel-angular
PHP | 2988 lines | 1580 code | 294 blank | 1114 comment | 279 complexity | 92986eff3f5014b0ad4f920a7ed1ce11 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1<?php
   2
   3/**
   4 * Pure-PHP PKCS#1 (v2.1) compliant implementation of RSA.
   5 *
   6 * PHP versions 4 and 5
   7 *
   8 * Here's an example of how to encrypt and decrypt text with this library:
   9 * <code>
  10 * <?php
  11 *    include 'Crypt/RSA.php';
  12 *
  13 *    $rsa = new 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 'Crypt/RSA.php';
  30 *
  31 *    $rsa = new 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 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
  45 * of this software and associated documentation files (the "Software"), to deal
  46 * in the Software without restriction, including without limitation the rights
  47 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  48 * copies of the Software, and to permit persons to whom the Software is
  49 * furnished to do so, subject to the following conditions:
  50 *
  51 * The above copyright notice and this permission notice shall be included in
  52 * all copies or substantial portions of the Software.
  53 *
  54 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  55 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  56 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  57 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  58 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  59 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  60 * THE SOFTWARE.
  61 *
  62 * @category  Crypt
  63 * @package   Crypt_RSA
  64 * @author    Jim Wigginton <terrafrost@php.net>
  65 * @copyright MMIX Jim Wigginton
  66 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
  67 * @link      http://phpseclib.sourceforge.net
  68 */
  69
  70/**
  71 * Include Crypt_Random
  72 */
  73// the class_exists() will only be called if the crypt_random_string function hasn't been defined and
  74// will trigger a call to __autoload() if you're wanting to auto-load classes
  75// call function_exists() a second time to stop the include_once from being called outside
  76// of the auto loader
  77if (!function_exists('crypt_random_string')) {
  78    include_once 'Random.php';
  79}
  80
  81/**
  82 * Include Crypt_Hash
  83 */
  84if (!class_exists('Crypt_Hash')) {
  85    include_once 'Hash.php';
  86}
  87
  88/**#@+
  89 * @access public
  90 * @see Crypt_RSA::encrypt()
  91 * @see Crypt_RSA::decrypt()
  92 */
  93/**
  94 * Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding}
  95 * (OAEP) for encryption / decryption.
  96 *
  97 * Uses sha1 by default.
  98 *
  99 * @see Crypt_RSA::setHash()
 100 * @see Crypt_RSA::setMGFHash()
 101 */
 102define('CRYPT_RSA_ENCRYPTION_OAEP',  1);
 103/**
 104 * Use PKCS#1 padding.
 105 *
 106 * Although CRYPT_RSA_ENCRYPTION_OAEP offers more security, including PKCS#1 padding is necessary for purposes of backwards
 107 * compatibility with protocols (like SSH-1) written before OAEP's introduction.
 108 */
 109define('CRYPT_RSA_ENCRYPTION_PKCS1', 2);
 110/**#@-*/
 111
 112/**#@+
 113 * @access public
 114 * @see Crypt_RSA::sign()
 115 * @see Crypt_RSA::verify()
 116 * @see Crypt_RSA::setHash()
 117 */
 118/**
 119 * Use the Probabilistic Signature Scheme for signing
 120 *
 121 * Uses sha1 by default.
 122 *
 123 * @see Crypt_RSA::setSaltLength()
 124 * @see Crypt_RSA::setMGFHash()
 125 */
 126define('CRYPT_RSA_SIGNATURE_PSS',  1);
 127/**
 128 * Use the PKCS#1 scheme by default.
 129 *
 130 * Although CRYPT_RSA_SIGNATURE_PSS offers more security, including PKCS#1 signing is necessary for purposes of backwards
 131 * compatibility with protocols (like SSH-2) written before PSS's introduction.
 132 */
 133define('CRYPT_RSA_SIGNATURE_PKCS1', 2);
 134/**#@-*/
 135
 136/**#@+
 137 * @access private
 138 * @see Crypt_RSA::createKey()
 139 */
 140/**
 141 * ASN1 Integer
 142 */
 143define('CRYPT_RSA_ASN1_INTEGER',     2);
 144/**
 145 * ASN1 Bit String
 146 */
 147define('CRYPT_RSA_ASN1_BITSTRING',   3);
 148/**
 149 * ASN1 Octet String
 150 */
 151define('CRYPT_RSA_ASN1_OCTETSTRING', 4);
 152/**
 153 * ASN1 Object Identifier
 154 */
 155define('CRYPT_RSA_ASN1_OBJECT',      6);
 156/**
 157 * ASN1 Sequence (with the constucted bit set)
 158 */
 159define('CRYPT_RSA_ASN1_SEQUENCE',   48);
 160/**#@-*/
 161
 162/**#@+
 163 * @access private
 164 * @see Crypt_RSA::Crypt_RSA()
 165 */
 166/**
 167 * To use the pure-PHP implementation
 168 */
 169define('CRYPT_RSA_MODE_INTERNAL', 1);
 170/**
 171 * To use the OpenSSL library
 172 *
 173 * (if enabled; otherwise, the internal implementation will be used)
 174 */
 175define('CRYPT_RSA_MODE_OPENSSL', 2);
 176/**#@-*/
 177
 178/**
 179 * Default openSSL configuration file.
 180 */
 181define('CRYPT_RSA_OPENSSL_CONFIG', dirname(__FILE__) . '/../openssl.cnf');
 182
 183/**#@+
 184 * @access public
 185 * @see Crypt_RSA::createKey()
 186 * @see Crypt_RSA::setPrivateKeyFormat()
 187 */
 188/**
 189 * PKCS#1 formatted private key
 190 *
 191 * Used by OpenSSH
 192 */
 193define('CRYPT_RSA_PRIVATE_FORMAT_PKCS1', 0);
 194/**
 195 * PuTTY formatted private key
 196 */
 197define('CRYPT_RSA_PRIVATE_FORMAT_PUTTY', 1);
 198/**
 199 * XML formatted private key
 200 */
 201define('CRYPT_RSA_PRIVATE_FORMAT_XML', 2);
 202/**
 203 * PKCS#8 formatted private key
 204 */
 205define('CRYPT_RSA_PRIVATE_FORMAT_PKCS8', 3);
 206/**#@-*/
 207
 208/**#@+
 209 * @access public
 210 * @see Crypt_RSA::createKey()
 211 * @see Crypt_RSA::setPublicKeyFormat()
 212 */
 213/**
 214 * Raw public key
 215 *
 216 * An array containing two Math_BigInteger objects.
 217 *
 218 * The exponent can be indexed with any of the following:
 219 *
 220 * 0, e, exponent, publicExponent
 221 *
 222 * The modulus can be indexed with any of the following:
 223 *
 224 * 1, n, modulo, modulus
 225 */
 226define('CRYPT_RSA_PUBLIC_FORMAT_RAW', 3);
 227/**
 228 * PKCS#1 formatted public key (raw)
 229 *
 230 * Used by File/X509.php
 231 *
 232 * Has the following header:
 233 *
 234 * -----BEGIN RSA PUBLIC KEY-----
 235 *
 236 * Analogous to ssh-keygen's pem format (as specified by -m)
 237 */
 238define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1', 4);
 239define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW', 4);
 240/**
 241 * XML formatted public key
 242 */
 243define('CRYPT_RSA_PUBLIC_FORMAT_XML', 5);
 244/**
 245 * OpenSSH formatted public key
 246 *
 247 * Place in $HOME/.ssh/authorized_keys
 248 */
 249define('CRYPT_RSA_PUBLIC_FORMAT_OPENSSH', 6);
 250/**
 251 * PKCS#1 formatted public key (encapsulated)
 252 *
 253 * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set)
 254 *
 255 * Has the following header:
 256 *
 257 * -----BEGIN PUBLIC KEY-----
 258 *
 259 * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
 260 * is specific to private keys it's basically creating a DER-encoded wrapper
 261 * for keys. This just extends that same concept to public keys (much like ssh-keygen)
 262 */
 263define('CRYPT_RSA_PUBLIC_FORMAT_PKCS8', 7);
 264/**#@-*/
 265
 266/**
 267 * Pure-PHP PKCS#1 compliant implementation of RSA.
 268 *
 269 * @package Crypt_RSA
 270 * @author  Jim Wigginton <terrafrost@php.net>
 271 * @access  public
 272 */
 273class Crypt_RSA
 274{
 275    /**
 276     * Precomputed Zero
 277     *
 278     * @var Array
 279     * @access private
 280     */
 281    var $zero;
 282
 283    /**
 284     * Precomputed One
 285     *
 286     * @var Array
 287     * @access private
 288     */
 289    var $one;
 290
 291    /**
 292     * Private Key Format
 293     *
 294     * @var Integer
 295     * @access private
 296     */
 297    var $privateKeyFormat = CRYPT_RSA_PRIVATE_FORMAT_PKCS1;
 298
 299    /**
 300     * Public Key Format
 301     *
 302     * @var Integer
 303     * @access public
 304     */
 305    var $publicKeyFormat = CRYPT_RSA_PUBLIC_FORMAT_PKCS8;
 306
 307    /**
 308     * Modulus (ie. n)
 309     *
 310     * @var Math_BigInteger
 311     * @access private
 312     */
 313    var $modulus;
 314
 315    /**
 316     * Modulus length
 317     *
 318     * @var Math_BigInteger
 319     * @access private
 320     */
 321    var $k;
 322
 323    /**
 324     * Exponent (ie. e or d)
 325     *
 326     * @var Math_BigInteger
 327     * @access private
 328     */
 329    var $exponent;
 330
 331    /**
 332     * Primes for Chinese Remainder Theorem (ie. p and q)
 333     *
 334     * @var Array
 335     * @access private
 336     */
 337    var $primes;
 338
 339    /**
 340     * Exponents for Chinese Remainder Theorem (ie. dP and dQ)
 341     *
 342     * @var Array
 343     * @access private
 344     */
 345    var $exponents;
 346
 347    /**
 348     * Coefficients for Chinese Remainder Theorem (ie. qInv)
 349     *
 350     * @var Array
 351     * @access private
 352     */
 353    var $coefficients;
 354
 355    /**
 356     * Hash name
 357     *
 358     * @var String
 359     * @access private
 360     */
 361    var $hashName;
 362
 363    /**
 364     * Hash function
 365     *
 366     * @var Crypt_Hash
 367     * @access private
 368     */
 369    var $hash;
 370
 371    /**
 372     * Length of hash function output
 373     *
 374     * @var Integer
 375     * @access private
 376     */
 377    var $hLen;
 378
 379    /**
 380     * Length of salt
 381     *
 382     * @var Integer
 383     * @access private
 384     */
 385    var $sLen;
 386
 387    /**
 388     * Hash function for the Mask Generation Function
 389     *
 390     * @var Crypt_Hash
 391     * @access private
 392     */
 393    var $mgfHash;
 394
 395    /**
 396     * Length of MGF hash function output
 397     *
 398     * @var Integer
 399     * @access private
 400     */
 401    var $mgfHLen;
 402
 403    /**
 404     * Encryption mode
 405     *
 406     * @var Integer
 407     * @access private
 408     */
 409    var $encryptionMode = CRYPT_RSA_ENCRYPTION_OAEP;
 410
 411    /**
 412     * Signature mode
 413     *
 414     * @var Integer
 415     * @access private
 416     */
 417    var $signatureMode = CRYPT_RSA_SIGNATURE_PSS;
 418
 419    /**
 420     * Public Exponent
 421     *
 422     * @var Mixed
 423     * @access private
 424     */
 425    var $publicExponent = false;
 426
 427    /**
 428     * Password
 429     *
 430     * @var String
 431     * @access private
 432     */
 433    var $password = false;
 434
 435    /**
 436     * Components
 437     *
 438     * For use with parsing XML formatted keys.  PHP's XML Parser functions use utilized - instead of PHP's DOM functions -
 439     * because PHP's XML Parser functions work on PHP4 whereas PHP's DOM functions - although surperior - don't.
 440     *
 441     * @see Crypt_RSA::_start_element_handler()
 442     * @var Array
 443     * @access private
 444     */
 445    var $components = array();
 446
 447    /**
 448     * Current String
 449     *
 450     * For use with parsing XML formatted keys.
 451     *
 452     * @see Crypt_RSA::_character_handler()
 453     * @see Crypt_RSA::_stop_element_handler()
 454     * @var Mixed
 455     * @access private
 456     */
 457    var $current;
 458
 459    /**
 460     * OpenSSL configuration file name.
 461     *
 462     * Set to null to use system configuration file.
 463     * @see Crypt_RSA::createKey()
 464     * @var Mixed
 465     * @Access public
 466     */
 467    var $configFile;
 468
 469    /**
 470     * Public key comment field.
 471     *
 472     * @var String
 473     * @access private
 474     */
 475    var $comment = 'phpseclib-generated-key';
 476
 477    /**
 478     * The constructor
 479     *
 480     * If you want to make use of the openssl extension, you'll need to set the mode manually, yourself.  The reason
 481     * Crypt_RSA doesn't do it is because OpenSSL doesn't fail gracefully.  openssl_pkey_new(), in particular, requires
 482     * openssl.cnf be present somewhere and, unfortunately, the only real way to find out is too late.
 483     *
 484     * @return Crypt_RSA
 485     * @access public
 486     */
 487    function Crypt_RSA()
 488    {
 489        if (!class_exists('Math_BigInteger')) {
 490            include_once 'Math/BigInteger.php';
 491        }
 492
 493        $this->configFile = CRYPT_RSA_OPENSSL_CONFIG;
 494
 495        if ( !defined('CRYPT_RSA_MODE') ) {
 496            // Math/BigInteger's openssl requirements are a little less stringent than Crypt/RSA's. in particular,
 497            // Math/BigInteger doesn't require an openssl.cfg file whereas Crypt/RSA does. so if Math/BigInteger
 498            // can't use OpenSSL it can be pretty trivially assumed, then, that Crypt/RSA can't either.
 499            if ( defined('MATH_BIGINTEGER_OPENSSL_DISABLE') ) {
 500                define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
 501            }
 502
 503            switch ( !defined('CRYPT_RSA_MODE') ) { // ie. only run this if the above didn't set CRYPT_RSA_MODE already
 504                // openssl_pkey_get_details - which is used in the only place Crypt/RSA.php uses OpenSSL - was introduced in PHP 5.2.0
 505                case !function_exists('openssl_pkey_get_details'):
 506                    define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
 507                    break;
 508                case extension_loaded('openssl') && version_compare(PHP_VERSION, '4.2.0', '>=') && file_exists($this->configFile):
 509                    // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
 510                    ob_start();
 511                    @phpinfo();
 512                    $content = ob_get_contents();
 513                    ob_end_clean();
 514
 515                    preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches);
 516
 517                    $versions = array();
 518                    if (!empty($matches[1])) {
 519                       for ($i = 0; $i < count($matches[1]); $i++) {
 520                          $versions[$matches[1][$i]] = trim(str_replace('=>', '', strip_tags($matches[2][$i])));
 521                       }
 522                    }
 523
 524                    // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+
 525                    switch (true) {
 526                        case !isset($versions['Header']):
 527                        case !isset($versions['Library']):
 528                        case $versions['Header'] == $versions['Library']:
 529                            define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_OPENSSL);
 530                            break;
 531                        default:
 532                            define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
 533                            define('MATH_BIGINTEGER_OPENSSL_DISABLE', true);
 534                    }
 535                    break;
 536                case true:
 537                    define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
 538            }
 539        }
 540
 541        $this->zero = new Math_BigInteger();
 542        $this->one = new Math_BigInteger(1);
 543
 544        $this->hash = new Crypt_Hash('sha1');
 545        $this->hLen = $this->hash->getLength();
 546        $this->hashName = 'sha1';
 547        $this->mgfHash = new Crypt_Hash('sha1');
 548        $this->mgfHLen = $this->mgfHash->getLength();
 549    }
 550
 551    /**
 552     * Create public / private key pair
 553     *
 554     * Returns an array with the following three elements:
 555     *  - 'privatekey': The private key.
 556     *  - 'publickey':  The public key.
 557     *  - 'partialkey': A partially computed key (if the execution time exceeded $timeout).
 558     *                  Will need to be passed back to Crypt_RSA::createKey() as the third parameter for further processing.
 559     *
 560     * @access public
 561     * @param optional Integer $bits
 562     * @param optional Integer $timeout
 563     * @param optional Math_BigInteger $p
 564     */
 565    function createKey($bits = 1024, $timeout = false, $partial = array())
 566    {
 567        if (!defined('CRYPT_RSA_EXPONENT')) {
 568            // http://en.wikipedia.org/wiki/65537_%28number%29
 569            define('CRYPT_RSA_EXPONENT', '65537');
 570        }
 571        // per <http://cseweb.ucsd.edu/~hovav/dist/survey.pdf#page=5>, this number ought not result in primes smaller
 572        // 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
 573        // to 384 bits then you're going to get a 384 bit prime and a 640 bit prime (384 + 1024 % 384). at least if
 574        // CRYPT_RSA_MODE is set to CRYPT_RSA_MODE_INTERNAL. if CRYPT_RSA_MODE is set to CRYPT_RSA_MODE_OPENSSL then
 575        // CRYPT_RSA_SMALLEST_PRIME is ignored (ie. multi-prime RSA support is more intended as a way to speed up RSA key
 576        // generation when there's a chance neither gmp nor OpenSSL are installed)
 577        if (!defined('CRYPT_RSA_SMALLEST_PRIME')) {
 578            define('CRYPT_RSA_SMALLEST_PRIME', 4096);
 579        }
 580
 581        // OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum
 582        if ( CRYPT_RSA_MODE == CRYPT_RSA_MODE_OPENSSL && $bits >= 384 && CRYPT_RSA_EXPONENT == 65537) {
 583            $config = array();
 584            if (isset($this->configFile)) {
 585                $config['config'] = $this->configFile;
 586            }
 587            $rsa = openssl_pkey_new(array('private_key_bits' => $bits) + $config);
 588            openssl_pkey_export($rsa, $privatekey, null, $config);
 589            $publickey = openssl_pkey_get_details($rsa);
 590            $publickey = $publickey['key'];
 591
 592            $privatekey = call_user_func_array(array($this, '_convertPrivateKey'), array_values($this->_parseKey($privatekey, CRYPT_RSA_PRIVATE_FORMAT_PKCS1)));
 593            $publickey = call_user_func_array(array($this, '_convertPublicKey'), array_values($this->_parseKey($publickey, CRYPT_RSA_PUBLIC_FORMAT_PKCS1)));
 594
 595            // clear the buffer of error strings stemming from a minimalistic openssl.cnf
 596            while (openssl_error_string() !== false);
 597
 598            return array(
 599                'privatekey' => $privatekey,
 600                'publickey' => $publickey,
 601                'partialkey' => false
 602            );
 603        }
 604
 605        static $e;
 606        if (!isset($e)) {
 607            $e = new Math_BigInteger(CRYPT_RSA_EXPONENT);
 608        }
 609
 610        extract($this->_generateMinMax($bits));
 611        $absoluteMin = $min;
 612        $temp = $bits >> 1; // divide by two to see how many bits P and Q would be
 613        if ($temp > CRYPT_RSA_SMALLEST_PRIME) {
 614            $num_primes = floor($bits / CRYPT_RSA_SMALLEST_PRIME);
 615            $temp = CRYPT_RSA_SMALLEST_PRIME;
 616        } else {
 617            $num_primes = 2;
 618        }
 619        extract($this->_generateMinMax($temp + $bits % $temp));
 620        $finalMax = $max;
 621        extract($this->_generateMinMax($temp));
 622
 623        $generator = new Math_BigInteger();
 624
 625        $n = $this->one->copy();
 626        if (!empty($partial)) {
 627            extract(unserialize($partial));
 628        } else {
 629            $exponents = $coefficients = $primes = array();
 630            $lcm = array(
 631                'top' => $this->one->copy(),
 632                'bottom' => false
 633            );
 634        }
 635
 636        $start = time();
 637        $i0 = count($primes) + 1;
 638
 639        do {
 640            for ($i = $i0; $i <= $num_primes; $i++) {
 641                if ($timeout !== false) {
 642                    $timeout-= time() - $start;
 643                    $start = time();
 644                    if ($timeout <= 0) {
 645                        return array(
 646                            'privatekey' => '',
 647                            'publickey'  => '',
 648                            'partialkey' => serialize(array(
 649                                'primes' => $primes,
 650                                'coefficients' => $coefficients,
 651                                'lcm' => $lcm,
 652                                'exponents' => $exponents
 653                            ))
 654                        );
 655                    }
 656                }
 657
 658                if ($i == $num_primes) {
 659                    list($min, $temp) = $absoluteMin->divide($n);
 660                    if (!$temp->equals($this->zero)) {
 661                        $min = $min->add($this->one); // ie. ceil()
 662                    }
 663                    $primes[$i] = $generator->randomPrime($min, $finalMax, $timeout);
 664                } else {
 665                    $primes[$i] = $generator->randomPrime($min, $max, $timeout);
 666                }
 667
 668                if ($primes[$i] === false) { // if we've reached the timeout
 669                    if (count($primes) > 1) {
 670                        $partialkey = '';
 671                    } else {
 672                        array_pop($primes);
 673                        $partialkey = serialize(array(
 674                            'primes' => $primes,
 675                            'coefficients' => $coefficients,
 676                            'lcm' => $lcm,
 677                            'exponents' => $exponents
 678                        ));
 679                    }
 680
 681                    return array(
 682                        'privatekey' => '',
 683                        'publickey'  => '',
 684                        'partialkey' => $partialkey
 685                    );
 686                }
 687
 688                // the first coefficient is calculated differently from the rest
 689                // ie. instead of being $primes[1]->modInverse($primes[2]), it's $primes[2]->modInverse($primes[1])
 690                if ($i > 2) {
 691                    $coefficients[$i] = $n->modInverse($primes[$i]);
 692                }
 693
 694                $n = $n->multiply($primes[$i]);
 695
 696                $temp = $primes[$i]->subtract($this->one);
 697
 698                // textbook RSA implementations use Euler's totient function instead of the least common multiple.
 699                // see http://en.wikipedia.org/wiki/Euler%27s_totient_function
 700                $lcm['top'] = $lcm['top']->multiply($temp);
 701                $lcm['bottom'] = $lcm['bottom'] === false ? $temp : $lcm['bottom']->gcd($temp);
 702
 703                $exponents[$i] = $e->modInverse($temp);
 704            }
 705
 706            list($temp) = $lcm['top']->divide($lcm['bottom']);
 707            $gcd = $temp->gcd($e);
 708            $i0 = 1;
 709        } while (!$gcd->equals($this->one));
 710
 711        $d = $e->modInverse($temp);
 712
 713        $coefficients[2] = $primes[2]->modInverse($primes[1]);
 714
 715        // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.2>:
 716        // RSAPrivateKey ::= SEQUENCE {
 717        //     version           Version,
 718        //     modulus           INTEGER,  -- n
 719        //     publicExponent    INTEGER,  -- e
 720        //     privateExponent   INTEGER,  -- d
 721        //     prime1            INTEGER,  -- p
 722        //     prime2            INTEGER,  -- q
 723        //     exponent1         INTEGER,  -- d mod (p-1)
 724        //     exponent2         INTEGER,  -- d mod (q-1)
 725        //     coefficient       INTEGER,  -- (inverse of q) mod p
 726        //     otherPrimeInfos   OtherPrimeInfos OPTIONAL
 727        // }
 728
 729        return array(
 730            'privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients),
 731            'publickey'  => $this->_convertPublicKey($n, $e),
 732            'partialkey' => false
 733        );
 734    }
 735
 736    /**
 737     * Convert a private key to the appropriate format.
 738     *
 739     * @access private
 740     * @see setPrivateKeyFormat()
 741     * @param String $RSAPrivateKey
 742     * @return String
 743     */
 744    function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients)
 745    {
 746        $num_primes = count($primes);
 747        $raw = array(
 748            'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi
 749            'modulus' => $n->toBytes(true),
 750            'publicExponent' => $e->toBytes(true),
 751            'privateExponent' => $d->toBytes(true),
 752            'prime1' => $primes[1]->toBytes(true),
 753            'prime2' => $primes[2]->toBytes(true),
 754            'exponent1' => $exponents[1]->toBytes(true),
 755            'exponent2' => $exponents[2]->toBytes(true),
 756            'coefficient' => $coefficients[2]->toBytes(true)
 757        );
 758
 759        // if the format in question does not support multi-prime rsa and multi-prime rsa was used,
 760        // call _convertPublicKey() instead.
 761        switch ($this->privateKeyFormat) {
 762            case CRYPT_RSA_PRIVATE_FORMAT_XML:
 763                if ($num_primes != 2) {
 764                    return false;
 765                }
 766                return "<RSAKeyValue>\r\n" .
 767                       '  <Modulus>' . base64_encode($raw['modulus']) . "</Modulus>\r\n" .
 768                       '  <Exponent>' . base64_encode($raw['publicExponent']) . "</Exponent>\r\n" .
 769                       '  <P>' . base64_encode($raw['prime1']) . "</P>\r\n" .
 770                       '  <Q>' . base64_encode($raw['prime2']) . "</Q>\r\n" .
 771                       '  <DP>' . base64_encode($raw['exponent1']) . "</DP>\r\n" .
 772                       '  <DQ>' . base64_encode($raw['exponent2']) . "</DQ>\r\n" .
 773                       '  <InverseQ>' . base64_encode($raw['coefficient']) . "</InverseQ>\r\n" .
 774                       '  <D>' . base64_encode($raw['privateExponent']) . "</D>\r\n" .
 775                       '</RSAKeyValue>';
 776                break;
 777            case CRYPT_RSA_PRIVATE_FORMAT_PUTTY:
 778                if ($num_primes != 2) {
 779                    return false;
 780                }
 781                $key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: ";
 782                $encryption = (!empty($this->password) || is_string($this->password)) ? 'aes256-cbc' : 'none';
 783                $key.= $encryption;
 784                $key.= "\r\nComment: " . $this->comment . "\r\n";
 785                $public = pack('Na*Na*Na*',
 786                    strlen('ssh-rsa'), 'ssh-rsa', strlen($raw['publicExponent']), $raw['publicExponent'], strlen($raw['modulus']), $raw['modulus']
 787                );
 788                $source = pack('Na*Na*Na*Na*',
 789                    strlen('ssh-rsa'), 'ssh-rsa', strlen($encryption), $encryption,
 790                    strlen($this->comment), $this->comment, strlen($public), $public
 791                );
 792                $public = base64_encode($public);
 793                $key.= "Public-Lines: " . ((strlen($public) + 63) >> 6) . "\r\n";
 794                $key.= chunk_split($public, 64);
 795                $private = pack('Na*Na*Na*Na*',
 796                    strlen($raw['privateExponent']), $raw['privateExponent'], strlen($raw['prime1']), $raw['prime1'],
 797                    strlen($raw['prime2']), $raw['prime2'], strlen($raw['coefficient']), $raw['coefficient']
 798                );
 799                if (empty($this->password) && !is_string($this->password)) {
 800                    $source.= pack('Na*', strlen($private), $private);
 801                    $hashkey = 'putty-private-key-file-mac-key';
 802                } else {
 803                    $private.= crypt_random_string(16 - (strlen($private) & 15));
 804                    $source.= pack('Na*', strlen($private), $private);
 805                    if (!class_exists('Crypt_AES')) {
 806                        include_once 'Crypt/AES.php';
 807                    }
 808                    $sequence = 0;
 809                    $symkey = '';
 810                    while (strlen($symkey) < 32) {
 811                        $temp = pack('Na*', $sequence++, $this->password);
 812                        $symkey.= pack('H*', sha1($temp));
 813                    }
 814                    $symkey = substr($symkey, 0, 32);
 815                    $crypto = new Crypt_AES();
 816
 817                    $crypto->setKey($symkey);
 818                    $crypto->disablePadding();
 819                    $private = $crypto->encrypt($private);
 820                    $hashkey = 'putty-private-key-file-mac-key' . $this->password;
 821                }
 822
 823                $private = base64_encode($private);
 824                $key.= 'Private-Lines: ' . ((strlen($private) + 63) >> 6) . "\r\n";
 825                $key.= chunk_split($private, 64);
 826                if (!class_exists('Crypt_Hash')) {
 827                    include_once 'Crypt/Hash.php';
 828                }
 829                $hash = new Crypt_Hash('sha1');
 830                $hash->setKey(pack('H*', sha1($hashkey)));
 831                $key.= 'Private-MAC: ' . bin2hex($hash->hash($source)) . "\r\n";
 832
 833                return $key;
 834            default: // eg. CRYPT_RSA_PRIVATE_FORMAT_PKCS1
 835                $components = array();
 836                foreach ($raw as $name => $value) {
 837                    $components[$name] = pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($value)), $value);
 838                }
 839
 840                $RSAPrivateKey = implode('', $components);
 841
 842                if ($num_primes > 2) {
 843                    $OtherPrimeInfos = '';
 844                    for ($i = 3; $i <= $num_primes; $i++) {
 845                        // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
 846                        //
 847                        // OtherPrimeInfo ::= SEQUENCE {
 848                        //     prime             INTEGER,  -- ri
 849                        //     exponent          INTEGER,  -- di
 850                        //     coefficient       INTEGER   -- ti
 851                        // }
 852                        $OtherPrimeInfo = pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true));
 853                        $OtherPrimeInfo.= pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true));
 854                        $OtherPrimeInfo.= pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true));
 855                        $OtherPrimeInfos.= pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo);
 856                    }
 857                    $RSAPrivateKey.= pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos);
 858                }
 859
 860                $RSAPrivateKey = pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
 861
 862                if ($this->privateKeyFormat == CRYPT_RSA_PRIVATE_FORMAT_PKCS8) {
 863                    $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
 864                    $RSAPrivateKey = pack('Ca*a*Ca*a*',
 865                        CRYPT_RSA_ASN1_INTEGER, "\01\00", $rsaOID, 4, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey
 866                    );
 867                    $RSAPrivateKey = pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
 868                    if (!empty($this->password) || is_string($this->password)) {
 869                        $salt = crypt_random_string(8);
 870                        $iterationCount = 2048;
 871
 872                        if (!class_exists('Crypt_DES')) {
 873                            include_once 'Crypt/DES.php';
 874                        }
 875                        $crypto = new Crypt_DES();
 876                        $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount);
 877                        $RSAPrivateKey = $crypto->encrypt($RSAPrivateKey);
 878
 879                        $parameters = pack('Ca*a*Ca*N',
 880                            CRYPT_RSA_ASN1_OCTETSTRING, $this->_encodeLength(strlen($salt)), $salt,
 881                            CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(4), $iterationCount
 882                        );
 883                        $pbeWithMD5AndDES_CBC = "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03";
 884
 885                        $encryptionAlgorithm = pack('Ca*a*Ca*a*',
 886                            CRYPT_RSA_ASN1_OBJECT, $this->_encodeLength(strlen($pbeWithMD5AndDES_CBC)), $pbeWithMD5AndDES_CBC,
 887                            CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($parameters)), $parameters
 888                        );
 889
 890                        $RSAPrivateKey = pack('Ca*a*Ca*a*',
 891                            CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($encryptionAlgorithm)), $encryptionAlgorithm,
 892                            CRYPT_RSA_ASN1_OCTETSTRING, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey
 893                        );
 894
 895                        $RSAPrivateKey = pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
 896
 897                        $RSAPrivateKey = "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" .
 898                                         chunk_split(base64_encode($RSAPrivateKey), 64) .
 899                                         '-----END ENCRYPTED PRIVATE KEY-----';
 900                    } else {
 901                        $RSAPrivateKey = "-----BEGIN PRIVATE KEY-----\r\n" .
 902                                         chunk_split(base64_encode($RSAPrivateKey), 64) .
 903                                         '-----END PRIVATE KEY-----';
 904                    }
 905                    return $RSAPrivateKey;
 906                }
 907
 908                if (!empty($this->password) || is_string($this->password)) {
 909                    $iv = crypt_random_string(8);
 910                    $symkey = pack('H*', md5($this->password . $iv)); // symkey is short for symmetric key
 911                    $symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8);
 912                    if (!class_exists('Crypt_TripleDES')) {
 913                        include_once 'Crypt/TripleDES.php';
 914                    }
 915                    $des = new Crypt_TripleDES();
 916                    $des->setKey($symkey);
 917                    $des->setIV($iv);
 918                    $iv = strtoupper(bin2hex($iv));
 919                    $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
 920                                     "Proc-Type: 4,ENCRYPTED\r\n" .
 921                                     "DEK-Info: DES-EDE3-CBC,$iv\r\n" .
 922                                     "\r\n" .
 923                                     chunk_split(base64_encode($des->encrypt($RSAPrivateKey)), 64) .
 924                                     '-----END RSA PRIVATE KEY-----';
 925                } else {
 926                    $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
 927                                     chunk_split(base64_encode($RSAPrivateKey), 64) .
 928                                     '-----END RSA PRIVATE KEY-----';
 929                }
 930
 931                return $RSAPrivateKey;
 932        }
 933    }
 934
 935    /**
 936     * Convert a public key to the appropriate format
 937     *
 938     * @access private
 939     * @see setPublicKeyFormat()
 940     * @param String $RSAPrivateKey
 941     * @return String
 942     */
 943    function _convertPublicKey($n, $e)
 944    {
 945        $modulus = $n->toBytes(true);
 946        $publicExponent = $e->toBytes(true);
 947
 948        switch ($this->publicKeyFormat) {
 949            case CRYPT_RSA_PUBLIC_FORMAT_RAW:
 950                return array('e' => $e->copy(), 'n' => $n->copy());
 951            case CRYPT_RSA_PUBLIC_FORMAT_XML:
 952                return "<RSAKeyValue>\r\n" .
 953                       '  <Modulus>' . base64_encode($modulus) . "</Modulus>\r\n" .
 954                       '  <Exponent>' . base64_encode($publicExponent) . "</Exponent>\r\n" .
 955                       '</RSAKeyValue>';
 956                break;
 957            case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
 958                // from <http://tools.ietf.org/html/rfc4253#page-15>:
 959                // string    "ssh-rsa"
 960                // mpint     e
 961                // mpint     n
 962                $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus);
 963                $RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment;
 964
 965                return $RSAPublicKey;
 966            default: // eg. CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW or CRYPT_RSA_PUBLIC_FORMAT_PKCS1
 967                // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.1>:
 968                // RSAPublicKey ::= SEQUENCE {
 969                //     modulus           INTEGER,  -- n
 970                //     publicExponent    INTEGER   -- e
 971                // }
 972                $components = array(
 973                    'modulus' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus),
 974                    'publicExponent' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($publicExponent)), $publicExponent)
 975                );
 976
 977                $RSAPublicKey = pack('Ca*a*a*',
 978                    CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])),
 979                    $components['modulus'], $components['publicExponent']
 980                );
 981
 982                if ($this->publicKeyFormat == CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW) {
 983                    $RSAPublicKey = "-----BEGIN RSA PUBLIC KEY-----\r\n" .
 984                                    chunk_split(base64_encode($RSAPublicKey), 64) .
 985                                    '-----END RSA PUBLIC KEY-----';
 986                } else {
 987                    // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
 988                    $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
 989                    $RSAPublicKey = chr(0) . $RSAPublicKey;
 990                    $RSAPublicKey = chr(3) . $this->_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey;
 991
 992                    $RSAPublicKey = pack('Ca*a*',
 993                        CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($rsaOID . $RSAPublicKey)), $rsaOID . $RSAPublicKey
 994                    );
 995
 996                    $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
 997                                     chunk_split(base64_encode($RSAPublicKey), 64) .
 998                                     '-----END PUBLIC KEY-----';
 999                }
1000
1001                return $RSAPublicKey;
1002        }
1003    }
1004
1005    /**
1006     * Break a public or private key down into its constituant components
1007     *
1008     * @access private
1009     * @see _convertPublicKey()
1010     * @see _convertPrivateKey()
1011     * @param String $key
1012     * @param Integer $type
1013     * @return Array
1014     */
1015    function _parseKey($key, $type)
1016    {
1017        if ($type != CRYPT_RSA_PUBLIC_FORMAT_RAW && !is_string($key)) {
1018            return false;
1019        }
1020
1021        switch ($type) {
1022            case CRYPT_RSA_PUBLIC_FORMAT_RAW:
1023                if (!is_array($key)) {
1024                    return false;
1025                }
1026                $components = array();
1027                switch (true) {
1028                    case isset($key['e']):
1029                        $components['publicExponent'] = $key['e']->copy();
1030                        break;
1031                    case isset($key['exponent']):
1032                        $components['publicExponent'] = $key['exponent']->copy();
1033                        break;
1034                    case isset($key['publicExponent']):
1035                        $components['publicExponent'] = $key['publicExponent']->copy();
1036                        break;
1037                    case isset($key[0]):
1038                        $components['publicExponent'] = $key[0]->copy();
1039                }
1040                switch (true) {
1041                    case isset($key['n']):
1042                        $components['modulus'] = $key['n']->copy();
1043                        break;
1044                    case isset($key['modulo']):
1045                        $components['modulus'] = $key['modulo']->copy();
1046                        break;
1047                    case isset($key['modulus']):
1048                        $components['modulus'] = $key['modulus']->copy();
1049                        break;
1050                    case isset($key[1]):
1051                        $components['modulus'] = $key[1]->copy();
1052                }
1053                return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false;
1054            case CRYPT_RSA_PRIVATE_FORMAT_PKCS1:
1055            case CRYPT_RSA_PRIVATE_FORMAT_PKCS8:
1056            case CRYPT_RSA_PUBLIC_FORMAT_PKCS1:
1057                /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
1058                   "outside the scope" of PKCS#1.  PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
1059                   protect private keys, however, that's not what OpenSSL* does.  OpenSSL protects private keys by adding
1060                   two new "fields" to the key - DEK-Info and Proc-Type.  These fields are discussed here:
1061
1062                   http://tools.ietf.org/html/rfc1421#section-4.6.1.1
1063                   http://tools.ietf.org/html/rfc1421#section-4.6.1.3
1064
1065                   DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell.
1066                   DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation
1067                   function.  As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's
1068                   own implementation.  ie. the implementation *is* the standard and any bugs that may exist in that
1069                   implementation are part of the standard, as well.
1070
1071                   * OpenSSL is the de facto standard.  It's utilized by OpenSSH and other projects */
1072                if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
1073                    $iv = pack('H*', trim($matches[2]));
1074                    $symkey = pack('H*', md5($this->password . substr($iv, 0, 8))); // symkey is short for symmetric key
1075                    $symkey.= pack('H*', md5($symkey . $this->password . substr($iv, 0, 8)));
1076                    // remove the Proc-Type / DEK-Info sections as they're no longer needed
1077                    $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key);
1078                    $ciphertext = $this->_extractBER($key);
1079                    if ($ciphertext === false) {
1080                        $ciphertext = $key;
1081                    }
1082                    switch ($matches[1]) {
1083                        case 'AES-256-CBC':
1084                            if (!class_exists('Crypt_AES')) {
1085                                include_once 'Crypt/AES.php';
1086                            }
1087                            $crypto = new Crypt_AES();
1088                            break;
1089                        case 'AES-128-CBC':
1090                            if (!class_exists('Crypt_AES')) {
1091                                include_once 'Crypt/AES.php';
1092                            }
1093                            $symkey = substr($symkey, 0, 16);
1094                            $crypto = new Crypt_AES();
1095                            break;
1096                        case 'DES-EDE3-CFB':
1097                            if (!class_exists('Crypt_TripleDES')) {
1098                                include_once 'Crypt/TripleDES.php';
1099                            }
1100                            $crypto = new Crypt_TripleDES(CRYPT_DES_MODE_CFB);
1101                            break;
1102                        case 'DES-EDE3-CBC':
1103                            if (!class_exists('Crypt_TripleDES')) {
1104                                include_once 'Crypt/TripleDES.php';
1105                            }
1106                            $symkey = substr($symkey, 0, 24);
1107                            $crypto = new Crypt_TripleDES();
1108                            break;
1109                        case 'DES-CBC':
1110                            if (!class_exists('Crypt_DES')) {
1111                                include_once 'Crypt/DES.php';
1112                            }
1113                            $crypto = new Crypt_DES();
1114                            break;
1115                        default:
1116                            return false;
1117                    }
1118                    $crypto->setKey($symkey);
1119                    $crypto->setIV($iv);
1120                    $decoded = $crypto->decrypt($ciphertext);
1121                } else {
1122                    $decoded = $this->_extractBER($key);
1123                }
1124
1125                if ($decoded !== false) {
1126                    $key = $decoded;
1127                }
1128
1129                $components = array();
1130
1131                if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
1132                    return false;
1133                }
1134                if ($this->_decodeLength($key) != strlen($key)) {
1135                    return false;
1136                }
1137
1138                $tag = ord($this->_string_shift($key));
1139                /* intended for keys for which OpenSSL's asn1parse returns the following:
1140
1141                    0:d=0  hl=4 l= 631 cons: SEQUENCE
1142                    4:d=1  hl=2 l=   1 prim:  INTEGER           :00
1143                    7:d=1  hl=2 l=  13 cons:  SEQUENCE
1144                    9:d=2  hl=2 l=   9 prim:   OBJECT            :rsaEncryption
1145                   20:d=2  hl=2 l=   0 prim:   NULL
1146                   22:d=1  hl=4 l= 609 prim:  OCTET STRING
1147
1148                   ie. PKCS8 keys*/
1149
1150                if ($tag == CRYPT_RSA_ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") {
1151                    $this->_string_shift($key, 3);
1152                    $tag = CRYPT_RSA_ASN1_SEQUENCE;
1153                }
1154
1155                if ($tag == CRYPT_RSA_ASN1_SEQUENCE) {
1156                    $temp = $this->_string_shift($key, $this->_decodeLength($key));
1157                    if (ord($this->_string_shift($temp)) != CRYPT_RSA_ASN1_OBJECT) {
1158                        return false;
1159                    }
1160                    $length = $this->_decodeLength($temp);
1161                    switch ($this->_string_shift($temp, $length)) {
1162                        case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption
1163                            break;
1164                        case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC
1165                            /*
1166                               PBEParameter ::= SEQUENCE {
1167                                   salt OCTET STRING (SIZE(8)),
1168                                   iterationCount INTEGER }
1169                            */
1170                            if (ord($this->_string_shift($temp)) != CRYPT_RSA_ASN1_SEQUENCE) {
1171                                return false;
1172                            }
1173                            if ($this->_decodeLength($temp) != strlen($temp)) {
1174                                return false;
1175                            }
1176                            $this->_string_shift($temp); // assume it's an octet string
1177                            $salt = $this->_string_shift($temp, $this->_decodeLength($temp));
1178                            if (ord($this->_string_shift($temp)) != CRYPT_RSA_ASN1_INTEGER) {
1179                                return false;
1180                            }
1181                            $this->_decodeLength($temp);
1182                            list(, $iterationCount) = unpack('N', str_pad($temp, 4, chr(0), STR_PAD_LEFT));
1183                            $this->_string_shift($key); // assume it's an octet string
1184                            $length = $this->_decodeLength($key);
1185                            if (strlen($key) != $length) {
1186                                return false;
1187                            }
1188
1189                            if (!class_exists('Crypt_DES')) {
1190                                include_once 'Crypt/DES.php';
1191                            }
1192                            $crypto = new Crypt_DES();
1193                            $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount);
1194                            $key = $crypto->decrypt($key);
1195                            if ($key === false) {
1196                                return false;
1197                            }
1198                            return $this->_parseKey($key, CRYPT_RSA_PRIVATE_FORMAT_PKCS1);
1199                        default:
1200                            return false;
1201                    }
1202                    /* intended for keys for which OpenSSL's asn1parse returns the following:
1203
1204                        0:d=0  hl=4 l= 290 cons: SEQUENCE
1205                        4:d=1  hl=2 l=  13 cons:  SEQUENCE
1206                        6:d=2  hl=2 l=   9 prim:   OBJECT            :rsaEncryption
1207                       17:d=2  hl=2 l=   0 prim:   NULL
1208                       19:d=1  hl=4 l= 271 prim:  BIT STRING */
1209                    $tag = ord($this->_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag
1210                    $this->_decodeLength($key); // skip over the BIT STRING / OCTET STRING length
1211                    // "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of
1212                    //  unused bits in the final subsequent octet. The number shall be in the range zero to seven."
1213                    //  -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2)
1214                    if ($tag == CRYPT_RSA_ASN1_BITSTRING) {
1215                        $this->_string_shift($key);
1216                    }
1217                    if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
1218                        return false;
1219                    }
1220                    if ($this->_decodeLength($key) != strlen($key)) {
1221                        return false;
1222                    }
1223                    $tag = ord($this->_string_shift($key));
1224                }
1225                if ($tag != CRYPT_RSA_ASN1_INTEGER) {
1226                    return false;
1227                }
1228
1229                $length = $this->_decodeLength($key);
1230                $temp = $this->_string_shift($key, $length);
1231                if (strlen($temp) != 1 || ord($temp) > 2) {
1232                    $components['modulus'] = new Math_BigInteger($temp, 256);
1233                    $this->_string_shift($key); // skip over CRYPT_RSA_ASN1_INTEGER
1234                    $length = $this->_decodeLength($key);
1235                    $components[$type == CRYPT_RSA_PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1236
1237                    return $components;
1238                }
1239                if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_INTEGER) {
1240                    return false;
1241                }
1242                $length = $this->_decodeLength($key);
1243                $components['modulus'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1244                $this->_string_shift($key);
1245                $length = $this->_decodeLength($key);
1246                $components['publicExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1247                $this->_string_shift($key);
1248                $length = $this->_decodeLength($key);
1249                $components['privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1250                $this->_string_shift($key);
1251                $length = $this->_decodeLength($key);
1252                $components['primes'] = array(1 => new Math_BigInteger($this->_string_shift($key, $length), 256));
1253                $this->_string_shift($key);
1254                $length = $this->_decodeLength($key);
1255                $components['primes'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1256                $this->_string_shift($key);
1257                $length = $this->_decodeLength($key);
1258                $components['expon…

Large files files are truncated, but you can click here to view the full file