PageRenderTime 18ms CodeModel.GetById 5ms app.highlight 34ms RepoModel.GetById 1ms app.codeStats 1ms

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

https://github.com/phocode/my_reddit
PHP | 2990 lines | 1582 code | 294 blank | 1114 comment | 280 complexity | f3373a03929df88dd6d5e71e409cb834 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            switch (true) {
 497                // Math/BigInteger's openssl requirements are a little less stringent than Crypt/RSA's. in particular,
 498                // Math/BigInteger doesn't require an openssl.cfg file whereas Crypt/RSA does. so if Math/BigInteger
 499                // can't use OpenSSL it can be pretty trivially assumed, then, that Crypt/RSA can't either.
 500                case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'):
 501                    define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
 502                    break;
 503                // openssl_pkey_get_details - which is used in the only place Crypt/RSA.php uses OpenSSL - was introduced in PHP 5.2.0
 504                case !function_exists('openssl_pkey_get_details'):
 505                    define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
 506                    break;
 507                case extension_loaded('openssl') && version_compare(PHP_VERSION, '4.2.0', '>=') && file_exists($this->configFile):
 508                    // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
 509                    ob_start();
 510                    @phpinfo();
 511                    $content = ob_get_contents();
 512                    ob_end_clean();
 513
 514                    preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches);
 515
 516                    $versions = array();
 517                    if (!empty($matches[1])) {
 518                       for ($i = 0; $i < count($matches[1]); $i++) {
 519                          $versions[$matches[1][$i]] = trim(str_replace('=>', '', strip_tags($matches[2][$i])));
 520                       }
 521                    }
 522
 523                    // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+
 524                    switch (true) {
 525                        case !isset($versions['Header']):
 526                        case !isset($versions['Library']):
 527                        case $versions['Header'] == $versions['Library']:
 528                            define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_OPENSSL);
 529                            break;
 530                        default:
 531                            define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
 532                            define('MATH_BIGINTEGER_OPENSSL_DISABLE', true);
 533                    }
 534                    break;
 535                default:
 536                    define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
 537            }
 538        }
 539
 540        $this->zero = new Math_BigInteger();
 541        $this->one = new Math_BigInteger(1);
 542
 543        $this->hash = new Crypt_Hash('sha1');
 544        $this->hLen = $this->hash->getLength();
 545        $this->hashName = 'sha1';
 546        $this->mgfHash = new Crypt_Hash('sha1');
 547        $this->mgfHLen = $this->mgfHash->getLength();
 548    }
 549
 550    /**
 551     * Create public / private key pair
 552     *
 553     * Returns an array with the following three elements:
 554     *  - 'privatekey': The private key.
 555     *  - 'publickey':  The public key.
 556     *  - 'partialkey': A partially computed key (if the execution time exceeded $timeout).
 557     *                  Will need to be passed back to Crypt_RSA::createKey() as the third parameter for further processing.
 558     *
 559     * @access public
 560     * @param optional Integer $bits
 561     * @param optional Integer $timeout
 562     * @param optional Math_BigInteger $p
 563     */
 564    function createKey($bits = 1024, $timeout = false, $partial = array())
 565    {
 566        if (!defined('CRYPT_RSA_EXPONENT')) {
 567            // http://en.wikipedia.org/wiki/65537_%28number%29
 568            define('CRYPT_RSA_EXPONENT', '65537');
 569        }
 570        // per <http://cseweb.ucsd.edu/~hovav/dist/survey.pdf#page=5>, this number ought not result in primes smaller
 571        // 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
 572        // to 384 bits then you're going to get a 384 bit prime and a 640 bit prime (384 + 1024 % 384). at least if
 573        // CRYPT_RSA_MODE is set to CRYPT_RSA_MODE_INTERNAL. if CRYPT_RSA_MODE is set to CRYPT_RSA_MODE_OPENSSL then
 574        // CRYPT_RSA_SMALLEST_PRIME is ignored (ie. multi-prime RSA support is more intended as a way to speed up RSA key
 575        // generation when there's a chance neither gmp nor OpenSSL are installed)
 576        if (!defined('CRYPT_RSA_SMALLEST_PRIME')) {
 577            define('CRYPT_RSA_SMALLEST_PRIME', 4096);
 578        }
 579
 580        // OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum
 581        if ( CRYPT_RSA_MODE == CRYPT_RSA_MODE_OPENSSL && $bits >= 384 && CRYPT_RSA_EXPONENT == 65537) {
 582            $config = array();
 583            if (isset($this->configFile)) {
 584                $config['config'] = $this->configFile;
 585            }
 586            $rsa = openssl_pkey_new(array('private_key_bits' => $bits) + $config);
 587            openssl_pkey_export($rsa, $privatekey, null, $config);
 588            $publickey = openssl_pkey_get_details($rsa);
 589            $publickey = $publickey['key'];
 590
 591            $privatekey = call_user_func_array(array($this, '_convertPrivateKey'), array_values($this->_parseKey($privatekey, CRYPT_RSA_PRIVATE_FORMAT_PKCS1)));
 592            $publickey = call_user_func_array(array($this, '_convertPublicKey'), array_values($this->_parseKey($publickey, CRYPT_RSA_PUBLIC_FORMAT_PKCS1)));
 593
 594            // clear the buffer of error strings stemming from a minimalistic openssl.cnf
 595            while (openssl_error_string() !== false);
 596
 597            return array(
 598                'privatekey' => $privatekey,
 599                'publickey' => $publickey,
 600                'partialkey' => false
 601            );
 602        }
 603
 604        static $e;
 605        if (!isset($e)) {
 606            $e = new Math_BigInteger(CRYPT_RSA_EXPONENT);
 607        }
 608
 609        extract($this->_generateMinMax($bits));
 610        $absoluteMin = $min;
 611        $temp = $bits >> 1; // divide by two to see how many bits P and Q would be
 612        if ($temp > CRYPT_RSA_SMALLEST_PRIME) {
 613            $num_primes = floor($bits / CRYPT_RSA_SMALLEST_PRIME);
 614            $temp = CRYPT_RSA_SMALLEST_PRIME;
 615        } else {
 616            $num_primes = 2;
 617        }
 618        extract($this->_generateMinMax($temp + $bits % $temp));
 619        $finalMax = $max;
 620        extract($this->_generateMinMax($temp));
 621
 622        $generator = new Math_BigInteger();
 623
 624        $n = $this->one->copy();
 625        if (!empty($partial)) {
 626            extract(unserialize($partial));
 627        } else {
 628            $exponents = $coefficients = $primes = array();
 629            $lcm = array(
 630                'top' => $this->one->copy(),
 631                'bottom' => false
 632            );
 633        }
 634
 635        $start = time();
 636        $i0 = count($primes) + 1;
 637
 638        do {
 639            for ($i = $i0; $i <= $num_primes; $i++) {
 640                if ($timeout !== false) {
 641                    $timeout-= time() - $start;
 642                    $start = time();
 643                    if ($timeout <= 0) {
 644                        return array(
 645                            'privatekey' => '',
 646                            'publickey'  => '',
 647                            'partialkey' => serialize(array(
 648                                'primes' => $primes,
 649                                'coefficients' => $coefficients,
 650                                'lcm' => $lcm,
 651                                'exponents' => $exponents
 652                            ))
 653                        );
 654                    }
 655                }
 656
 657                if ($i == $num_primes) {
 658                    list($min, $temp) = $absoluteMin->divide($n);
 659                    if (!$temp->equals($this->zero)) {
 660                        $min = $min->add($this->one); // ie. ceil()
 661                    }
 662                    $primes[$i] = $generator->randomPrime($min, $finalMax, $timeout);
 663                } else {
 664                    $primes[$i] = $generator->randomPrime($min, $max, $timeout);
 665                }
 666
 667                if ($primes[$i] === false) { // if we've reached the timeout
 668                    if (count($primes) > 1) {
 669                        $partialkey = '';
 670                    } else {
 671                        array_pop($primes);
 672                        $partialkey = serialize(array(
 673                            'primes' => $primes,
 674                            'coefficients' => $coefficients,
 675                            'lcm' => $lcm,
 676                            'exponents' => $exponents
 677                        ));
 678                    }
 679
 680                    return array(
 681                        'privatekey' => '',
 682                        'publickey'  => '',
 683                        'partialkey' => $partialkey
 684                    );
 685                }
 686
 687                // the first coefficient is calculated differently from the rest
 688                // ie. instead of being $primes[1]->modInverse($primes[2]), it's $primes[2]->modInverse($primes[1])
 689                if ($i > 2) {
 690                    $coefficients[$i] = $n->modInverse($primes[$i]);
 691                }
 692
 693                $n = $n->multiply($primes[$i]);
 694
 695                $temp = $primes[$i]->subtract($this->one);
 696
 697                // textbook RSA implementations use Euler's totient function instead of the least common multiple.
 698                // see http://en.wikipedia.org/wiki/Euler%27s_totient_function
 699                $lcm['top'] = $lcm['top']->multiply($temp);
 700                $lcm['bottom'] = $lcm['bottom'] === false ? $temp : $lcm['bottom']->gcd($temp);
 701
 702                $exponents[$i] = $e->modInverse($temp);
 703            }
 704
 705            list($temp) = $lcm['top']->divide($lcm['bottom']);
 706            $gcd = $temp->gcd($e);
 707            $i0 = 1;
 708        } while (!$gcd->equals($this->one));
 709
 710        $d = $e->modInverse($temp);
 711
 712        $coefficients[2] = $primes[2]->modInverse($primes[1]);
 713
 714        // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.2>:
 715        // RSAPrivateKey ::= SEQUENCE {
 716        //     version           Version,
 717        //     modulus           INTEGER,  -- n
 718        //     publicExponent    INTEGER,  -- e
 719        //     privateExponent   INTEGER,  -- d
 720        //     prime1            INTEGER,  -- p
 721        //     prime2            INTEGER,  -- q
 722        //     exponent1         INTEGER,  -- d mod (p-1)
 723        //     exponent2         INTEGER,  -- d mod (q-1)
 724        //     coefficient       INTEGER,  -- (inverse of q) mod p
 725        //     otherPrimeInfos   OtherPrimeInfos OPTIONAL
 726        // }
 727
 728        return array(
 729            'privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients),
 730            'publickey'  => $this->_convertPublicKey($n, $e),
 731            'partialkey' => false
 732        );
 733    }
 734
 735    /**
 736     * Convert a private key to the appropriate format.
 737     *
 738     * @access private
 739     * @see setPrivateKeyFormat()
 740     * @param String $RSAPrivateKey
 741     * @return String
 742     */
 743    function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients)
 744    {
 745        $signed = $this->privateKeyFormat != CRYPT_RSA_PRIVATE_FORMAT_XML;
 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($signed),
 750            'publicExponent' => $e->toBytes($signed),
 751            'privateExponent' => $d->toBytes($signed),
 752            'prime1' => $primes[1]->toBytes($signed),
 753            'prime2' => $primes[2]->toBytes($signed),
 754            'exponent1' => $exponents[1]->toBytes($signed),
 755            'exponent2' => $exponents[2]->toBytes($signed),
 756            'coefficient' => $coefficients[2]->toBytes($signed)
 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        $signed = $this->publicKeyFormat != CRYPT_RSA_PUBLIC_FORMAT_XML;
 946
 947        $modulus = $n->toBytes($signed);
 948        $publicExponent = $e->toBytes($signed);
 949
 950        switch ($this->publicKeyFormat) {
 951            case CRYPT_RSA_PUBLIC_FORMAT_RAW:
 952                return array('e' => $e->copy(), 'n' => $n->copy());
 953            case CRYPT_RSA_PUBLIC_FORMAT_XML:
 954                return "<RSAKeyValue>\r\n" .
 955                       '  <Modulus>' . base64_encode($modulus) . "</Modulus>\r\n" .
 956                       '  <Exponent>' . base64_encode($publicExponent) . "</Exponent>\r\n" .
 957                       '</RSAKeyValue>';
 958                break;
 959            case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
 960                // from <http://tools.ietf.org/html/rfc4253#page-15>:
 961                // string    "ssh-rsa"
 962                // mpint     e
 963                // mpint     n
 964                $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus);
 965                $RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment;
 966
 967                return $RSAPublicKey;
 968            default: // eg. CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW or CRYPT_RSA_PUBLIC_FORMAT_PKCS1
 969                // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.1>:
 970                // RSAPublicKey ::= SEQUENCE {
 971                //     modulus           INTEGER,  -- n
 972                //     publicExponent    INTEGER   -- e
 973                // }
 974                $components = array(
 975                    'modulus' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus),
 976                    'publicExponent' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($publicExponent)), $publicExponent)
 977                );
 978
 979                $RSAPublicKey = pack('Ca*a*a*',
 980                    CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])),
 981                    $components['modulus'], $components['publicExponent']
 982                );
 983
 984                if ($this->publicKeyFormat == CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW) {
 985                    $RSAPublicKey = "-----BEGIN RSA PUBLIC KEY-----\r\n" .
 986                                    chunk_split(base64_encode($RSAPublicKey), 64) .
 987                                    '-----END RSA PUBLIC KEY-----';
 988                } else {
 989                    // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
 990                    $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
 991                    $RSAPublicKey = chr(0) . $RSAPublicKey;
 992                    $RSAPublicKey = chr(3) . $this->_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey;
 993
 994                    $RSAPublicKey = pack('Ca*a*',
 995                        CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($rsaOID . $RSAPublicKey)), $rsaOID . $RSAPublicKey
 996                    );
 997
 998                    $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
 999                                     chunk_split(base64_encode($RSAPublicKey), 64) .
