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