]> git.mxchange.org Git - friendica-addons.git/blob - securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php
securemail: update pgp library
[friendica-addons.git] / securemail / vendor / phpseclib / phpseclib / phpseclib / Crypt / RSA.php
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 2009 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
77 if (!function_exists('crypt_random_string')) {
78     include_once 'Random.php';
79 }
80
81 /**
82  * Include Crypt_Hash
83  */
84 if (!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  */
102 define('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  */
109 define('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  */
126 define('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  */
133 define('CRYPT_RSA_SIGNATURE_PKCS1', 2);
134 /**#@-*/
135
136 /**#@+
137  * @access private
138  * @see Crypt_RSA::createKey()
139  */
140 /**
141  * ASN1 Integer
142  */
143 define('CRYPT_RSA_ASN1_INTEGER',     2);
144 /**
145  * ASN1 Bit String
146  */
147 define('CRYPT_RSA_ASN1_BITSTRING',   3);
148 /**
149  * ASN1 Octet String
150  */
151 define('CRYPT_RSA_ASN1_OCTETSTRING', 4);
152 /**
153  * ASN1 Object Identifier
154  */
155 define('CRYPT_RSA_ASN1_OBJECT',      6);
156 /**
157  * ASN1 Sequence (with the constucted bit set)
158  */
159 define('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  */
169 define('CRYPT_RSA_MODE_INTERNAL', 1);
170 /**
171  * To use the OpenSSL library
172  *
173  * (if enabled; otherwise, the internal implementation will be used)
174  */
175 define('CRYPT_RSA_MODE_OPENSSL', 2);
176 /**#@-*/
177
178 /**
179  * Default openSSL configuration file.
180  */
181 define('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  */
193 define('CRYPT_RSA_PRIVATE_FORMAT_PKCS1', 0);
194 /**
195  * PuTTY formatted private key
196  */
197 define('CRYPT_RSA_PRIVATE_FORMAT_PUTTY', 1);
198 /**
199  * XML formatted private key
200  */
201 define('CRYPT_RSA_PRIVATE_FORMAT_XML', 2);
202 /**
203  * PKCS#8 formatted private key
204  */
205 define('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  */
226 define('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  */
238 define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1', 4);
239 define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW', 4);
240 /**
241  * XML formatted public key
242  */
243 define('CRYPT_RSA_PUBLIC_FORMAT_XML', 5);
244 /**
245  * OpenSSH formatted public key
246  *
247  * Place in $HOME/.ssh/authorized_keys
248  */
249 define('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  */
263 define('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  */
273 class 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                             $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i])));
520
521                             // Remove letter part in OpenSSL version
522                             if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) {
523                                 $versions[$matches[1][$i]] = $fullVersion;
524                             } else {
525                                 $versions[$matches[1][$i]] = $m[0];
526                             }
527                         }
528                     }
529
530                     // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+
531                     switch (true) {
532                         case !isset($versions['Header']):
533                         case !isset($versions['Library']):
534                         case $versions['Header'] == $versions['Library']:
535                             define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_OPENSSL);
536                             break;
537                         default:
538                             define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
539                             define('MATH_BIGINTEGER_OPENSSL_DISABLE', true);
540                     }
541                     break;
542                 default:
543                     define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
544             }
545         }
546
547         $this->zero = new Math_BigInteger();
548         $this->one = new Math_BigInteger(1);
549
550         $this->hash = new Crypt_Hash('sha1');
551         $this->hLen = $this->hash->getLength();
552         $this->hashName = 'sha1';
553         $this->mgfHash = new Crypt_Hash('sha1');
554         $this->mgfHLen = $this->mgfHash->getLength();
555     }
556
557     /**
558      * Create public / private key pair
559      *
560      * Returns an array with the following three elements:
561      *  - 'privatekey': The private key.
562      *  - 'publickey':  The public key.
563      *  - 'partialkey': A partially computed key (if the execution time exceeded $timeout).
564      *                  Will need to be passed back to Crypt_RSA::createKey() as the third parameter for further processing.
565      *
566      * @access public
567      * @param optional Integer $bits
568      * @param optional Integer $timeout
569      * @param optional Math_BigInteger $p
570      */
571     function createKey($bits = 1024, $timeout = false, $partial = array())
572     {
573         if (!defined('CRYPT_RSA_EXPONENT')) {
574             // http://en.wikipedia.org/wiki/65537_%28number%29
575             define('CRYPT_RSA_EXPONENT', '65537');
576         }
577         // per <http://cseweb.ucsd.edu/~hovav/dist/survey.pdf#page=5>, this number ought not result in primes smaller
578         // 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
579         // to 384 bits then you're going to get a 384 bit prime and a 640 bit prime (384 + 1024 % 384). at least if
580         // CRYPT_RSA_MODE is set to CRYPT_RSA_MODE_INTERNAL. if CRYPT_RSA_MODE is set to CRYPT_RSA_MODE_OPENSSL then
581         // CRYPT_RSA_SMALLEST_PRIME is ignored (ie. multi-prime RSA support is more intended as a way to speed up RSA key
582         // generation when there's a chance neither gmp nor OpenSSL are installed)
583         if (!defined('CRYPT_RSA_SMALLEST_PRIME')) {
584             define('CRYPT_RSA_SMALLEST_PRIME', 4096);
585         }
586
587         // OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum
588         if ( CRYPT_RSA_MODE == CRYPT_RSA_MODE_OPENSSL && $bits >= 384 && CRYPT_RSA_EXPONENT == 65537) {
589             $config = array();
590             if (isset($this->configFile)) {
591                 $config['config'] = $this->configFile;
592             }
593             $rsa = openssl_pkey_new(array('private_key_bits' => $bits) + $config);
594             openssl_pkey_export($rsa, $privatekey, null, $config);
595             $publickey = openssl_pkey_get_details($rsa);
596             $publickey = $publickey['key'];
597
598             $privatekey = call_user_func_array(array($this, '_convertPrivateKey'), array_values($this->_parseKey($privatekey, CRYPT_RSA_PRIVATE_FORMAT_PKCS1)));
599             $publickey = call_user_func_array(array($this, '_convertPublicKey'), array_values($this->_parseKey($publickey, CRYPT_RSA_PUBLIC_FORMAT_PKCS1)));
600
601             // clear the buffer of error strings stemming from a minimalistic openssl.cnf
602             while (openssl_error_string() !== false);
603
604             return array(
605                 'privatekey' => $privatekey,
606                 'publickey' => $publickey,
607                 'partialkey' => false
608             );
609         }
610
611         static $e;
612         if (!isset($e)) {
613             $e = new Math_BigInteger(CRYPT_RSA_EXPONENT);
614         }
615
616         extract($this->_generateMinMax($bits));
617         $absoluteMin = $min;
618         $temp = $bits >> 1; // divide by two to see how many bits P and Q would be
619         if ($temp > CRYPT_RSA_SMALLEST_PRIME) {
620             $num_primes = floor($bits / CRYPT_RSA_SMALLEST_PRIME);
621             $temp = CRYPT_RSA_SMALLEST_PRIME;
622         } else {
623             $num_primes = 2;
624         }
625         extract($this->_generateMinMax($temp + $bits % $temp));
626         $finalMax = $max;
627         extract($this->_generateMinMax($temp));
628
629         $generator = new Math_BigInteger();
630
631         $n = $this->one->copy();
632         if (!empty($partial)) {
633             extract(unserialize($partial));
634         } else {
635             $exponents = $coefficients = $primes = array();
636             $lcm = array(
637                 'top' => $this->one->copy(),
638                 'bottom' => false
639             );
640         }
641
642         $start = time();
643         $i0 = count($primes) + 1;
644
645         do {
646             for ($i = $i0; $i <= $num_primes; $i++) {
647                 if ($timeout !== false) {
648                     $timeout-= time() - $start;
649                     $start = time();
650                     if ($timeout <= 0) {
651                         return array(
652                             'privatekey' => '',
653                             'publickey'  => '',
654                             'partialkey' => serialize(array(
655                                 'primes' => $primes,
656                                 'coefficients' => $coefficients,
657                                 'lcm' => $lcm,
658                                 'exponents' => $exponents
659                             ))
660                         );
661                     }
662                 }
663
664                 if ($i == $num_primes) {
665                     list($min, $temp) = $absoluteMin->divide($n);
666                     if (!$temp->equals($this->zero)) {
667                         $min = $min->add($this->one); // ie. ceil()
668                     }
669                     $primes[$i] = $generator->randomPrime($min, $finalMax, $timeout);
670                 } else {
671                     $primes[$i] = $generator->randomPrime($min, $max, $timeout);
672                 }
673
674                 if ($primes[$i] === false) { // if we've reached the timeout
675                     if (count($primes) > 1) {
676                         $partialkey = '';
677                     } else {
678                         array_pop($primes);
679                         $partialkey = serialize(array(
680                             'primes' => $primes,
681                             'coefficients' => $coefficients,
682                             'lcm' => $lcm,
683                             'exponents' => $exponents
684                         ));
685                     }
686
687                     return array(
688                         'privatekey' => '',
689                         'publickey'  => '',
690                         'partialkey' => $partialkey
691                     );
692                 }
693
694                 // the first coefficient is calculated differently from the rest
695                 // ie. instead of being $primes[1]->modInverse($primes[2]), it's $primes[2]->modInverse($primes[1])
696                 if ($i > 2) {
697                     $coefficients[$i] = $n->modInverse($primes[$i]);
698                 }
699
700                 $n = $n->multiply($primes[$i]);
701
702                 $temp = $primes[$i]->subtract($this->one);
703
704                 // textbook RSA implementations use Euler's totient function instead of the least common multiple.
705                 // see http://en.wikipedia.org/wiki/Euler%27s_totient_function
706                 $lcm['top'] = $lcm['top']->multiply($temp);
707                 $lcm['bottom'] = $lcm['bottom'] === false ? $temp : $lcm['bottom']->gcd($temp);
708
709                 $exponents[$i] = $e->modInverse($temp);
710             }
711
712             list($temp) = $lcm['top']->divide($lcm['bottom']);
713             $gcd = $temp->gcd($e);
714             $i0 = 1;
715         } while (!$gcd->equals($this->one));
716
717         $d = $e->modInverse($temp);
718
719         $coefficients[2] = $primes[2]->modInverse($primes[1]);
720
721         // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.2>:
722         // RSAPrivateKey ::= SEQUENCE {
723         //     version           Version,
724         //     modulus           INTEGER,  -- n
725         //     publicExponent    INTEGER,  -- e
726         //     privateExponent   INTEGER,  -- d
727         //     prime1            INTEGER,  -- p
728         //     prime2            INTEGER,  -- q
729         //     exponent1         INTEGER,  -- d mod (p-1)
730         //     exponent2         INTEGER,  -- d mod (q-1)
731         //     coefficient       INTEGER,  -- (inverse of q) mod p
732         //     otherPrimeInfos   OtherPrimeInfos OPTIONAL
733         // }
734
735         return array(
736             'privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients),
737             'publickey'  => $this->_convertPublicKey($n, $e),
738             'partialkey' => false
739         );
740     }
741
742     /**
743      * Convert a private key to the appropriate format.
744      *
745      * @access private
746      * @see setPrivateKeyFormat()
747      * @param String $RSAPrivateKey
748      * @return String
749      */
750     function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients)
751     {
752         $signed = $this->privateKeyFormat != CRYPT_RSA_PRIVATE_FORMAT_XML;
753         $num_primes = count($primes);
754         $raw = array(
755             'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi
756             'modulus' => $n->toBytes($signed),
757             'publicExponent' => $e->toBytes($signed),
758             'privateExponent' => $d->toBytes($signed),
759             'prime1' => $primes[1]->toBytes($signed),
760             'prime2' => $primes[2]->toBytes($signed),
761             'exponent1' => $exponents[1]->toBytes($signed),
762             'exponent2' => $exponents[2]->toBytes($signed),
763             'coefficient' => $coefficients[2]->toBytes($signed)
764         );
765
766         // if the format in question does not support multi-prime rsa and multi-prime rsa was used,
767         // call _convertPublicKey() instead.
768         switch ($this->privateKeyFormat) {
769             case CRYPT_RSA_PRIVATE_FORMAT_XML:
770                 if ($num_primes != 2) {
771                     return false;
772                 }
773                 return "<RSAKeyValue>\r\n" .
774                        '  <Modulus>' . base64_encode($raw['modulus']) . "</Modulus>\r\n" .
775                        '  <Exponent>' . base64_encode($raw['publicExponent']) . "</Exponent>\r\n" .
776                        '  <P>' . base64_encode($raw['prime1']) . "</P>\r\n" .
777                        '  <Q>' . base64_encode($raw['prime2']) . "</Q>\r\n" .
778                        '  <DP>' . base64_encode($raw['exponent1']) . "</DP>\r\n" .
779                        '  <DQ>' . base64_encode($raw['exponent2']) . "</DQ>\r\n" .
780                        '  <InverseQ>' . base64_encode($raw['coefficient']) . "</InverseQ>\r\n" .
781                        '  <D>' . base64_encode($raw['privateExponent']) . "</D>\r\n" .
782                        '</RSAKeyValue>';
783                 break;
784             case CRYPT_RSA_PRIVATE_FORMAT_PUTTY:
785                 if ($num_primes != 2) {
786                     return false;
787                 }
788                 $key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: ";
789                 $encryption = (!empty($this->password) || is_string($this->password)) ? 'aes256-cbc' : 'none';
790                 $key.= $encryption;
791                 $key.= "\r\nComment: " . $this->comment . "\r\n";
792                 $public = pack('Na*Na*Na*',
793                     strlen('ssh-rsa'), 'ssh-rsa', strlen($raw['publicExponent']), $raw['publicExponent'], strlen($raw['modulus']), $raw['modulus']
794                 );
795                 $source = pack('Na*Na*Na*Na*',
796                     strlen('ssh-rsa'), 'ssh-rsa', strlen($encryption), $encryption,
797                     strlen($this->comment), $this->comment, strlen($public), $public
798                 );
799                 $public = base64_encode($public);
800                 $key.= "Public-Lines: " . ((strlen($public) + 63) >> 6) . "\r\n";
801                 $key.= chunk_split($public, 64);
802                 $private = pack('Na*Na*Na*Na*',
803                     strlen($raw['privateExponent']), $raw['privateExponent'], strlen($raw['prime1']), $raw['prime1'],
804                     strlen($raw['prime2']), $raw['prime2'], strlen($raw['coefficient']), $raw['coefficient']
805                 );
806                 if (empty($this->password) && !is_string($this->password)) {
807                     $source.= pack('Na*', strlen($private), $private);
808                     $hashkey = 'putty-private-key-file-mac-key';
809                 } else {
810                     $private.= crypt_random_string(16 - (strlen($private) & 15));
811                     $source.= pack('Na*', strlen($private), $private);
812                     if (!class_exists('Crypt_AES')) {
813                         include_once 'Crypt/AES.php';
814                     }
815                     $sequence = 0;
816                     $symkey = '';
817                     while (strlen($symkey) < 32) {
818                         $temp = pack('Na*', $sequence++, $this->password);
819                         $symkey.= pack('H*', sha1($temp));
820                     }
821                     $symkey = substr($symkey, 0, 32);
822                     $crypto = new Crypt_AES();
823
824                     $crypto->setKey($symkey);
825                     $crypto->disablePadding();
826                     $private = $crypto->encrypt($private);
827                     $hashkey = 'putty-private-key-file-mac-key' . $this->password;
828                 }
829
830                 $private = base64_encode($private);
831                 $key.= 'Private-Lines: ' . ((strlen($private) + 63) >> 6) . "\r\n";
832                 $key.= chunk_split($private, 64);
833                 if (!class_exists('Crypt_Hash')) {
834                     include_once 'Crypt/Hash.php';
835                 }
836                 $hash = new Crypt_Hash('sha1');
837                 $hash->setKey(pack('H*', sha1($hashkey)));
838                 $key.= 'Private-MAC: ' . bin2hex($hash->hash($source)) . "\r\n";
839
840                 return $key;
841             default: // eg. CRYPT_RSA_PRIVATE_FORMAT_PKCS1
842                 $components = array();
843                 foreach ($raw as $name => $value) {
844                     $components[$name] = pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($value)), $value);
845                 }
846
847                 $RSAPrivateKey = implode('', $components);
848
849                 if ($num_primes > 2) {
850                     $OtherPrimeInfos = '';
851                     for ($i = 3; $i <= $num_primes; $i++) {
852                         // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
853                         //
854                         // OtherPrimeInfo ::= SEQUENCE {
855                         //     prime             INTEGER,  -- ri
856                         //     exponent          INTEGER,  -- di
857                         //     coefficient       INTEGER   -- ti
858                         // }
859                         $OtherPrimeInfo = pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true));
860                         $OtherPrimeInfo.= pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true));
861                         $OtherPrimeInfo.= pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true));
862                         $OtherPrimeInfos.= pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo);
863                     }
864                     $RSAPrivateKey.= pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos);
865                 }
866
867                 $RSAPrivateKey = pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
868
869                 if ($this->privateKeyFormat == CRYPT_RSA_PRIVATE_FORMAT_PKCS8) {
870                     $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
871                     $RSAPrivateKey = pack('Ca*a*Ca*a*',
872                         CRYPT_RSA_ASN1_INTEGER, "\01\00", $rsaOID, 4, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey
873                     );
874                     $RSAPrivateKey = pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
875                     if (!empty($this->password) || is_string($this->password)) {
876                         $salt = crypt_random_string(8);
877                         $iterationCount = 2048;
878
879                         if (!class_exists('Crypt_DES')) {
880                             include_once 'Crypt/DES.php';
881                         }
882                         $crypto = new Crypt_DES();
883                         $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount);
884                         $RSAPrivateKey = $crypto->encrypt($RSAPrivateKey);
885
886                         $parameters = pack('Ca*a*Ca*N',
887                             CRYPT_RSA_ASN1_OCTETSTRING, $this->_encodeLength(strlen($salt)), $salt,
888                             CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(4), $iterationCount
889                         );
890                         $pbeWithMD5AndDES_CBC = "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03";
891
892                         $encryptionAlgorithm = pack('Ca*a*Ca*a*',
893                             CRYPT_RSA_ASN1_OBJECT, $this->_encodeLength(strlen($pbeWithMD5AndDES_CBC)), $pbeWithMD5AndDES_CBC,
894                             CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($parameters)), $parameters
895                         );
896
897                         $RSAPrivateKey = pack('Ca*a*Ca*a*',
898                             CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($encryptionAlgorithm)), $encryptionAlgorithm,
899                             CRYPT_RSA_ASN1_OCTETSTRING, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey
900                         );
901
902                         $RSAPrivateKey = pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
903
904                         $RSAPrivateKey = "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" .
905                                          chunk_split(base64_encode($RSAPrivateKey), 64) .
906                                          '-----END ENCRYPTED PRIVATE KEY-----';
907                     } else {
908                         $RSAPrivateKey = "-----BEGIN PRIVATE KEY-----\r\n" .
909                                          chunk_split(base64_encode($RSAPrivateKey), 64) .
910                                          '-----END PRIVATE KEY-----';
911                     }
912                     return $RSAPrivateKey;
913                 }
914
915                 if (!empty($this->password) || is_string($this->password)) {
916                     $iv = crypt_random_string(8);
917                     $symkey = pack('H*', md5($this->password . $iv)); // symkey is short for symmetric key
918                     $symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8);
919                     if (!class_exists('Crypt_TripleDES')) {
920                         include_once 'Crypt/TripleDES.php';
921                     }
922                     $des = new Crypt_TripleDES();
923                     $des->setKey($symkey);
924                     $des->setIV($iv);
925                     $iv = strtoupper(bin2hex($iv));
926                     $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
927                                      "Proc-Type: 4,ENCRYPTED\r\n" .
928                                      "DEK-Info: DES-EDE3-CBC,$iv\r\n" .
929                                      "\r\n" .
930                                      chunk_split(base64_encode($des->encrypt($RSAPrivateKey)), 64) .
931                                      '-----END RSA PRIVATE KEY-----';
932                 } else {
933                     $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
934                                      chunk_split(base64_encode($RSAPrivateKey), 64) .
935                                      '-----END RSA PRIVATE KEY-----';
936                 }
937
938                 return $RSAPrivateKey;
939         }
940     }
941
942     /**
943      * Convert a public key to the appropriate format
944      *
945      * @access private
946      * @see setPublicKeyFormat()
947      * @param String $RSAPrivateKey
948      * @return String
949      */
950     function _convertPublicKey($n, $e)
951     {
952         $signed = $this->publicKeyFormat != CRYPT_RSA_PUBLIC_FORMAT_XML;
953
954         $modulus = $n->toBytes($signed);
955         $publicExponent = $e->toBytes($signed);
956
957         switch ($this->publicKeyFormat) {
958             case CRYPT_RSA_PUBLIC_FORMAT_RAW:
959                 return array('e' => $e->copy(), 'n' => $n->copy());
960             case CRYPT_RSA_PUBLIC_FORMAT_XML:
961                 return "<RSAKeyValue>\r\n" .
962                        '  <Modulus>' . base64_encode($modulus) . "</Modulus>\r\n" .
963                        '  <Exponent>' . base64_encode($publicExponent) . "</Exponent>\r\n" .
964                        '</RSAKeyValue>';
965                 break;
966             case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
967                 // from <http://tools.ietf.org/html/rfc4253#page-15>:
968                 // string    "ssh-rsa"
969                 // mpint     e
970                 // mpint     n
971                 $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus);
972                 $RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment;
973
974                 return $RSAPublicKey;
975             default: // eg. CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW or CRYPT_RSA_PUBLIC_FORMAT_PKCS1
976                 // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.1>:
977                 // RSAPublicKey ::= SEQUENCE {
978                 //     modulus           INTEGER,  -- n
979                 //     publicExponent    INTEGER   -- e
980                 // }
981                 $components = array(
982                     'modulus' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus),
983                     'publicExponent' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($publicExponent)), $publicExponent)
984                 );
985
986                 $RSAPublicKey = pack('Ca*a*a*',
987                     CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])),
988                     $components['modulus'], $components['publicExponent']
989                 );
990
991                 if ($this->publicKeyFormat == CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW) {
992                     $RSAPublicKey = "-----BEGIN RSA PUBLIC KEY-----\r\n" .
993                                     chunk_split(base64_encode($RSAPublicKey), 64) .
994                                     '-----END RSA PUBLIC KEY-----';
995                 } else {
996                     // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
997                     $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
998                     $RSAPublicKey = chr(0) . $RSAPublicKey;
999                     $RSAPublicKey = chr(3) . $this->_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey;
1000
1001                     $RSAPublicKey = pack('Ca*a*',
1002                         CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($rsaOID . $RSAPublicKey)), $rsaOID . $RSAPublicKey
1003                     );
1004
1005                     $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
1006                                      chunk_split(base64_encode($RSAPublicKey), 64) .
1007                                      '-----END PUBLIC KEY-----';
1008                 }
1009
1010                 return $RSAPublicKey;
1011         }
1012     }
1013
1014     /**
1015      * Break a public or private key down into its constituant components
1016      *
1017      * @access private
1018      * @see _convertPublicKey()
1019      * @see _convertPrivateKey()
1020      * @param String $key
1021      * @param Integer $type
1022      * @return Array
1023      */
1024     function _parseKey($key, $type)
1025     {
1026         if ($type != CRYPT_RSA_PUBLIC_FORMAT_RAW && !is_string($key)) {
1027             return false;
1028         }
1029
1030         switch ($type) {
1031             case CRYPT_RSA_PUBLIC_FORMAT_RAW:
1032                 if (!is_array($key)) {
1033                     return false;
1034                 }
1035                 $components = array();
1036                 switch (true) {
1037                     case isset($key['e']):
1038                         $components['publicExponent'] = $key['e']->copy();
1039                         break;
1040                     case isset($key['exponent']):
1041                         $components['publicExponent'] = $key['exponent']->copy();
1042                         break;
1043                     case isset($key['publicExponent']):
1044                         $components['publicExponent'] = $key['publicExponent']->copy();
1045                         break;
1046                     case isset($key[0]):
1047                         $components['publicExponent'] = $key[0]->copy();
1048                 }
1049                 switch (true) {
1050                     case isset($key['n']):
1051                         $components['modulus'] = $key['n']->copy();
1052                         break;
1053                     case isset($key['modulo']):
1054                         $components['modulus'] = $key['modulo']->copy();
1055                         break;
1056                     case isset($key['modulus']):
1057                         $components['modulus'] = $key['modulus']->copy();
1058                         break;
1059                     case isset($key[1]):
1060                         $components['modulus'] = $key[1]->copy();
1061                 }
1062                 return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false;
1063             case CRYPT_RSA_PRIVATE_FORMAT_PKCS1:
1064             case CRYPT_RSA_PRIVATE_FORMAT_PKCS8:
1065             case CRYPT_RSA_PUBLIC_FORMAT_PKCS1:
1066                 /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
1067                    "outside the scope" of PKCS#1.  PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
1068                    protect private keys, however, that's not what OpenSSL* does.  OpenSSL protects private keys by adding
1069                    two new "fields" to the key - DEK-Info and Proc-Type.  These fields are discussed here:
1070
1071                    http://tools.ietf.org/html/rfc1421#section-4.6.1.1
1072                    http://tools.ietf.org/html/rfc1421#section-4.6.1.3
1073
1074                    DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell.
1075                    DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation
1076                    function.  As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's
1077                    own implementation.  ie. the implementation *is* the standard and any bugs that may exist in that
1078                    implementation are part of the standard, as well.
1079
1080                    * OpenSSL is the de facto standard.  It's utilized by OpenSSH and other projects */
1081                 if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
1082                     $iv = pack('H*', trim($matches[2]));
1083                     $symkey = pack('H*', md5($this->password . substr($iv, 0, 8))); // symkey is short for symmetric key
1084                     $symkey.= pack('H*', md5($symkey . $this->password . substr($iv, 0, 8)));
1085                     // remove the Proc-Type / DEK-Info sections as they're no longer needed
1086                     $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key);
1087                     $ciphertext = $this->_extractBER($key);
1088                     if ($ciphertext === false) {
1089                         $ciphertext = $key;
1090                     }
1091                     switch ($matches[1]) {
1092                         case 'AES-256-CBC':
1093                             if (!class_exists('Crypt_AES')) {
1094                                 include_once 'Crypt/AES.php';
1095                             }
1096                             $crypto = new Crypt_AES();
1097                             break;
1098                         case 'AES-128-CBC':
1099                             if (!class_exists('Crypt_AES')) {
1100                                 include_once 'Crypt/AES.php';
1101                             }
1102                             $symkey = substr($symkey, 0, 16);
1103                             $crypto = new Crypt_AES();
1104                             break;
1105                         case 'DES-EDE3-CFB':
1106                             if (!class_exists('Crypt_TripleDES')) {
1107                                 include_once 'Crypt/TripleDES.php';
1108                             }
1109                             $crypto = new Crypt_TripleDES(CRYPT_DES_MODE_CFB);
1110                             break;
1111                         case 'DES-EDE3-CBC':
1112                             if (!class_exists('Crypt_TripleDES')) {
1113                                 include_once 'Crypt/TripleDES.php';
1114                             }
1115                             $symkey = substr($symkey, 0, 24);
1116                             $crypto = new Crypt_TripleDES();
1117                             break;
1118                         case 'DES-CBC':
1119                             if (!class_exists('Crypt_DES')) {
1120                                 include_once 'Crypt/DES.php';
1121                             }
1122                             $crypto = new Crypt_DES();
1123                             break;
1124                         default:
1125                             return false;
1126                     }
1127                     $crypto->setKey($symkey);
1128                     $crypto->setIV($iv);
1129                     $decoded = $crypto->decrypt($ciphertext);
1130                 } else {
1131                     $decoded = $this->_extractBER($key);
1132                 }
1133
1134                 if ($decoded !== false) {
1135                     $key = $decoded;
1136                 }
1137
1138                 $components = array();
1139
1140                 if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
1141                     return false;
1142                 }
1143                 if ($this->_decodeLength($key) != strlen($key)) {
1144                     return false;
1145                 }
1146
1147                 $tag = ord($this->_string_shift($key));
1148                 /* intended for keys for which OpenSSL's asn1parse returns the following:
1149
1150                     0:d=0  hl=4 l= 631 cons: SEQUENCE
1151                     4:d=1  hl=2 l=   1 prim:  INTEGER           :00
1152                     7:d=1  hl=2 l=  13 cons:  SEQUENCE
1153                     9:d=2  hl=2 l=   9 prim:   OBJECT            :rsaEncryption
1154                    20:d=2  hl=2 l=   0 prim:   NULL
1155                    22:d=1  hl=4 l= 609 prim:  OCTET STRING
1156
1157                    ie. PKCS8 keys*/
1158
1159                 if ($tag == CRYPT_RSA_ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") {
1160                     $this->_string_shift($key, 3);
1161                     $tag = CRYPT_RSA_ASN1_SEQUENCE;
1162                 }
1163
1164                 if ($tag == CRYPT_RSA_ASN1_SEQUENCE) {
1165                     $temp = $this->_string_shift($key, $this->_decodeLength($key));
1166                     if (ord($this->_string_shift($temp)) != CRYPT_RSA_ASN1_OBJECT) {
1167                         return false;
1168                     }
1169                     $length = $this->_decodeLength($temp);
1170                     switch ($this->_string_shift($temp, $length)) {
1171                         case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption
1172                             break;
1173                         case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC
1174                             /*
1175                                PBEParameter ::= SEQUENCE {
1176                                    salt OCTET STRING (SIZE(8)),
1177                                    iterationCount INTEGER }
1178                             */
1179                             if (ord($this->_string_shift($temp)) != CRYPT_RSA_ASN1_SEQUENCE) {
1180                                 return false;
1181                             }
1182                             if ($this->_decodeLength($temp) != strlen($temp)) {
1183                                 return false;
1184                             }
1185                             $this->_string_shift($temp); // assume it's an octet string
1186                             $salt = $this->_string_shift($temp, $this->_decodeLength($temp));
1187                             if (ord($this->_string_shift($temp)) != CRYPT_RSA_ASN1_INTEGER) {
1188                                 return false;
1189                             }
1190                             $this->_decodeLength($temp);
1191                             list(, $iterationCount) = unpack('N', str_pad($temp, 4, chr(0), STR_PAD_LEFT));
1192                             $this->_string_shift($key); // assume it's an octet string
1193                             $length = $this->_decodeLength($key);
1194                             if (strlen($key) != $length) {
1195                                 return false;
1196                             }
1197
1198                             if (!class_exists('Crypt_DES')) {
1199                                 include_once 'Crypt/DES.php';
1200                             }
1201                             $crypto = new Crypt_DES();
1202                             $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount);
1203                             $key = $crypto->decrypt($key);
1204                             if ($key === false) {
1205                                 return false;
1206                             }
1207                             return $this->_parseKey($key, CRYPT_RSA_PRIVATE_FORMAT_PKCS1);
1208                         default:
1209                             return false;
1210                     }
1211                     /* intended for keys for which OpenSSL's asn1parse returns the following:
1212
1213                         0:d=0  hl=4 l= 290 cons: SEQUENCE
1214                         4:d=1  hl=2 l=  13 cons:  SEQUENCE
1215                         6:d=2  hl=2 l=   9 prim:   OBJECT            :rsaEncryption
1216                        17:d=2  hl=2 l=   0 prim:   NULL
1217                        19:d=1  hl=4 l= 271 prim:  BIT STRING */
1218                     $tag = ord($this->_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag
1219                     $this->_decodeLength($key); // skip over the BIT STRING / OCTET STRING length
1220                     // "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of
1221                     //  unused bits in the final subsequent octet. The number shall be in the range zero to seven."
1222                     //  -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2)
1223                     if ($tag == CRYPT_RSA_ASN1_BITSTRING) {
1224                         $this->_string_shift($key);
1225                     }
1226                     if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
1227                         return false;
1228                     }
1229                     if ($this->_decodeLength($key) != strlen($key)) {
1230                         return false;
1231                     }
1232                     $tag = ord($this->_string_shift($key));
1233                 }
1234                 if ($tag != CRYPT_RSA_ASN1_INTEGER) {
1235                     return false;
1236                 }
1237
1238                 $length = $this->_decodeLength($key);
1239                 $temp = $this->_string_shift($key, $length);
1240                 if (strlen($temp) != 1 || ord($temp) > 2) {
1241                     $components['modulus'] = new Math_BigInteger($temp, 256);
1242                     $this->_string_shift($key); // skip over CRYPT_RSA_ASN1_INTEGER
1243                     $length = $this->_decodeLength($key);
1244                     $components[$type == CRYPT_RSA_PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1245
1246                     return $components;
1247                 }
1248                 if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_INTEGER) {
1249                     return false;
1250                 }
1251                 $length = $this->_decodeLength($key);
1252                 $components['modulus'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1253                 $this->_string_shift($key);
1254                 $length = $this->_decodeLength($key);
1255                 $components['publicExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1256                 $this->_string_shift($key);
1257                 $length = $this->_decodeLength($key);
1258                 $components['privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1259                 $this->_string_shift($key);
1260                 $length = $this->_decodeLength($key);
1261                 $components['primes'] = array(1 => new Math_BigInteger($this->_string_shift($key, $length), 256));
1262                 $this->_string_shift($key);
1263                 $length = $this->_decodeLength($key);
1264                 $components['primes'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1265                 $this->_string_shift($key);
1266                 $length = $this->_decodeLength($key);
1267                 $components['exponents'] = array(1 => new Math_BigInteger($this->_string_shift($key, $length), 256));
1268                 $this->_string_shift($key);
1269                 $length = $this->_decodeLength($key);
1270                 $components['exponents'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1271                 $this->_string_shift($key);
1272                 $length = $this->_decodeLength($key);
1273                 $components['coefficients'] = array(2 => new Math_BigInteger($this->_string_shift($key, $length), 256));
1274
1275                 if (!empty($key)) {
1276                     if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
1277                         return false;
1278                     }
1279                     $this->_decodeLength($key);
1280                     while (!empty($key)) {
1281                         if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
1282                             return false;
1283                         }
1284                         $this->_decodeLength($key);
1285                         $key = substr($key, 1);
1286                         $length = $this->_decodeLength($key);
1287                         $components['primes'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1288                         $this->_string_shift($key);
1289                         $length = $this->_decodeLength($key);
1290                         $components['exponents'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1291                         $this->_string_shift($key);
1292                         $length = $this->_decodeLength($key);
1293                         $components['coefficients'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1294                     }
1295                 }
1296
1297                 return $components;
1298             case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
1299                 $parts = explode(' ', $key, 3);
1300
1301                 $key = isset($parts[1]) ? base64_decode($parts[1]) : false;
1302                 if ($key === false) {
1303                     return false;
1304                 }
1305
1306                 $comment = isset($parts[2]) ? $parts[2] : false;
1307
1308                 $cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa";
1309
1310                 if (strlen($key) <= 4) {
1311                     return false;
1312                 }
1313                 extract(unpack('Nlength', $this->_string_shift($key, 4)));
1314                 $publicExponent = new Math_BigInteger($this->_string_shift($key, $length), -256);
1315                 if (strlen($key) <= 4) {
1316                     return false;
1317                 }
1318                 extract(unpack('Nlength', $this->_string_shift($key, 4)));
1319                 $modulus = new Math_BigInteger($this->_string_shift($key, $length), -256);
1320
1321                 if ($cleanup && strlen($key)) {
1322                     if (strlen($key) <= 4) {
1323                         return false;
1324                     }
1325                     extract(unpack('Nlength', $this->_string_shift($key, 4)));
1326                     $realModulus = new Math_BigInteger($this->_string_shift($key, $length), -256);
1327                     return strlen($key) ? false : array(
1328                         'modulus' => $realModulus,
1329                         'publicExponent' => $modulus,
1330                         'comment' => $comment
1331                     );
1332                 } else {
1333                     return strlen($key) ? false : array(
1334                         'modulus' => $modulus,
1335                         'publicExponent' => $publicExponent,
1336                         'comment' => $comment
1337                     );
1338                 }
1339             // http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue
1340             // http://en.wikipedia.org/wiki/XML_Signature
1341             case CRYPT_RSA_PRIVATE_FORMAT_XML:
1342             case CRYPT_RSA_PUBLIC_FORMAT_XML:
1343                 $this->components = array();
1344
1345                 $xml = xml_parser_create('UTF-8');
1346                 xml_set_object($xml, $this);
1347                 xml_set_element_handler($xml, '_start_element_handler', '_stop_element_handler');
1348                 xml_set_character_data_handler($xml, '_data_handler');
1349                 // add <xml></xml> to account for "dangling" tags like <BitStrength>...</BitStrength> that are sometimes added
1350                 if (!xml_parse($xml, '<xml>' . $key . '</xml>')) {
1351                     return false;
1352                 }
1353
1354                 return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false;
1355             // from PuTTY's SSHPUBK.C
1356             case CRYPT_RSA_PRIVATE_FORMAT_PUTTY:
1357                 $components = array();
1358                 $key = preg_split('#\r\n|\r|\n#', $key);
1359                 $type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0]));
1360                 if ($type != 'ssh-rsa') {
1361                     return false;
1362                 }
1363                 $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1]));
1364                 $comment = trim(preg_replace('#Comment: (.+)#', '$1', $key[2]));
1365
1366                 $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3]));
1367                 $public = base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength))));
1368                 $public = substr($public, 11);
1369                 extract(unpack('Nlength', $this->_string_shift($public, 4)));
1370                 $components['publicExponent'] = new Math_BigInteger($this->_string_shift($public, $length), -256);
1371                 extract(unpack('Nlength', $this->_string_shift($public, 4)));
1372                 $components['modulus'] = new Math_BigInteger($this->_string_shift($public, $length), -256);
1373
1374                 $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$publicLength + 4]));
1375                 $private = base64_decode(implode('', array_map('trim', array_slice($key, $publicLength + 5, $privateLength))));
1376
1377                 switch ($encryption) {
1378                     case 'aes256-cbc':
1379                         if (!class_exists('Crypt_AES')) {
1380                             include_once 'Crypt/AES.php';
1381                         }
1382                         $symkey = '';
1383                         $sequence = 0;
1384                         while (strlen($symkey) < 32) {
1385                             $temp = pack('Na*', $sequence++, $this->password);
1386                             $symkey.= pack('H*', sha1($temp));
1387                         }
1388                         $symkey = substr($symkey, 0, 32);
1389                         $crypto = new Crypt_AES();
1390                 }
1391
1392                 if ($encryption != 'none') {
1393                     $crypto->setKey($symkey);
1394                     $crypto->disablePadding();
1395                     $private = $crypto->decrypt($private);
1396                     if ($private === false) {
1397                         return false;
1398                     }
1399                 }
1400
1401                 extract(unpack('Nlength', $this->_string_shift($private, 4)));
1402                 if (strlen($private) < $length) {
1403                     return false;
1404                 }
1405                 $components['privateExponent'] = new Math_BigInteger($this->_string_shift($private, $length), -256);
1406                 extract(unpack('Nlength', $this->_string_shift($private, 4)));
1407                 if (strlen($private) < $length) {
1408                     return false;
1409                 }
1410                 $components['primes'] = array(1 => new Math_BigInteger($this->_string_shift($private, $length), -256));
1411                 extract(unpack('Nlength', $this->_string_shift($private, 4)));
1412                 if (strlen($private) < $length) {
1413                     return false;
1414                 }
1415                 $components['primes'][] = new Math_BigInteger($this->_string_shift($private, $length), -256);
1416
1417                 $temp = $components['primes'][1]->subtract($this->one);
1418                 $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp));
1419                 $temp = $components['primes'][2]->subtract($this->one);
1420                 $components['exponents'][] = $components['publicExponent']->modInverse($temp);
1421
1422                 extract(unpack('Nlength', $this->_string_shift($private, 4)));
1423                 if (strlen($private) < $length) {
1424                     return false;
1425                 }
1426                 $components['coefficients'] = array(2 => new Math_BigInteger($this->_string_shift($private, $length), -256));
1427
1428                 return $components;
1429         }
1430     }
1431
1432     /**
1433      * Returns the key size
1434      *
1435      * More specifically, this returns the size of the modulo in bits.
1436      *
1437      * @access public
1438      * @return Integer
1439      */
1440     function getSize()
1441     {
1442         return !isset($this->modulus) ? 0 : strlen($this->modulus->toBits());
1443     }
1444
1445     /**
1446      * Start Element Handler
1447      *
1448      * Called by xml_set_element_handler()
1449      *
1450      * @access private
1451      * @param Resource $parser
1452      * @param String $name
1453      * @param Array $attribs
1454      */
1455     function _start_element_handler($parser, $name, $attribs)
1456     {
1457         //$name = strtoupper($name);
1458         switch ($name) {
1459             case 'MODULUS':
1460                 $this->current = &$this->components['modulus'];
1461                 break;
1462             case 'EXPONENT':
1463                 $this->current = &$this->components['publicExponent'];
1464                 break;
1465             case 'P':
1466                 $this->current = &$this->components['primes'][1];
1467                 break;
1468             case 'Q':
1469                 $this->current = &$this->components['primes'][2];
1470                 break;
1471             case 'DP':
1472                 $this->current = &$this->components['exponents'][1];
1473                 break;
1474             case 'DQ':
1475                 $this->current = &$this->components['exponents'][2];
1476                 break;
1477             case 'INVERSEQ':
1478                 $this->current = &$this->components['coefficients'][2];
1479                 break;
1480             case 'D':
1481                 $this->current = &$this->components['privateExponent'];
1482         }
1483         $this->current = '';
1484     }
1485
1486     /**
1487      * Stop Element Handler
1488      *
1489      * Called by xml_set_element_handler()
1490      *
1491      * @access private
1492      * @param Resource $parser
1493      * @param String $name
1494      */
1495     function _stop_element_handler($parser, $name)
1496     {
1497         if (isset($this->current)) {
1498             $this->current = new Math_BigInteger(base64_decode($this->current), 256);
1499             unset($this->current);
1500         }
1501     }
1502
1503     /**
1504      * Data Handler
1505      *
1506      * Called by xml_set_character_data_handler()
1507      *
1508      * @access private
1509      * @param Resource $parser
1510      * @param String $data
1511      */
1512     function _data_handler($parser, $data)
1513     {
1514         if (!isset($this->current) || is_object($this->current)) {
1515             return;
1516         }
1517         $this->current.= trim($data);
1518     }
1519
1520     /**
1521      * Loads a public or private key
1522      *
1523      * Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed)
1524      *
1525      * @access public
1526      * @param String $key
1527      * @param Integer $type optional
1528      */
1529     function loadKey($key, $type = false)
1530     {
1531         if (is_object($key) && strtolower(get_class($key)) == 'crypt_rsa') {
1532             $this->privateKeyFormat = $key->privateKeyFormat;
1533             $this->publicKeyFormat = $key->publicKeyFormat;
1534             $this->k = $key->k;
1535             $this->hLen = $key->hLen;
1536             $this->sLen = $key->sLen;
1537             $this->mgfHLen = $key->mgfHLen;
1538             $this->encryptionMode = $key->encryptionMode;
1539             $this->signatureMode = $key->signatureMode;
1540             $this->password = $key->password;
1541             $this->configFile = $key->configFile;
1542             $this->comment = $key->comment;
1543
1544             if (is_object($key->hash)) {
1545                 $this->hash = new Crypt_Hash($key->hash->getHash());
1546             }
1547             if (is_object($key->mgfHash)) {
1548                 $this->mgfHash = new Crypt_Hash($key->mgfHash->getHash());
1549             }
1550
1551             if (is_object($key->modulus)) {
1552                 $this->modulus = $key->modulus->copy();
1553             }
1554             if (is_object($key->exponent)) {
1555                 $this->exponent = $key->exponent->copy();
1556             }
1557             if (is_object($key->publicExponent)) {
1558                 $this->publicExponent = $key->publicExponent->copy();
1559             }
1560
1561             $this->primes = array();
1562             $this->exponents = array();
1563             $this->coefficients = array();
1564
1565             foreach ($this->primes as $prime) {
1566                 $this->primes[] = $prime->copy();
1567             }
1568             foreach ($this->exponents as $exponent) {
1569                 $this->exponents[] = $exponent->copy();
1570             }
1571             foreach ($this->coefficients as $coefficient) {
1572                 $this->coefficients[] = $coefficient->copy();
1573             }
1574
1575             return true;
1576         }
1577
1578         if ($type === false) {
1579             $types = array(
1580                 CRYPT_RSA_PUBLIC_FORMAT_RAW,
1581                 CRYPT_RSA_PRIVATE_FORMAT_PKCS1,
1582                 CRYPT_RSA_PRIVATE_FORMAT_XML,
1583                 CRYPT_RSA_PRIVATE_FORMAT_PUTTY,
1584                 CRYPT_RSA_PUBLIC_FORMAT_OPENSSH
1585             );
1586             foreach ($types as $type) {
1587                 $components = $this->_parseKey($key, $type);
1588                 if ($components !== false) {
1589                     break;
1590                 }
1591             }
1592
1593         } else {
1594             $components = $this->_parseKey($key, $type);
1595         }
1596
1597         if ($components === false) {
1598             return false;
1599         }
1600
1601         if (isset($components['comment']) && $components['comment'] !== false) {
1602             $this->comment = $components['comment'];
1603         }
1604         $this->modulus = $components['modulus'];
1605         $this->k = strlen($this->modulus->toBytes());
1606         $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent'];
1607         if (isset($components['primes'])) {
1608             $this->primes = $components['primes'];
1609             $this->exponents = $components['exponents'];
1610             $this->coefficients = $components['coefficients'];
1611             $this->publicExponent = $components['publicExponent'];
1612         } else {
1613             $this->primes = array();
1614             $this->exponents = array();
1615             $this->coefficients = array();
1616             $this->publicExponent = false;
1617         }
1618
1619         switch ($type) {
1620             case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
1621             case CRYPT_RSA_PUBLIC_FORMAT_RAW:
1622                 $this->setPublicKey();
1623                 break;
1624             case CRYPT_RSA_PRIVATE_FORMAT_PKCS1:
1625                 switch (true) {
1626                     case strpos($key, '-BEGIN PUBLIC KEY-') !== false:
1627                     case strpos($key, '-BEGIN RSA PUBLIC KEY-') !== false:
1628                         $this->setPublicKey();
1629                 }
1630         }
1631
1632         return true;
1633     }
1634
1635     /**
1636      * Sets the password
1637      *
1638      * Private keys can be encrypted with a password.  To unset the password, pass in the empty string or false.
1639      * Or rather, pass in $password such that empty($password) && !is_string($password) is true.
1640      *
1641      * @see createKey()
1642      * @see loadKey()
1643      * @access public
1644      * @param String $password
1645      */
1646     function setPassword($password = false)
1647     {
1648         $this->password = $password;
1649     }
1650
1651     /**
1652      * Defines the public key
1653      *
1654      * Some private key formats define the public exponent and some don't.  Those that don't define it are problematic when
1655      * used in certain contexts.  For example, in SSH-2, RSA authentication works by sending the public key along with a
1656      * message signed by the private key to the server.  The SSH-2 server looks the public key up in an index of public keys
1657      * and if it's present then proceeds to verify the signature.  Problem is, if your private key doesn't include the public
1658      * exponent this won't work unless you manually add the public exponent. phpseclib tries to guess if the key being used
1659      * is the public key but in the event that it guesses incorrectly you might still want to explicitly set the key as being
1660      * public.
1661      *
1662      * Do note that when a new key is loaded the index will be cleared.
1663      *
1664      * Returns true on success, false on failure
1665      *
1666      * @see getPublicKey()
1667      * @access public
1668      * @param String $key optional
1669      * @param Integer $type optional
1670      * @return Boolean
1671      */
1672     function setPublicKey($key = false, $type = false)
1673     {
1674         // if a public key has already been loaded return false
1675         if (!empty($this->publicExponent)) {
1676             return false;
1677         }
1678
1679         if ($key === false && !empty($this->modulus)) {
1680             $this->publicExponent = $this->exponent;
1681             return true;
1682         }
1683
1684         if ($type === false) {
1685             $types = array(
1686                 CRYPT_RSA_PUBLIC_FORMAT_RAW,
1687                 CRYPT_RSA_PUBLIC_FORMAT_PKCS1,
1688                 CRYPT_RSA_PUBLIC_FORMAT_XML,
1689                 CRYPT_RSA_PUBLIC_FORMAT_OPENSSH
1690             );
1691             foreach ($types as $type) {
1692                 $components = $this->_parseKey($key, $type);
1693                 if ($components !== false) {
1694                     break;
1695                 }
1696             }
1697         } else {
1698             $components = $this->_parseKey($key, $type);
1699         }
1700
1701         if ($components === false) {
1702             return false;
1703         }
1704
1705         if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) {
1706             $this->modulus = $components['modulus'];
1707             $this->exponent = $this->publicExponent = $components['publicExponent'];
1708             return true;
1709         }
1710
1711         $this->publicExponent = $components['publicExponent'];
1712
1713         return true;
1714     }
1715
1716     /**
1717      * Defines the private key
1718      *
1719      * If phpseclib guessed a private key was a public key and loaded it as such it might be desirable to force
1720      * phpseclib to treat the key as a private key. This function will do that.
1721      *
1722      * Do note that when a new key is loaded the index will be cleared.
1723      *
1724      * Returns true on success, false on failure
1725      *
1726      * @see getPublicKey()
1727      * @access public
1728      * @param String $key optional
1729      * @param Integer $type optional
1730      * @return Boolean
1731      */
1732     function setPrivateKey($key = false, $type = false)
1733     {
1734         if ($key === false && !empty($this->publicExponent)) {
1735             unset($this->publicExponent);
1736             return true;
1737         }
1738
1739         $rsa = new Crypt_RSA();
1740         if (!$rsa->loadKey($key, $type)) {
1741             return false;
1742         }
1743         unset($rsa->publicExponent);
1744
1745         // don't overwrite the old key if the new key is invalid
1746         $this->loadKey($rsa);
1747         return true;
1748     }
1749
1750     /**
1751      * Returns the public key
1752      *
1753      * The public key is only returned under two circumstances - if the private key had the public key embedded within it
1754      * or if the public key was set via setPublicKey().  If the currently loaded key is supposed to be the public key this
1755      * function won't return it since this library, for the most part, doesn't distinguish between public and private keys.
1756      *
1757      * @see getPublicKey()
1758      * @access public
1759      * @param String $key
1760      * @param Integer $type optional
1761      */
1762     function getPublicKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS8)
1763     {
1764         if (empty($this->modulus) || empty($this->publicExponent)) {
1765             return false;
1766         }
1767
1768         $oldFormat = $this->publicKeyFormat;
1769         $this->publicKeyFormat = $type;
1770         $temp = $this->_convertPublicKey($this->modulus, $this->publicExponent);
1771         $this->publicKeyFormat = $oldFormat;
1772         return $temp;
1773     }
1774
1775     /**
1776      * Returns the private key
1777      *
1778      * The private key is only returned if the currently loaded key contains the constituent prime numbers.
1779      *
1780      * @see getPublicKey()
1781      * @access public
1782      * @param String $key
1783      * @param Integer $type optional
1784      */
1785     function getPrivateKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS1)
1786     {
1787         if (empty($this->primes)) {
1788             return false;
1789         }
1790
1791         $oldFormat = $this->privateKeyFormat;
1792         $this->privateKeyFormat = $type;
1793         $temp = $this->_convertPrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients);
1794         $this->privateKeyFormat = $oldFormat;
1795         return $temp;
1796     }
1797
1798     /**
1799      * Returns a minimalistic private key
1800      *
1801      * Returns the private key without the prime number constituants.  Structurally identical to a public key that
1802      * hasn't been set as the public key
1803      *
1804      * @see getPrivateKey()
1805      * @access private
1806      * @param String $key
1807      * @param Integer $type optional
1808      */
1809     function _getPrivatePublicKey($mode = CRYPT_RSA_PUBLIC_FORMAT_PKCS8)
1810     {
1811         if (empty($this->modulus) || empty($this->exponent)) {
1812             return false;
1813         }
1814
1815         $oldFormat = $this->publicKeyFormat;
1816         $this->publicKeyFormat = $mode;
1817         $temp = $this->_convertPublicKey($this->modulus, $this->exponent);
1818         $this->publicKeyFormat = $oldFormat;
1819         return $temp;
1820     }
1821
1822     /**
1823      *  __toString() magic method
1824      *
1825      * @access public
1826      */
1827     function __toString()
1828     {
1829         $key = $this->getPrivateKey($this->privateKeyFormat);
1830         if ($key !== false) {
1831             return $key;
1832         }
1833         $key = $this->_getPrivatePublicKey($this->publicKeyFormat);
1834         return $key !== false ? $key : '';
1835     }
1836
1837     /**
1838      *  __clone() magic method
1839      *
1840      * @access public
1841      */
1842     function __clone()
1843     {
1844         $key = new Crypt_RSA();
1845         $key->loadKey($this);
1846         return $key;
1847     }
1848
1849     /**
1850      * Generates the smallest and largest numbers requiring $bits bits
1851      *
1852      * @access private
1853      * @param Integer $bits
1854      * @return Array
1855      */
1856     function _generateMinMax($bits)
1857     {
1858         $bytes = $bits >> 3;
1859         $min = str_repeat(chr(0), $bytes);
1860         $max = str_repeat(chr(0xFF), $bytes);
1861         $msb = $bits & 7;
1862         if ($msb) {
1863             $min = chr(1 << ($msb - 1)) . $min;
1864             $max = chr((1 << $msb) - 1) . $max;
1865         } else {
1866             $min[0] = chr(0x80);
1867         }
1868
1869         return array(
1870             'min' => new Math_BigInteger($min, 256),
1871             'max' => new Math_BigInteger($max, 256)
1872         );
1873     }
1874
1875     /**
1876      * DER-decode the length
1877      *
1878      * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4.  See
1879      * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
1880      *
1881      * @access private
1882      * @param String $string
1883      * @return Integer
1884      */
1885     function _decodeLength(&$string)
1886     {
1887         $length = ord($this->_string_shift($string));
1888         if ( $length & 0x80 ) { // definite length, long form
1889             $length&= 0x7F;
1890             $temp = $this->_string_shift($string, $length);
1891             list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4));
1892         }
1893         return $length;
1894     }
1895
1896     /**
1897      * DER-encode the length
1898      *
1899      * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4.  See
1900      * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
1901      *
1902      * @access private
1903      * @param Integer $length
1904      * @return String
1905      */
1906     function _encodeLength($length)
1907     {
1908         if ($length <= 0x7F) {
1909             return chr($length);
1910         }
1911
1912         $temp = ltrim(pack('N', $length), chr(0));
1913         return pack('Ca*', 0x80 | strlen($temp), $temp);
1914     }
1915
1916     /**
1917      * String Shift
1918      *
1919      * Inspired by array_shift
1920      *
1921      * @param String $string
1922      * @param optional Integer $index
1923      * @return String
1924      * @access private
1925      */
1926     function _string_shift(&$string, $index = 1)
1927     {
1928         $substr = substr($string, 0, $index);
1929         $string = substr($string, $index);
1930         return $substr;
1931     }
1932
1933     /**
1934      * Determines the private key format
1935      *
1936      * @see createKey()
1937      * @access public
1938      * @param Integer $format
1939      */
1940     function setPrivateKeyFormat($format)
1941     {
1942         $this->privateKeyFormat = $format;
1943     }
1944
1945     /**
1946      * Determines the public key format
1947      *
1948      * @see createKey()
1949      * @access public
1950      * @param Integer $format
1951      */
1952     function setPublicKeyFormat($format)
1953     {
1954         $this->publicKeyFormat = $format;
1955     }
1956
1957     /**
1958      * Determines which hashing function should be used
1959      *
1960      * Used with signature production / verification and (if the encryption mode is CRYPT_RSA_ENCRYPTION_OAEP) encryption and
1961      * decryption.  If $hash isn't supported, sha1 is used.
1962      *
1963      * @access public
1964      * @param String $hash
1965      */
1966     function setHash($hash)
1967     {
1968         // Crypt_Hash supports algorithms that PKCS#1 doesn't support.  md5-96 and sha1-96, for example.
1969         switch ($hash) {
1970             case 'md2':
1971             case 'md5':
1972             case 'sha1':
1973             case 'sha256':
1974             case 'sha384':
1975             case 'sha512':
1976                 $this->hash = new Crypt_Hash($hash);
1977                 $this->hashName = $hash;
1978                 break;
1979             default:
1980                 $this->hash = new Crypt_Hash('sha1');
1981                 $this->hashName = 'sha1';
1982         }
1983         $this->hLen = $this->hash->getLength();
1984     }
1985
1986     /**
1987      * Determines which hashing function should be used for the mask generation function
1988      *
1989      * The mask generation function is used by CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_SIGNATURE_PSS and although it's
1990      * best if Hash and MGFHash are set to the same thing this is not a requirement.
1991      *
1992      * @access public
1993      * @param String $hash
1994      */
1995     function setMGFHash($hash)
1996     {
1997         // Crypt_Hash supports algorithms that PKCS#1 doesn't support.  md5-96 and sha1-96, for example.
1998         switch ($hash) {
1999             case 'md2':
2000             case 'md5':
2001             case 'sha1':
2002             case 'sha256':
2003             case 'sha384':
2004             case 'sha512':
2005                 $this->mgfHash = new Crypt_Hash($hash);
2006                 break;
2007             default:
2008                 $this->mgfHash = new Crypt_Hash('sha1');
2009         }
2010         $this->mgfHLen = $this->mgfHash->getLength();
2011     }
2012
2013     /**
2014      * Determines the salt length
2015      *
2016      * To quote from {@link http://tools.ietf.org/html/rfc3447#page-38 RFC3447#page-38}:
2017      *
2018      *    Typical salt lengths in octets are hLen (the length of the output
2019      *    of the hash function Hash) and 0.
2020      *
2021      * @access public
2022      * @param Integer $format
2023      */
2024     function setSaltLength($sLen)
2025     {
2026         $this->sLen = $sLen;
2027     }
2028
2029     /**
2030      * Integer-to-Octet-String primitive
2031      *
2032      * See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}.
2033      *
2034      * @access private
2035      * @param Math_BigInteger $x
2036      * @param Integer $xLen
2037      * @return String
2038      */
2039     function _i2osp($x, $xLen)
2040     {
2041         $x = $x->toBytes();
2042         if (strlen($x) > $xLen) {
2043             user_error('Integer too large');
2044             return false;
2045         }
2046         return str_pad($x, $xLen, chr(0), STR_PAD_LEFT);
2047     }
2048
2049     /**
2050      * Octet-String-to-Integer primitive
2051      *
2052      * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}.
2053      *
2054      * @access private
2055      * @param String $x
2056      * @return Math_BigInteger
2057      */
2058     function _os2ip($x)
2059     {
2060         return new Math_BigInteger($x, 256);
2061     }
2062
2063     /**
2064      * Exponentiate with or without Chinese Remainder Theorem
2065      *
2066      * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.2}.
2067      *
2068      * @access private
2069      * @param Math_BigInteger $x
2070      * @return Math_BigInteger
2071      */
2072     function _exponentiate($x)
2073     {
2074         if (empty($this->primes) || empty($this->coefficients) || empty($this->exponents)) {
2075             return $x->modPow($this->exponent, $this->modulus);
2076         }
2077
2078         $num_primes = count($this->primes);
2079
2080         if (defined('CRYPT_RSA_DISABLE_BLINDING')) {
2081             $m_i = array(
2082                 1 => $x->modPow($this->exponents[1], $this->primes[1]),
2083                 2 => $x->modPow($this->exponents[2], $this->primes[2])
2084             );
2085             $h = $m_i[1]->subtract($m_i[2]);
2086             $h = $h->multiply($this->coefficients[2]);
2087             list(, $h) = $h->divide($this->primes[1]);
2088             $m = $m_i[2]->add($h->multiply($this->primes[2]));
2089
2090             $r = $this->primes[1];
2091             for ($i = 3; $i <= $num_primes; $i++) {
2092                 $m_i = $x->modPow($this->exponents[$i], $this->primes[$i]);
2093
2094                 $r = $r->multiply($this->primes[$i - 1]);
2095
2096                 $h = $m_i->subtract($m);
2097                 $h = $h->multiply($this->coefficients[$i]);
2098                 list(, $h) = $h->divide($this->primes[$i]);
2099
2100                 $m = $m->add($r->multiply($h));
2101             }
2102         } else {
2103             $smallest = $this->primes[1];
2104             for ($i = 2; $i <= $num_primes; $i++) {
2105                 if ($smallest->compare($this->primes[$i]) > 0) {
2106                     $smallest = $this->primes[$i];
2107                 }
2108             }
2109
2110             $one = new Math_BigInteger(1);
2111
2112             $r = $one->random($one, $smallest->subtract($one));
2113
2114             $m_i = array(
2115                 1 => $this->_blind($x, $r, 1),
2116                 2 => $this->_blind($x, $r, 2)
2117             );
2118             $h = $m_i[1]->subtract($m_i[2]);
2119             $h = $h->multiply($this->coefficients[2]);
2120             list(, $h) = $h->divide($this->primes[1]);
2121             $m = $m_i[2]->add($h->multiply($this->primes[2]));
2122
2123             $r = $this->primes[1];
2124             for ($i = 3; $i <= $num_primes; $i++) {
2125                 $m_i = $this->_blind($x, $r, $i);
2126
2127                 $r = $r->multiply($this->primes[$i - 1]);
2128
2129                 $h = $m_i->subtract($m);
2130                 $h = $h->multiply($this->coefficients[$i]);
2131                 list(, $h) = $h->divide($this->primes[$i]);
2132
2133                 $m = $m->add($r->multiply($h));
2134             }
2135         }
2136
2137         return $m;
2138     }
2139
2140     /**
2141      * Performs RSA Blinding
2142      *
2143      * Protects against timing attacks by employing RSA Blinding.
2144      * Returns $x->modPow($this->exponents[$i], $this->primes[$i])
2145      *
2146      * @access private
2147      * @param Math_BigInteger $x
2148      * @param Math_BigInteger $r
2149      * @param Integer $i
2150      * @return Math_BigInteger
2151      */
2152     function _blind($x, $r, $i)
2153     {
2154         $x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i]));
2155         $x = $x->modPow($this->exponents[$i], $this->primes[$i]);
2156
2157         $r = $r->modInverse($this->primes[$i]);
2158         $x = $x->multiply($r);
2159         list(, $x) = $x->divide($this->primes[$i]);
2160
2161         return $x;
2162     }
2163
2164     /**
2165      * Performs blinded RSA equality testing
2166      *
2167      * Protects against a particular type of timing attack described.
2168      *
2169      * See {@link http://codahale.com/a-lesson-in-timing-attacks/ A Lesson In Timing Attacks (or, Don't use MessageDigest.isEquals)}
2170      *
2171      * Thanks for the heads up singpolyma!
2172      *
2173      * @access private
2174      * @param String $x
2175      * @param String $y
2176      * @return Boolean
2177      */
2178     function _equals($x, $y)
2179     {
2180         if (strlen($x) != strlen($y)) {
2181             return false;
2182         }
2183
2184         $result = 0;
2185         for ($i = 0; $i < strlen($x); $i++) {
2186             $result |= ord($x[$i]) ^ ord($y[$i]);
2187         }
2188
2189         return $result == 0;
2190     }
2191
2192     /**
2193      * RSAEP
2194      *
2195      * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}.
2196      *
2197      * @access private
2198      * @param Math_BigInteger $m
2199      * @return Math_BigInteger
2200      */
2201     function _rsaep($m)
2202     {
2203         if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
2204             user_error('Message representative out of range');
2205             return false;
2206         }
2207         return $this->_exponentiate($m);
2208     }
2209
2210     /**
2211      * RSADP
2212      *
2213      * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}.
2214      *
2215      * @access private
2216      * @param Math_BigInteger $c
2217      * @return Math_BigInteger
2218      */
2219     function _rsadp($c)
2220     {
2221         if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) {
2222             user_error('Ciphertext representative out of range');
2223             return false;
2224         }
2225         return $this->_exponentiate($c);
2226     }
2227
2228     /**
2229      * RSASP1
2230      *
2231      * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}.
2232      *
2233      * @access private
2234      * @param Math_BigInteger $m
2235      * @return Math_BigInteger
2236      */
2237     function _rsasp1($m)
2238     {
2239         if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
2240             user_error('Message representative out of range');
2241             return false;
2242         }
2243         return $this->_exponentiate($m);
2244     }
2245
2246     /**
2247      * RSAVP1
2248      *
2249      * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}.
2250      *
2251      * @access private
2252      * @param Math_BigInteger $s
2253      * @return Math_BigInteger
2254      */
2255     function _rsavp1($s)
2256     {
2257         if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) {
2258             user_error('Signature representative out of range');
2259             return false;
2260         }
2261         return $this->_exponentiate($s);
2262     }
2263
2264     /**
2265      * MGF1
2266      *
2267      * See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}.
2268      *
2269      * @access private
2270      * @param String $mgfSeed
2271      * @param Integer $mgfLen
2272      * @return String
2273      */
2274     function _mgf1($mgfSeed, $maskLen)
2275     {
2276         // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output.
2277
2278         $t = '';
2279         $count = ceil($maskLen / $this->mgfHLen);
2280         for ($i = 0; $i < $count; $i++) {
2281             $c = pack('N', $i);
2282             $t.= $this->mgfHash->hash($mgfSeed . $c);
2283         }
2284
2285         return substr($t, 0, $maskLen);
2286     }
2287
2288     /**
2289      * RSAES-OAEP-ENCRYPT
2290      *
2291      * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1 RFC3447#section-7.1.1} and
2292      * {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}.
2293      *
2294      * @access private
2295      * @param String $m
2296      * @param String $l
2297      * @return String
2298      */
2299     function _rsaes_oaep_encrypt($m, $l = '')
2300     {
2301         $mLen = strlen($m);
2302
2303         // Length checking
2304
2305         // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
2306         // be output.
2307
2308         if ($mLen > $this->k - 2 * $this->hLen - 2) {
2309             user_error('Message too long');
2310             return false;
2311         }
2312
2313         // EME-OAEP encoding
2314
2315         $lHash = $this->hash->hash($l);
2316         $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2);
2317         $db = $lHash . $ps . chr(1) . $m;
2318         $seed = crypt_random_string($this->hLen);
2319         $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
2320         $maskedDB = $db ^ $dbMask;
2321         $seedMask = $this->_mgf1($maskedDB, $this->hLen);
2322         $maskedSeed = $seed ^ $seedMask;
2323         $em = chr(0) . $maskedSeed . $maskedDB;
2324
2325         // RSA encryption
2326
2327         $m = $this->_os2ip($em);
2328         $c = $this->_rsaep($m);
2329         $c = $this->_i2osp($c, $this->k);
2330
2331         // Output the ciphertext C
2332
2333         return $c;
2334     }
2335
2336     /**
2337      * RSAES-OAEP-DECRYPT
2338      *
2339      * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}.  The fact that the error
2340      * messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2:
2341      *
2342      *    Note.  Care must be taken to ensure that an opponent cannot
2343      *    distinguish the different error conditions in Step 3.g, whether by
2344      *    error message or timing, or, more generally, learn partial
2345      *    information about the encoded message EM.  Otherwise an opponent may
2346      *    be able to obtain useful information about the decryption of the
2347      *    ciphertext C, leading to a chosen-ciphertext attack such as the one
2348      *    observed by Manger [36].
2349      *
2350      * As for $l...  to quote from {@link http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}:
2351      *
2352      *    Both the encryption and the decryption operations of RSAES-OAEP take
2353      *    the value of a label L as input.  In this version of PKCS #1, L is
2354      *    the empty string; other uses of the label are outside the scope of
2355      *    this document.
2356      *
2357      * @access private
2358      * @param String $c
2359      * @param String $l
2360      * @return String
2361      */
2362     function _rsaes_oaep_decrypt($c, $l = '')
2363     {
2364         // Length checking
2365
2366         // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
2367         // be output.
2368
2369         if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) {
2370             user_error('Decryption error');
2371             return false;
2372         }
2373
2374         // RSA decryption
2375
2376         $c = $this->_os2ip($c);
2377         $m = $this->_rsadp($c);
2378         if ($m === false) {
2379             user_error('Decryption error');
2380             return false;
2381         }
2382         $em = $this->_i2osp($m, $this->k);
2383
2384         // EME-OAEP decoding
2385
2386         $lHash = $this->hash->hash($l);
2387         $y = ord($em[0]);
2388         $maskedSeed = substr($em, 1, $this->hLen);
2389         $maskedDB = substr($em, $this->hLen + 1);
2390         $seedMask = $this->_mgf1($maskedDB, $this->hLen);
2391         $seed = $maskedSeed ^ $seedMask;
2392         $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
2393         $db = $maskedDB ^ $dbMask;
2394         $lHash2 = substr($db, 0, $this->hLen);
2395         $m = substr($db, $this->hLen);
2396         if ($lHash != $lHash2) {
2397             user_error('Decryption error');
2398             return false;
2399         }
2400         $m = ltrim($m, chr(0));
2401         if (ord($m[0]) != 1) {
2402             user_error('Decryption error');
2403             return false;
2404         }
2405
2406         // Output the message M
2407
2408         return substr($m, 1);
2409     }
2410
2411     /**
2412      * RSAES-PKCS1-V1_5-ENCRYPT
2413      *
2414      * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}.
2415      *
2416      * @access private
2417      * @param String $m
2418      * @return String
2419      */
2420     function _rsaes_pkcs1_v1_5_encrypt($m)
2421     {
2422         $mLen = strlen($m);
2423
2424         // Length checking
2425
2426         if ($mLen > $this->k - 11) {
2427             user_error('Message too long');
2428             return false;
2429         }
2430
2431         // EME-PKCS1-v1_5 encoding
2432
2433         $psLen = $this->k - $mLen - 3;
2434         $ps = '';
2435         while (strlen($ps) != $psLen) {
2436             $temp = crypt_random_string($psLen - strlen($ps));
2437             $temp = str_replace("\x00", '', $temp);
2438             $ps.= $temp;
2439         }
2440         $type = 2;
2441         // see the comments of _rsaes_pkcs1_v1_5_decrypt() to understand why this is being done
2442         if (defined('CRYPT_RSA_PKCS15_COMPAT') && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) {
2443             $type = 1;
2444             // "The padding string PS shall consist of k-3-||D|| octets. ... for block type 01, they shall have value FF"
2445             $ps = str_repeat("\xFF", $psLen);
2446         }
2447         $em = chr(0) . chr($type) . $ps . chr(0) . $m;
2448
2449         // RSA encryption
2450         $m = $this->_os2ip($em);
2451         $c = $this->_rsaep($m);
2452         $c = $this->_i2osp($c, $this->k);
2453
2454         // Output the ciphertext C
2455
2456         return $c;
2457     }
2458
2459     /**
2460      * RSAES-PKCS1-V1_5-DECRYPT
2461      *
2462      * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}.
2463      *
2464      * For compatibility purposes, this function departs slightly from the description given in RFC3447.
2465      * The reason being that RFC2313#section-8.1 (PKCS#1 v1.5) states that ciphertext's encrypted by the
2466      * private key should have the second byte set to either 0 or 1 and that ciphertext's encrypted by the
2467      * public key should have the second byte set to 2.  In RFC3447 (PKCS#1 v2.1), the second byte is supposed
2468      * to be 2 regardless of which key is used.  For compatibility purposes, we'll just check to make sure the
2469      * second byte is 2 or less.  If it is, we'll accept the decrypted string as valid.
2470      *
2471      * As a consequence of this, a private key encrypted ciphertext produced with Crypt_RSA may not decrypt
2472      * with a strictly PKCS#1 v1.5 compliant RSA implementation.  Public key encrypted ciphertext's should but
2473      * not private key encrypted ciphertext's.
2474      *
2475      * @access private
2476      * @param String $c
2477      * @return String
2478      */
2479     function _rsaes_pkcs1_v1_5_decrypt($c)
2480     {
2481         // Length checking
2482
2483         if (strlen($c) != $this->k) { // or if k < 11
2484             user_error('Decryption error');
2485             return false;
2486         }
2487
2488         // RSA decryption
2489
2490         $c = $this->_os2ip($c);
2491         $m = $this->_rsadp($c);
2492
2493         if ($m === false) {
2494             user_error('Decryption error');
2495             return false;
2496         }
2497         $em = $this->_i2osp($m, $this->k);
2498
2499         // EME-PKCS1-v1_5 decoding
2500
2501         if (ord($em[0]) != 0 || ord($em[1]) > 2) {
2502             user_error('Decryption error');
2503             return false;
2504         }
2505
2506         $ps = substr($em, 2, strpos($em, chr(0), 2) - 2);
2507         $m = substr($em, strlen($ps) + 3);
2508
2509         if (strlen($ps) < 8) {
2510             user_error('Decryption error');
2511             return false;
2512         }
2513
2514         // Output M
2515
2516         return $m;
2517     }
2518
2519     /**
2520      * EMSA-PSS-ENCODE
2521      *
2522      * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}.
2523      *
2524      * @access private
2525      * @param String $m
2526      * @param Integer $emBits
2527      */
2528     function _emsa_pss_encode($m, $emBits)
2529     {
2530         // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
2531         // be output.
2532
2533         $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8)
2534         $sLen = $this->sLen == false ? $this->hLen : $this->sLen;
2535
2536         $mHash = $this->hash->hash($m);
2537         if ($emLen < $this->hLen + $sLen + 2) {
2538             user_error('Encoding error');
2539             return false;
2540         }
2541
2542         $salt = crypt_random_string($sLen);
2543         $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
2544         $h = $this->hash->hash($m2);
2545         $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2);
2546         $db = $ps . chr(1) . $salt;
2547         $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
2548         $maskedDB = $db ^ $dbMask;
2549         $maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0];
2550         $em = $maskedDB . $h . chr(0xBC);
2551
2552         return $em;
2553     }
2554
2555     /**
2556      * EMSA-PSS-VERIFY
2557      *
2558      * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}.
2559      *
2560      * @access private
2561      * @param String $m
2562      * @param String $em
2563      * @param Integer $emBits
2564      * @return String
2565      */
2566     function _emsa_pss_verify($m, $em, $emBits)
2567     {
2568         // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
2569         // be output.
2570
2571         $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8);
2572         $sLen = $this->sLen == false ? $this->hLen : $this->sLen;
2573
2574         $mHash = $this->hash->hash($m);
2575         if ($emLen < $this->hLen + $sLen + 2) {
2576             return false;
2577         }
2578
2579         if ($em[strlen($em) - 1] != chr(0xBC)) {
2580             return false;
2581         }
2582
2583         $maskedDB = substr($em, 0, -$this->hLen - 1);
2584         $h = substr($em, -$this->hLen - 1, $this->hLen);
2585         $temp = chr(0xFF << ($emBits & 7));
2586         if ((~$maskedDB[0] & $temp) != $temp) {
2587             return false;
2588         }
2589         $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
2590         $db = $maskedDB ^ $dbMask;
2591         $db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
2592         $temp = $emLen - $this->hLen - $sLen - 2;
2593         if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) {
2594             return false;
2595         }
2596         $salt = substr($db, $temp + 1); // should be $sLen long
2597         $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
2598         $h2 = $this->hash->hash($m2);
2599         return $this->_equals($h, $h2);
2600     }
2601
2602     /**
2603      * RSASSA-PSS-SIGN
2604      *
2605      * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}.
2606      *
2607      * @access private
2608      * @param String $m
2609      * @return String
2610      */
2611     function _rsassa_pss_sign($m)
2612     {
2613         // EMSA-PSS encoding
2614
2615         $em = $this->_emsa_pss_encode($m, 8 * $this->k - 1);
2616
2617         // RSA signature
2618
2619         $m = $this->_os2ip($em);
2620         $s = $this->_rsasp1($m);
2621         $s = $this->_i2osp($s, $this->k);
2622
2623         // Output the signature S
2624
2625         return $s;
2626     }
2627
2628     /**
2629      * RSASSA-PSS-VERIFY
2630      *
2631      * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}.
2632      *
2633      * @access private
2634      * @param String $m
2635      * @param String $s
2636      * @return String
2637      */
2638     function _rsassa_pss_verify($m, $s)
2639     {
2640         // Length checking
2641
2642         if (strlen($s) != $this->k) {
2643             user_error('Invalid signature');
2644             return false;
2645         }
2646
2647         // RSA verification
2648
2649         $modBits = 8 * $this->k;
2650
2651         $s2 = $this->_os2ip($s);
2652         $m2 = $this->_rsavp1($s2);
2653         if ($m2 === false) {
2654             user_error('Invalid signature');
2655             return false;
2656         }
2657         $em = $this->_i2osp($m2, $modBits >> 3);
2658         if ($em === false) {
2659             user_error('Invalid signature');
2660             return false;
2661         }
2662
2663         // EMSA-PSS verification
2664
2665         return $this->_emsa_pss_verify($m, $em, $modBits - 1);
2666     }
2667
2668     /**
2669      * EMSA-PKCS1-V1_5-ENCODE
2670      *
2671      * See {@link http://tools.ietf.org/html/rfc3447#section-9.2 RFC3447#section-9.2}.
2672      *
2673      * @access private
2674      * @param String $m
2675      * @param Integer $emLen
2676      * @return String
2677      */
2678     function _emsa_pkcs1_v1_5_encode($m, $emLen)
2679     {
2680         $h = $this->hash->hash($m);
2681         if ($h === false) {
2682             return false;
2683         }
2684
2685         // see http://tools.ietf.org/html/rfc3447#page-43
2686         switch ($this->hashName) {
2687             case 'md2':
2688                 $t = pack('H*', '3020300c06082a864886f70d020205000410');
2689                 break;
2690             case 'md5':
2691                 $t = pack('H*', '3020300c06082a864886f70d020505000410');
2692                 break;
2693             case 'sha1':
2694                 $t = pack('H*', '3021300906052b0e03021a05000414');
2695                 break;
2696             case 'sha256':
2697                 $t = pack('H*', '3031300d060960864801650304020105000420');
2698                 break;
2699             case 'sha384':
2700                 $t = pack('H*', '3041300d060960864801650304020205000430');
2701                 break;
2702             case 'sha512':
2703                 $t = pack('H*', '3051300d060960864801650304020305000440');
2704         }
2705         $t.= $h;
2706         $tLen = strlen($t);
2707
2708         if ($emLen < $tLen + 11) {
2709             user_error('Intended encoded message length too short');
2710             return false;
2711         }
2712
2713         $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
2714
2715         $em = "\0\1$ps\0$t";
2716
2717         return $em;
2718     }
2719
2720     /**
2721      * RSASSA-PKCS1-V1_5-SIGN
2722      *
2723      * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}.
2724      *
2725      * @access private
2726      * @param String $m
2727      * @return String
2728      */
2729     function _rsassa_pkcs1_v1_5_sign($m)
2730     {
2731         // EMSA-PKCS1-v1_5 encoding
2732
2733         $em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
2734         if ($em === false) {
2735             user_error('RSA modulus too short');
2736             return false;
2737         }
2738
2739         // RSA signature
2740
2741         $m = $this->_os2ip($em);
2742         $s = $this->_rsasp1($m);
2743         $s = $this->_i2osp($s, $this->k);
2744
2745         // Output the signature S
2746
2747         return $s;
2748     }
2749
2750     /**
2751      * RSASSA-PKCS1-V1_5-VERIFY
2752      *
2753      * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}.
2754      *
2755      * @access private
2756      * @param String $m
2757      * @return String
2758      */
2759     function _rsassa_pkcs1_v1_5_verify($m, $s)
2760     {
2761         // Length checking
2762
2763         if (strlen($s) != $this->k) {
2764             user_error('Invalid signature');
2765             return false;
2766         }
2767
2768         // RSA verification
2769
2770         $s = $this->_os2ip($s);
2771         $m2 = $this->_rsavp1($s);
2772         if ($m2 === false) {
2773             user_error('Invalid signature');
2774             return false;
2775         }
2776         $em = $this->_i2osp($m2, $this->k);
2777         if ($em === false) {
2778             user_error('Invalid signature');
2779             return false;
2780         }
2781
2782         // EMSA-PKCS1-v1_5 encoding
2783
2784         $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
2785         if ($em2 === false) {
2786             user_error('RSA modulus too short');
2787             return false;
2788         }
2789
2790         // Compare
2791         return $this->_equals($em, $em2);
2792     }
2793
2794     /**
2795      * Set Encryption Mode
2796      *
2797      * Valid values include CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_ENCRYPTION_PKCS1.
2798      *
2799      * @access public
2800      * @param Integer $mode
2801      */
2802     function setEncryptionMode($mode)
2803     {
2804         $this->encryptionMode = $mode;
2805     }
2806
2807     /**
2808      * Set Signature Mode
2809      *
2810      * Valid values include CRYPT_RSA_SIGNATURE_PSS and CRYPT_RSA_SIGNATURE_PKCS1
2811      *
2812      * @access public
2813      * @param Integer $mode
2814      */
2815     function setSignatureMode($mode)
2816     {
2817         $this->signatureMode = $mode;
2818     }
2819
2820     /**
2821      * Set public key comment.
2822      *
2823      * @access public
2824      * @param String $comment
2825      */
2826     function setComment($comment)
2827     {
2828         $this->comment = $comment;
2829     }
2830
2831     /**
2832      * Get public key comment.
2833      *
2834      * @access public
2835      * @return String
2836      */
2837     function getComment()
2838     {
2839         return $this->comment;
2840     }
2841
2842     /**
2843      * Encryption
2844      *
2845      * Both CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_ENCRYPTION_PKCS1 both place limits on how long $plaintext can be.
2846      * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will
2847      * be concatenated together.
2848      *
2849      * @see decrypt()
2850      * @access public
2851      * @param String $plaintext
2852      * @return String
2853      */
2854     function encrypt($plaintext)
2855     {
2856         switch ($this->encryptionMode) {
2857             case CRYPT_RSA_ENCRYPTION_PKCS1:
2858                 $length = $this->k - 11;
2859                 if ($length <= 0) {
2860                     return false;
2861                 }
2862
2863                 $plaintext = str_split($plaintext, $length);
2864                 $ciphertext = '';
2865                 foreach ($plaintext as $m) {
2866                     $ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m);
2867                 }
2868                 return $ciphertext;
2869             //case CRYPT_RSA_ENCRYPTION_OAEP:
2870             default:
2871                 $length = $this->k - 2 * $this->hLen - 2;
2872                 if ($length <= 0) {
2873                     return false;
2874                 }
2875
2876                 $plaintext = str_split($plaintext, $length);
2877                 $ciphertext = '';
2878                 foreach ($plaintext as $m) {
2879                     $ciphertext.= $this->_rsaes_oaep_encrypt($m);
2880                 }
2881                 return $ciphertext;
2882         }
2883     }
2884
2885     /**
2886      * Decryption
2887      *
2888      * @see encrypt()
2889      * @access public
2890      * @param String $plaintext
2891      * @return String
2892      */
2893     function decrypt($ciphertext)
2894     {
2895         if ($this->k <= 0) {
2896             return false;
2897         }
2898
2899         $ciphertext = str_split($ciphertext, $this->k);
2900         $ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], $this->k, chr(0), STR_PAD_LEFT);
2901
2902         $plaintext = '';
2903
2904         switch ($this->encryptionMode) {
2905             case CRYPT_RSA_ENCRYPTION_PKCS1:
2906                 $decrypt = '_rsaes_pkcs1_v1_5_decrypt';
2907                 break;
2908             //case CRYPT_RSA_ENCRYPTION_OAEP:
2909             default:
2910                 $decrypt = '_rsaes_oaep_decrypt';
2911         }
2912
2913         foreach ($ciphertext as $c) {
2914             $temp = $this->$decrypt($c);
2915             if ($temp === false) {
2916                 return false;
2917             }
2918             $plaintext.= $temp;
2919         }
2920
2921         return $plaintext;
2922     }
2923
2924     /**
2925      * Create a signature
2926      *
2927      * @see verify()
2928      * @access public
2929      * @param String $message
2930      * @return String
2931      */
2932     function sign($message)
2933     {
2934         if (empty($this->modulus) || empty($this->exponent)) {
2935             return false;
2936         }
2937
2938         switch ($this->signatureMode) {
2939             case CRYPT_RSA_SIGNATURE_PKCS1:
2940                 return $this->_rsassa_pkcs1_v1_5_sign($message);
2941             //case CRYPT_RSA_SIGNATURE_PSS:
2942             default:
2943                 return $this->_rsassa_pss_sign($message);
2944         }
2945     }
2946
2947     /**
2948      * Verifies a signature
2949      *
2950      * @see sign()
2951      * @access public
2952      * @param String $message
2953      * @param String $signature
2954      * @return Boolean
2955      */
2956     function verify($message, $signature)
2957     {
2958         if (empty($this->modulus) || empty($this->exponent)) {
2959             return false;
2960         }
2961
2962         switch ($this->signatureMode) {
2963             case CRYPT_RSA_SIGNATURE_PKCS1:
2964                 return $this->_rsassa_pkcs1_v1_5_verify($message, $signature);
2965             //case CRYPT_RSA_SIGNATURE_PSS:
2966             default:
2967                 return $this->_rsassa_pss_verify($message, $signature);
2968         }
2969     }
2970
2971     /**
2972      * Extract raw BER from Base64 encoding
2973      *
2974      * @access private
2975      * @param String $str
2976      * @return String
2977      */
2978     function _extractBER($str)
2979     {
2980         /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them
2981          * above and beyond the ceritificate.
2982          * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line:
2983          *
2984          * Bag Attributes
2985          *     localKeyID: 01 00 00 00
2986          * subject=/O=organization/OU=org unit/CN=common name
2987          * issuer=/O=organization/CN=common name
2988          */
2989         $temp = preg_replace('#.*?^-+[^-]+-+#ms', '', $str, 1);
2990         // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff
2991         $temp = preg_replace('#-+[^-]+-+#', '', $temp);
2992         // remove new lines
2993         $temp = str_replace(array("\r", "\n", ' '), '', $temp);
2994         $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
2995         return $temp != false ? $temp : $str;
2996     }
2997 }