PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 4.5.4
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v4.5.4
4.7.0 4.6.1 4.6.0 4.5.5 4.5.4 4.5.3 4.5.2 3.2.20 3.2.21 3.2.22 3.2.3 3.2.5 3.2.6 3.2.7 3.2.9 3.3.0 3.3.1 3.3.2 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.7.0 3.7.1 All 111 releases
wp-optimize / vendor / phpseclib / phpseclib / phpseclib / Crypt / RSA.php

RSA.php in WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance 4.5.4, at vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php

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