1000                                     '-----END PUBLIC KEY-----';
1001                }
1002
1003                return $RSAPublicKey;
1004        }
1005    }
1006
1007    /**
1008     * Break a public or private key down into its constituant components
1009     *
1010     * @access private
1011     * @see _convertPublicKey()
1012     * @see _convertPrivateKey()
1013     * @param String $key
1014     * @param Integer $type
1015     * @return Array
1016     */
1017    function _parseKey($key, $type)
1018    {
1019        if ($type != CRYPT_RSA_PUBLIC_FORMAT_RAW && !is_string($key)) {
1020            return false;
1021        }
1022
1023        switch ($type) {
1024            case CRYPT_RSA_PUBLIC_FORMAT_RAW:
1025                if (!is_array($key)) {
1026                    return false;
1027                }
1028                $components = array();
1029                switch (true) {
1030                    case isset($key['e']):
1031                        $components['publicExponent'] = $key['e']->copy();
1032                        break;
1033                    case isset($key['exponent']):
1034                        $components['publicExponent'] = $key['exponent']->copy();
1035                        break;
1036                    case isset($key['publicExponent']):
1037                        $components['publicExponent'] = $key['publicExponent']->copy();
1038                        break;
1039                    case isset($key[0]):
1040                        $components['publicExponent'] = $key[0]->copy();
1041                }
1042                switch (true) {
1043                    case isset($key['n']):
1044                        $components['modulus'] = $key['n']->copy();
1045                        break;
1046                    case isset($key['modulo']):
1047                        $components['modulus'] = $key['modulo']->copy();
1048                        break;
1049                    case isset($key['modulus']):
1050                        $components['modulus'] = $key['modulus']->copy();
1051                        break;
1052                    case isset($key[1]):
1053                        $components['modulus'] = $key[1]->copy();
1054                }
1055                return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false;
1056            case CRYPT_RSA_PRIVATE_FORMAT_PKCS1:
1057            case CRYPT_RSA_PRIVATE_FORMAT_PKCS8:
1058            case CRYPT_RSA_PUBLIC_FORMAT_PKCS1:
1059                /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
1060                   "outside the scope" of PKCS#1.  PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
1061                   protect private keys, however, that's not what OpenSSL* does.  OpenSSL protects private keys by adding
1062                   two new "fields" to the key - DEK-Info and Proc-Type.  These fields are discussed here:
1063
1064                   http://tools.ietf.org/html/rfc1421#section-4.6.1.1
1065                   http://tools.ietf.org/html/rfc1421#section-4.6.1.3
1066
1067                   DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell.
1068                   DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation
1069                   function.  As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's
1070                   own implementation.  ie. the implementation *is* the standard and any bugs that may exist in that
1071                   implementation are part of the standard, as well.
1072
1073                   * OpenSSL is the de facto standard.  It's utilized by OpenSSH and other projects */
1074                if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
1075                    $iv = pack('H*', trim($matches[2]));
1076                    $symkey = pack('H*', md5($this->password . substr($iv, 0, 8))); // symkey is short for symmetric key
1077                    $symkey.= pack('H*', md5($symkey . $this->password . substr($iv, 0, 8)));
1078                    // remove the Proc-Type / DEK-Info sections as they're no longer needed
1079                    $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key);
1080                    $ciphertext = $this->_extractBER($key);
1081                    if ($ciphertext === false) {
1082                        $ciphertext = $key;
1083                    }
1084                    switch ($matches[1]) {
1085                        case 'AES-256-CBC':
1086                            if (!class_exists('Crypt_AES')) {
1087                                include_once 'Crypt/AES.php';
1088                            }
1089                            $crypto = new Crypt_AES();
1090                            break;
1091                        case 'AES-128-CBC':
1092                            if (!class_exists('Crypt_AES')) {
1093                                include_once 'Crypt/AES.php';
1094                            }
1095                            $symkey = substr($symkey, 0, 16);
1096                            $crypto = new Crypt_AES();
1097                            break;
1098                        case 'DES-EDE3-CFB':
1099                            if (!class_exists('Crypt_TripleDES')) {
1100                                include_once 'Crypt/TripleDES.php';
1101                            }
1102                            $crypto = new Crypt_TripleDES(CRYPT_DES_MODE_CFB);
1103                            break;
1104                        case 'DES-EDE3-CBC':
1105                            if (!class_exists('Crypt_TripleDES')) {
1106                                include_once 'Crypt/TripleDES.php';
1107                            }
1108                            $symkey = substr($symkey, 0, 24);
1109                            $crypto = new Crypt_TripleDES();
1110                            break;
1111                        case 'DES-CBC':
1112                            if (!class_exists('Crypt_DES')) {
1113                                include_once 'Crypt/DES.php';
1114                            }
1115                            $crypto = new Crypt_DES();
1116                            break;
1117                        default:
1118                            return false;
1119                    }
1120                    $crypto->setKey($symkey);
1121                    $crypto->setIV($iv);
1122                    $decoded = $crypto->decrypt($ciphertext);
1123                } else {
1124                    $decoded = $this->_extractBER($key);
1125                }
1126
1127                if ($decoded !== false) {
1128                    $key = $decoded;
1129                }
1130
1131                $components = array();
1132
1133                if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
1134                    return false;
1135                }
1136                if ($this->_decodeLength($key) != strlen($key)) {
1137                    return false;
1138                }
1139
1140                $tag = ord($this->_string_shift($key));
1141                /* intended for keys for which OpenSSL's asn1parse returns the following:
1142
1143                    0:d=0  hl=4 l= 631 cons: SEQUENCE
1144                    4:d=1  hl=2 l=   1 prim:  INTEGER           :00
1145                    7:d=1  hl=2 l=  13 cons:  SEQUENCE
1146                    9:d=2  hl=2 l=   9 prim:   OBJECT            :rsaEncryption
1147                   20:d=2  hl=2 l=   0 prim:   NULL
1148                   22:d=1  hl=4 l= 609 prim:  OCTET STRING
1149
1150                   ie. PKCS8 keys*/
1151
1152                if ($tag == CRYPT_RSA_ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") {
1153                    $this->_string_shift($key, 3);
1154                    $tag = CRYPT_RSA_ASN1_SEQUENCE;
1155                }
1156
1157                if ($tag == CRYPT_RSA_ASN1_SEQUENCE) {
1158                    $temp = $this->_string_shift($key, $this->_decodeLength($key));
1159                    if (ord($this->_string_shift($temp)) != CRYPT_RSA_ASN1_OBJECT) {
1160                        return false;
1161                    }
1162                    $length = $this->_decodeLength($temp);
1163                    switch ($this->_string_shift($temp, $length)) {
1164                        case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption
1165                            break;
1166                        case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC
1167                            /*
1168                               PBEParameter ::= SEQUENCE {
1169                                   salt OCTET STRING (SIZE(8)),
1170                                   iterationCount INTEGER }
1171                            */
1172                            if (ord($this->_string_shift($temp)) != CRYPT_RSA_ASN1_SEQUENCE) {
1173                                return false;
1174                            }
1175                            if ($this->_decodeLength($temp) != strlen($temp)) {
1176                                return false;
1177                            }
1178                            $this->_string_shift($temp); // assume it's an octet string
1179                            $salt = $this->_string_shift($temp, $this->_decodeLength($temp));
1180                            if (ord($this->_string_shift($temp)) != CRYPT_RSA_ASN1_INTEGER) {
1181                                return false;
1182                            }
1183                            $this->_decodeLength($temp);
1184                            list(, $iterationCount) = unpack('N', str_pad($temp, 4, chr(0), STR_PAD_LEFT));
1185                            $this->_string_shift($key); // assume it's an octet string
1186                            $length = $this->_decodeLength($key);
1187                            if (strlen($key) != $length) {
1188                                return false;
1189                            }
1190
1191                            if (!class_exists('Crypt_DES')) {
1192                                include_once 'Crypt/DES.php';
1193                            }
1194                            $crypto = new Crypt_DES();
1195                            $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount);
1196                            $key = $crypto->decrypt($key);
1197                            if ($key === false) {
1198                                return false;
1199                            }
1200                            return $this->_parseKey($key, CRYPT_RSA_PRIVATE_FORMAT_PKCS1);
1201                        default:
1202                            return false;
1203                    }
1204                    /* intended for keys for which OpenSSL's asn1parse returns the following:
1205
1206                        0:d=0  hl=4 l= 290 cons: SEQUENCE
1207                        4:d=1  hl=2 l=  13 cons:  SEQUENCE
1208                        6:d=2  hl=2 l=   9 prim:   OBJECT            :rsaEncryption
1209                       17:d=2  hl=2 l=   0 prim:   NULL
1210                       19:d=1  hl=4 l= 271 prim:  BIT STRING */
1211                    $tag = ord($this->_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag
1212                    $this->_decodeLength($key); // skip over the BIT STRING / OCTET STRING length
1213                    // "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of
1214                    //  unused bits in the final subsequent octet. The number shall be in the range zero to seven."
1215                    //  -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2)
1216                    if ($tag == CRYPT_RSA_ASN1_BITSTRING) {
1217                        $this->_string_shift($key);
1218                    }
1219                    if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
1220                        return false;
1221                    }
1222                    if ($this->_decodeLength($key) != strlen($key)) {
1223                        return false;
1224                    }
1225                    $tag = ord($this->_string_shift($key));
1226                }
1227                if ($tag != CRYPT_RSA_ASN1_INTEGER) {
1228                    return false;
1229                }
1230
1231                $length = $this->_decodeLength($key);
1232                $temp = $this->_string_shift($key, $length);
1233                if (strlen($temp) != 1 || ord($temp) > 2) {
1234                    $components['modulus'] = new Math_BigInteger($temp, 256);
1235                    $this->_string_shift($key); // skip over CRYPT_RSA_ASN1_INTEGER
1236                    $length = $this->_decodeLength($key);
1237                    $components[$type == CRYPT_RSA_PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1238
1239                    return $components;
1240                }
1241                if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_INTEGER) {
1242                    return false;
1243                }
1244                $length = $this->_decodeLength($key);
1245                $components['modulus'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1246                $this->_string_shift($key);
1247                $length = $this->_decodeLength($key);
1248                $components['publicExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1249                $this->_string_shift($key);
1250                $length = $this->_decodeLength($key);
1251                $components['privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1252                $this->_string_shift($key);
1253                $length = $this->_decodeLength($key);
1254                $components['primes'] = array(1 => new Math_BigInteger($this->_string_shift($key, $length), 256));
1255                $this->_string_shift($key);
1256                $length = $this->_decodeLength($key);
1257                $components['primes'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1258                $t…

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