PluginProbe
ManageWP Worker / 4.9.25
ManageWP Worker v4.9.25
4.9.38 4.9.37 4.9.36 4.9.35 4.9.34 3.8.7 3.8.8 3.9.0 3.9.1 3.9.10 3.9.11 3.9.12 3.9.13 3.9.14 3.9.15 3.9.16 3.9.17 3.9.18 3.9.19 3.9.2 3.9.20 3.9.21 3.9.22 3.9.23 3.9.24 All 73 releases
worker / src / PHPSecLib / Crypt / RSA.php

RSA.php in ManageWP Worker 4.9.25, at src/PHPSecLib/Crypt/RSA.php

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