4 * Pure-PHP PKCS#1 (v2.1) compliant implementation of RSA.
8 * Here's an example of how to encrypt and decrypt text with this library:
11 * include 'Crypt/RSA.php';
13 * $rsa = new Crypt_RSA();
14 * extract($rsa->createKey());
16 * $plaintext = 'terrafrost';
18 * $rsa->loadKey($privatekey);
19 * $ciphertext = $rsa->encrypt($plaintext);
21 * $rsa->loadKey($publickey);
22 * echo $rsa->decrypt($ciphertext);
26 * Here's an example of how to create signatures and verify signatures with this library:
29 * include 'Crypt/RSA.php';
31 * $rsa = new Crypt_RSA();
32 * extract($rsa->createKey());
34 * $plaintext = 'terrafrost';
36 * $rsa->loadKey($privatekey);
37 * $signature = $rsa->sign($plaintext);
39 * $rsa->loadKey($publickey);
40 * echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified';
44 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
45 * of this software and associated documentation files (the "Software"), to deal
46 * in the Software without restriction, including without limitation the rights
47 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
48 * copies of the Software, and to permit persons to whom the Software is
49 * furnished to do so, subject to the following conditions:
51 * The above copyright notice and this permission notice shall be included in
52 * all copies or substantial portions of the Software.
54 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
55 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
56 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
57 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
58 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
59 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
64 * @author Jim Wigginton <terrafrost@php.net>
65 * @copyright 2009 Jim Wigginton
66 * @license http://www.opensource.org/licenses/mit-license.html MIT License
67 * @link http://phpseclib.sourceforge.net
71 * Include Crypt_Random
73 // the class_exists() will only be called if the crypt_random_string function hasn't been defined and
74 // will trigger a call to __autoload() if you're wanting to auto-load classes
75 // call function_exists() a second time to stop the include_once from being called outside
77 if (!function_exists('crypt_random_string')) {
78 include_once 'Random.php';
84 if (!class_exists('Crypt_Hash')) {
85 include_once 'Hash.php';
90 * @see Crypt_RSA::encrypt()
91 * @see Crypt_RSA::decrypt()
94 * Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding}
95 * (OAEP) for encryption / decryption.
97 * Uses sha1 by default.
99 * @see Crypt_RSA::setHash()
100 * @see Crypt_RSA::setMGFHash()
102 define('CRYPT_RSA_ENCRYPTION_OAEP', 1);
104 * Use PKCS#1 padding.
106 * Although CRYPT_RSA_ENCRYPTION_OAEP offers more security, including PKCS#1 padding is necessary for purposes of backwards
107 * compatibility with protocols (like SSH-1) written before OAEP's introduction.
109 define('CRYPT_RSA_ENCRYPTION_PKCS1', 2);
114 * @see Crypt_RSA::sign()
115 * @see Crypt_RSA::verify()
116 * @see Crypt_RSA::setHash()
119 * Use the Probabilistic Signature Scheme for signing
121 * Uses sha1 by default.
123 * @see Crypt_RSA::setSaltLength()
124 * @see Crypt_RSA::setMGFHash()
126 define('CRYPT_RSA_SIGNATURE_PSS', 1);
128 * Use the PKCS#1 scheme by default.
130 * Although CRYPT_RSA_SIGNATURE_PSS offers more security, including PKCS#1 signing is necessary for purposes of backwards
131 * compatibility with protocols (like SSH-2) written before PSS's introduction.
133 define('CRYPT_RSA_SIGNATURE_PKCS1', 2);
138 * @see Crypt_RSA::createKey()
143 define('CRYPT_RSA_ASN1_INTEGER', 2);
147 define('CRYPT_RSA_ASN1_BITSTRING', 3);
151 define('CRYPT_RSA_ASN1_OCTETSTRING', 4);
153 * ASN1 Object Identifier
155 define('CRYPT_RSA_ASN1_OBJECT', 6);
157 * ASN1 Sequence (with the constucted bit set)
159 define('CRYPT_RSA_ASN1_SEQUENCE', 48);
164 * @see Crypt_RSA::Crypt_RSA()
167 * To use the pure-PHP implementation
169 define('CRYPT_RSA_MODE_INTERNAL', 1);
171 * To use the OpenSSL library
173 * (if enabled; otherwise, the internal implementation will be used)
175 define('CRYPT_RSA_MODE_OPENSSL', 2);
179 * Default openSSL configuration file.
181 define('CRYPT_RSA_OPENSSL_CONFIG', dirname(__FILE__) . '/../openssl.cnf');
185 * @see Crypt_RSA::createKey()
186 * @see Crypt_RSA::setPrivateKeyFormat()
189 * PKCS#1 formatted private key
193 define('CRYPT_RSA_PRIVATE_FORMAT_PKCS1', 0);
195 * PuTTY formatted private key
197 define('CRYPT_RSA_PRIVATE_FORMAT_PUTTY', 1);
199 * XML formatted private key
201 define('CRYPT_RSA_PRIVATE_FORMAT_XML', 2);
203 * PKCS#8 formatted private key
205 define('CRYPT_RSA_PRIVATE_FORMAT_PKCS8', 3);
210 * @see Crypt_RSA::createKey()
211 * @see Crypt_RSA::setPublicKeyFormat()
216 * An array containing two Math_BigInteger objects.
218 * The exponent can be indexed with any of the following:
220 * 0, e, exponent, publicExponent
222 * The modulus can be indexed with any of the following:
224 * 1, n, modulo, modulus
226 define('CRYPT_RSA_PUBLIC_FORMAT_RAW', 3);
228 * PKCS#1 formatted public key (raw)
230 * Used by File/X509.php
232 * Has the following header:
234 * -----BEGIN RSA PUBLIC KEY-----
236 * Analogous to ssh-keygen's pem format (as specified by -m)
238 define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1', 4);
239 define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW', 4);
241 * XML formatted public key
243 define('CRYPT_RSA_PUBLIC_FORMAT_XML', 5);
245 * OpenSSH formatted public key
247 * Place in $HOME/.ssh/authorized_keys
249 define('CRYPT_RSA_PUBLIC_FORMAT_OPENSSH', 6);
251 * PKCS#1 formatted public key (encapsulated)
253 * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set)
255 * Has the following header:
257 * -----BEGIN PUBLIC KEY-----
259 * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
260 * is specific to private keys it's basically creating a DER-encoded wrapper
261 * for keys. This just extends that same concept to public keys (much like ssh-keygen)
263 define('CRYPT_RSA_PUBLIC_FORMAT_PKCS8', 7);
267 * Pure-PHP PKCS#1 compliant implementation of RSA.
270 * @author Jim Wigginton <terrafrost@php.net>
297 var $privateKeyFormat = CRYPT_RSA_PRIVATE_FORMAT_PKCS1;
305 var $publicKeyFormat = CRYPT_RSA_PUBLIC_FORMAT_PKCS8;
310 * @var Math_BigInteger
318 * @var Math_BigInteger
324 * Exponent (ie. e or d)
326 * @var Math_BigInteger
332 * Primes for Chinese Remainder Theorem (ie. p and q)
340 * Exponents for Chinese Remainder Theorem (ie. dP and dQ)
348 * Coefficients for Chinese Remainder Theorem (ie. qInv)
372 * Length of hash function output
388 * Hash function for the Mask Generation Function
396 * Length of MGF hash function output
409 var $encryptionMode = CRYPT_RSA_ENCRYPTION_OAEP;
417 var $signatureMode = CRYPT_RSA_SIGNATURE_PSS;
425 var $publicExponent = false;
433 var $password = false;
438 * For use with parsing XML formatted keys. PHP's XML Parser functions use utilized - instead of PHP's DOM functions -
439 * because PHP's XML Parser functions work on PHP4 whereas PHP's DOM functions - although surperior - don't.
441 * @see Crypt_RSA::_start_element_handler()
445 var $components = array();
450 * For use with parsing XML formatted keys.
452 * @see Crypt_RSA::_character_handler()
453 * @see Crypt_RSA::_stop_element_handler()
460 * OpenSSL configuration file name.
462 * Set to null to use system configuration file.
463 * @see Crypt_RSA::createKey()
470 * Public key comment field.
475 var $comment = 'phpseclib-generated-key';
480 * If you want to make use of the openssl extension, you'll need to set the mode manually, yourself. The reason
481 * Crypt_RSA doesn't do it is because OpenSSL doesn't fail gracefully. openssl_pkey_new(), in particular, requires
482 * openssl.cnf be present somewhere and, unfortunately, the only real way to find out is too late.
489 if (!class_exists('Math_BigInteger')) {
490 include_once 'Math/BigInteger.php';
493 $this->configFile = CRYPT_RSA_OPENSSL_CONFIG;
495 if ( !defined('CRYPT_RSA_MODE') ) {
497 // Math/BigInteger's openssl requirements are a little less stringent than Crypt/RSA's. in particular,
498 // Math/BigInteger doesn't require an openssl.cfg file whereas Crypt/RSA does. so if Math/BigInteger
499 // can't use OpenSSL it can be pretty trivially assumed, then, that Crypt/RSA can't either.
500 case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'):
501 define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
503 // openssl_pkey_get_details - which is used in the only place Crypt/RSA.php uses OpenSSL - was introduced in PHP 5.2.0
504 case !function_exists('openssl_pkey_get_details'):
505 define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
507 case extension_loaded('openssl') && version_compare(PHP_VERSION, '4.2.0', '>=') && file_exists($this->configFile):
508 // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
511 $content = ob_get_contents();
514 preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches);
517 if (!empty($matches[1])) {
518 for ($i = 0; $i < count($matches[1]); $i++) {
519 $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i])));
521 // Remove letter part in OpenSSL version
522 if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) {
523 $versions[$matches[1][$i]] = $fullVersion;
525 $versions[$matches[1][$i]] = $m[0];
530 // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+
532 case !isset($versions['Header']):
533 case !isset($versions['Library']):
534 case $versions['Header'] == $versions['Library']:
535 define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_OPENSSL);
538 define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
539 define('MATH_BIGINTEGER_OPENSSL_DISABLE', true);
543 define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
547 $this->zero = new Math_BigInteger();
548 $this->one = new Math_BigInteger(1);
550 $this->hash = new Crypt_Hash('sha1');
551 $this->hLen = $this->hash->getLength();
552 $this->hashName = 'sha1';
553 $this->mgfHash = new Crypt_Hash('sha1');
554 $this->mgfHLen = $this->mgfHash->getLength();
558 * Create public / private key pair
560 * Returns an array with the following three elements:
561 * - 'privatekey': The private key.
562 * - 'publickey': The public key.
563 * - 'partialkey': A partially computed key (if the execution time exceeded $timeout).
564 * Will need to be passed back to Crypt_RSA::createKey() as the third parameter for further processing.
567 * @param optional Integer $bits
568 * @param optional Integer $timeout
569 * @param optional Math_BigInteger $p
571 function createKey($bits = 1024, $timeout = false, $partial = array())
573 if (!defined('CRYPT_RSA_EXPONENT')) {
574 // http://en.wikipedia.org/wiki/65537_%28number%29
575 define('CRYPT_RSA_EXPONENT', '65537');
577 // per <http://cseweb.ucsd.edu/~hovav/dist/survey.pdf#page=5>, this number ought not result in primes smaller
578 // than 256 bits. as a consequence if the key you're trying to create is 1024 bits and you've set CRYPT_RSA_SMALLEST_PRIME
579 // to 384 bits then you're going to get a 384 bit prime and a 640 bit prime (384 + 1024 % 384). at least if
580 // CRYPT_RSA_MODE is set to CRYPT_RSA_MODE_INTERNAL. if CRYPT_RSA_MODE is set to CRYPT_RSA_MODE_OPENSSL then
581 // CRYPT_RSA_SMALLEST_PRIME is ignored (ie. multi-prime RSA support is more intended as a way to speed up RSA key
582 // generation when there's a chance neither gmp nor OpenSSL are installed)
583 if (!defined('CRYPT_RSA_SMALLEST_PRIME')) {
584 define('CRYPT_RSA_SMALLEST_PRIME', 4096);
587 // OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum
588 if ( CRYPT_RSA_MODE == CRYPT_RSA_MODE_OPENSSL && $bits >= 384 && CRYPT_RSA_EXPONENT == 65537) {
590 if (isset($this->configFile)) {
591 $config['config'] = $this->configFile;
593 $rsa = openssl_pkey_new(array('private_key_bits' => $bits) + $config);
594 openssl_pkey_export($rsa, $privatekey, null, $config);
595 $publickey = openssl_pkey_get_details($rsa);
596 $publickey = $publickey['key'];
598 $privatekey = call_user_func_array(array($this, '_convertPrivateKey'), array_values($this->_parseKey($privatekey, CRYPT_RSA_PRIVATE_FORMAT_PKCS1)));
599 $publickey = call_user_func_array(array($this, '_convertPublicKey'), array_values($this->_parseKey($publickey, CRYPT_RSA_PUBLIC_FORMAT_PKCS1)));
601 // clear the buffer of error strings stemming from a minimalistic openssl.cnf
602 while (openssl_error_string() !== false);
605 'privatekey' => $privatekey,
606 'publickey' => $publickey,
607 'partialkey' => false
613 $e = new Math_BigInteger(CRYPT_RSA_EXPONENT);
616 extract($this->_generateMinMax($bits));
618 $temp = $bits >> 1; // divide by two to see how many bits P and Q would be
619 if ($temp > CRYPT_RSA_SMALLEST_PRIME) {
620 $num_primes = floor($bits / CRYPT_RSA_SMALLEST_PRIME);
621 $temp = CRYPT_RSA_SMALLEST_PRIME;
625 extract($this->_generateMinMax($temp + $bits % $temp));
627 extract($this->_generateMinMax($temp));
629 $generator = new Math_BigInteger();
631 $n = $this->one->copy();
632 if (!empty($partial)) {
633 extract(unserialize($partial));
635 $exponents = $coefficients = $primes = array();
637 'top' => $this->one->copy(),
643 $i0 = count($primes) + 1;
646 for ($i = $i0; $i <= $num_primes; $i++) {
647 if ($timeout !== false) {
648 $timeout-= time() - $start;
654 'partialkey' => serialize(array(
656 'coefficients' => $coefficients,
658 'exponents' => $exponents
664 if ($i == $num_primes) {
665 list($min, $temp) = $absoluteMin->divide($n);
666 if (!$temp->equals($this->zero)) {
667 $min = $min->add($this->one); // ie. ceil()
669 $primes[$i] = $generator->randomPrime($min, $finalMax, $timeout);
671 $primes[$i] = $generator->randomPrime($min, $max, $timeout);
674 if ($primes[$i] === false) { // if we've reached the timeout
675 if (count($primes) > 1) {
679 $partialkey = serialize(array(
681 'coefficients' => $coefficients,
683 'exponents' => $exponents
690 'partialkey' => $partialkey
694 // the first coefficient is calculated differently from the rest
695 // ie. instead of being $primes[1]->modInverse($primes[2]), it's $primes[2]->modInverse($primes[1])
697 $coefficients[$i] = $n->modInverse($primes[$i]);
700 $n = $n->multiply($primes[$i]);
702 $temp = $primes[$i]->subtract($this->one);
704 // textbook RSA implementations use Euler's totient function instead of the least common multiple.
705 // see http://en.wikipedia.org/wiki/Euler%27s_totient_function
706 $lcm['top'] = $lcm['top']->multiply($temp);
707 $lcm['bottom'] = $lcm['bottom'] === false ? $temp : $lcm['bottom']->gcd($temp);
709 $exponents[$i] = $e->modInverse($temp);
712 list($temp) = $lcm['top']->divide($lcm['bottom']);
713 $gcd = $temp->gcd($e);
715 } while (!$gcd->equals($this->one));
717 $d = $e->modInverse($temp);
719 $coefficients[2] = $primes[2]->modInverse($primes[1]);
721 // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.2>:
722 // RSAPrivateKey ::= SEQUENCE {
724 // modulus INTEGER, -- n
725 // publicExponent INTEGER, -- e
726 // privateExponent INTEGER, -- d
727 // prime1 INTEGER, -- p
728 // prime2 INTEGER, -- q
729 // exponent1 INTEGER, -- d mod (p-1)
730 // exponent2 INTEGER, -- d mod (q-1)
731 // coefficient INTEGER, -- (inverse of q) mod p
732 // otherPrimeInfos OtherPrimeInfos OPTIONAL
736 'privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients),
737 'publickey' => $this->_convertPublicKey($n, $e),
738 'partialkey' => false
743 * Convert a private key to the appropriate format.
746 * @see setPrivateKeyFormat()
747 * @param String $RSAPrivateKey
750 function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients)
752 $signed = $this->privateKeyFormat != CRYPT_RSA_PRIVATE_FORMAT_XML;
753 $num_primes = count($primes);
755 'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi
756 'modulus' => $n->toBytes($signed),
757 'publicExponent' => $e->toBytes($signed),
758 'privateExponent' => $d->toBytes($signed),
759 'prime1' => $primes[1]->toBytes($signed),
760 'prime2' => $primes[2]->toBytes($signed),
761 'exponent1' => $exponents[1]->toBytes($signed),
762 'exponent2' => $exponents[2]->toBytes($signed),
763 'coefficient' => $coefficients[2]->toBytes($signed)
766 // if the format in question does not support multi-prime rsa and multi-prime rsa was used,
767 // call _convertPublicKey() instead.
768 switch ($this->privateKeyFormat) {
769 case CRYPT_RSA_PRIVATE_FORMAT_XML:
770 if ($num_primes != 2) {
773 return "<RSAKeyValue>\r\n" .
774 ' <Modulus>' . base64_encode($raw['modulus']) . "</Modulus>\r\n" .
775 ' <Exponent>' . base64_encode($raw['publicExponent']) . "</Exponent>\r\n" .
776 ' <P>' . base64_encode($raw['prime1']) . "</P>\r\n" .
777 ' <Q>' . base64_encode($raw['prime2']) . "</Q>\r\n" .
778 ' <DP>' . base64_encode($raw['exponent1']) . "</DP>\r\n" .
779 ' <DQ>' . base64_encode($raw['exponent2']) . "</DQ>\r\n" .
780 ' <InverseQ>' . base64_encode($raw['coefficient']) . "</InverseQ>\r\n" .
781 ' <D>' . base64_encode($raw['privateExponent']) . "</D>\r\n" .
784 case CRYPT_RSA_PRIVATE_FORMAT_PUTTY:
785 if ($num_primes != 2) {
788 $key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: ";
789 $encryption = (!empty($this->password) || is_string($this->password)) ? 'aes256-cbc' : 'none';
791 $key.= "\r\nComment: " . $this->comment . "\r\n";
792 $public = pack('Na*Na*Na*',
793 strlen('ssh-rsa'), 'ssh-rsa', strlen($raw['publicExponent']), $raw['publicExponent'], strlen($raw['modulus']), $raw['modulus']
795 $source = pack('Na*Na*Na*Na*',
796 strlen('ssh-rsa'), 'ssh-rsa', strlen($encryption), $encryption,
797 strlen($this->comment), $this->comment, strlen($public), $public
799 $public = base64_encode($public);
800 $key.= "Public-Lines: " . ((strlen($public) + 63) >> 6) . "\r\n";
801 $key.= chunk_split($public, 64);
802 $private = pack('Na*Na*Na*Na*',
803 strlen($raw['privateExponent']), $raw['privateExponent'], strlen($raw['prime1']), $raw['prime1'],
804 strlen($raw['prime2']), $raw['prime2'], strlen($raw['coefficient']), $raw['coefficient']
806 if (empty($this->password) && !is_string($this->password)) {
807 $source.= pack('Na*', strlen($private), $private);
808 $hashkey = 'putty-private-key-file-mac-key';
810 $private.= crypt_random_string(16 - (strlen($private) & 15));
811 $source.= pack('Na*', strlen($private), $private);
812 if (!class_exists('Crypt_AES')) {
813 include_once 'Crypt/AES.php';
817 while (strlen($symkey) < 32) {
818 $temp = pack('Na*', $sequence++, $this->password);
819 $symkey.= pack('H*', sha1($temp));
821 $symkey = substr($symkey, 0, 32);
822 $crypto = new Crypt_AES();
824 $crypto->setKey($symkey);
825 $crypto->disablePadding();
826 $private = $crypto->encrypt($private);
827 $hashkey = 'putty-private-key-file-mac-key' . $this->password;
830 $private = base64_encode($private);
831 $key.= 'Private-Lines: ' . ((strlen($private) + 63) >> 6) . "\r\n";
832 $key.= chunk_split($private, 64);
833 if (!class_exists('Crypt_Hash')) {
834 include_once 'Crypt/Hash.php';
836 $hash = new Crypt_Hash('sha1');
837 $hash->setKey(pack('H*', sha1($hashkey)));
838 $key.= 'Private-MAC: ' . bin2hex($hash->hash($source)) . "\r\n";
841 default: // eg. CRYPT_RSA_PRIVATE_FORMAT_PKCS1
842 $components = array();
843 foreach ($raw as $name => $value) {
844 $components[$name] = pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($value)), $value);
847 $RSAPrivateKey = implode('', $components);
849 if ($num_primes > 2) {
850 $OtherPrimeInfos = '';
851 for ($i = 3; $i <= $num_primes; $i++) {
852 // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
854 // OtherPrimeInfo ::= SEQUENCE {
855 // prime INTEGER, -- ri
856 // exponent INTEGER, -- di
857 // coefficient INTEGER -- ti
859 $OtherPrimeInfo = pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true));
860 $OtherPrimeInfo.= pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true));
861 $OtherPrimeInfo.= pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true));
862 $OtherPrimeInfos.= pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo);
864 $RSAPrivateKey.= pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos);
867 $RSAPrivateKey = pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
869 if ($this->privateKeyFormat == CRYPT_RSA_PRIVATE_FORMAT_PKCS8) {
870 $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
871 $RSAPrivateKey = pack('Ca*a*Ca*a*',
872 CRYPT_RSA_ASN1_INTEGER, "\01\00", $rsaOID, 4, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey
874 $RSAPrivateKey = pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
875 if (!empty($this->password) || is_string($this->password)) {
876 $salt = crypt_random_string(8);
877 $iterationCount = 2048;
879 if (!class_exists('Crypt_DES')) {
880 include_once 'Crypt/DES.php';
882 $crypto = new Crypt_DES();
883 $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount);
884 $RSAPrivateKey = $crypto->encrypt($RSAPrivateKey);
886 $parameters = pack('Ca*a*Ca*N',
887 CRYPT_RSA_ASN1_OCTETSTRING, $this->_encodeLength(strlen($salt)), $salt,
888 CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(4), $iterationCount
890 $pbeWithMD5AndDES_CBC = "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03";
892 $encryptionAlgorithm = pack('Ca*a*Ca*a*',
893 CRYPT_RSA_ASN1_OBJECT, $this->_encodeLength(strlen($pbeWithMD5AndDES_CBC)), $pbeWithMD5AndDES_CBC,
894 CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($parameters)), $parameters
897 $RSAPrivateKey = pack('Ca*a*Ca*a*',
898 CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($encryptionAlgorithm)), $encryptionAlgorithm,
899 CRYPT_RSA_ASN1_OCTETSTRING, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey
902 $RSAPrivateKey = pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
904 $RSAPrivateKey = "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" .
905 chunk_split(base64_encode($RSAPrivateKey), 64) .
906 '-----END ENCRYPTED PRIVATE KEY-----';
908 $RSAPrivateKey = "-----BEGIN PRIVATE KEY-----\r\n" .
909 chunk_split(base64_encode($RSAPrivateKey), 64) .
910 '-----END PRIVATE KEY-----';
912 return $RSAPrivateKey;
915 if (!empty($this->password) || is_string($this->password)) {
916 $iv = crypt_random_string(8);
917 $symkey = pack('H*', md5($this->password . $iv)); // symkey is short for symmetric key
918 $symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8);
919 if (!class_exists('Crypt_TripleDES')) {
920 include_once 'Crypt/TripleDES.php';
922 $des = new Crypt_TripleDES();
923 $des->setKey($symkey);
925 $iv = strtoupper(bin2hex($iv));
926 $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
927 "Proc-Type: 4,ENCRYPTED\r\n" .
928 "DEK-Info: DES-EDE3-CBC,$iv\r\n" .
930 chunk_split(base64_encode($des->encrypt($RSAPrivateKey)), 64) .
931 '-----END RSA PRIVATE KEY-----';
933 $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
934 chunk_split(base64_encode($RSAPrivateKey), 64) .
935 '-----END RSA PRIVATE KEY-----';
938 return $RSAPrivateKey;
943 * Convert a public key to the appropriate format
946 * @see setPublicKeyFormat()
947 * @param String $RSAPrivateKey
950 function _convertPublicKey($n, $e)
952 $signed = $this->publicKeyFormat != CRYPT_RSA_PUBLIC_FORMAT_XML;
954 $modulus = $n->toBytes($signed);
955 $publicExponent = $e->toBytes($signed);
957 switch ($this->publicKeyFormat) {
958 case CRYPT_RSA_PUBLIC_FORMAT_RAW:
959 return array('e' => $e->copy(), 'n' => $n->copy());
960 case CRYPT_RSA_PUBLIC_FORMAT_XML:
961 return "<RSAKeyValue>\r\n" .
962 ' <Modulus>' . base64_encode($modulus) . "</Modulus>\r\n" .
963 ' <Exponent>' . base64_encode($publicExponent) . "</Exponent>\r\n" .
966 case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
967 // from <http://tools.ietf.org/html/rfc4253#page-15>:
971 $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus);
972 $RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment;
974 return $RSAPublicKey;
975 default: // eg. CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW or CRYPT_RSA_PUBLIC_FORMAT_PKCS1
976 // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.1>:
977 // RSAPublicKey ::= SEQUENCE {
978 // modulus INTEGER, -- n
979 // publicExponent INTEGER -- e
982 'modulus' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus),
983 'publicExponent' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($publicExponent)), $publicExponent)
986 $RSAPublicKey = pack('Ca*a*a*',
987 CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])),
988 $components['modulus'], $components['publicExponent']
991 if ($this->publicKeyFormat == CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW) {
992 $RSAPublicKey = "-----BEGIN RSA PUBLIC KEY-----\r\n" .
993 chunk_split(base64_encode($RSAPublicKey), 64) .
994 '-----END RSA PUBLIC KEY-----';
996 // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
997 $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
998 $RSAPublicKey = chr(0) . $RSAPublicKey;
999 $RSAPublicKey = chr(3) . $this->_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey;
1001 $RSAPublicKey = pack('Ca*a*',
1002 CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($rsaOID . $RSAPublicKey)), $rsaOID . $RSAPublicKey
1005 $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
1006 chunk_split(base64_encode($RSAPublicKey), 64) .
1007 '-----END PUBLIC KEY-----';
1010 return $RSAPublicKey;
1015 * Break a public or private key down into its constituant components
1018 * @see _convertPublicKey()
1019 * @see _convertPrivateKey()
1020 * @param String $key
1021 * @param Integer $type
1024 function _parseKey($key, $type)
1026 if ($type != CRYPT_RSA_PUBLIC_FORMAT_RAW && !is_string($key)) {
1031 case CRYPT_RSA_PUBLIC_FORMAT_RAW:
1032 if (!is_array($key)) {
1035 $components = array();
1037 case isset($key['e']):
1038 $components['publicExponent'] = $key['e']->copy();
1040 case isset($key['exponent']):
1041 $components['publicExponent'] = $key['exponent']->copy();
1043 case isset($key['publicExponent']):
1044 $components['publicExponent'] = $key['publicExponent']->copy();
1046 case isset($key[0]):
1047 $components['publicExponent'] = $key[0]->copy();
1050 case isset($key['n']):
1051 $components['modulus'] = $key['n']->copy();
1053 case isset($key['modulo']):
1054 $components['modulus'] = $key['modulo']->copy();
1056 case isset($key['modulus']):
1057 $components['modulus'] = $key['modulus']->copy();
1059 case isset($key[1]):
1060 $components['modulus'] = $key[1]->copy();
1062 return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false;
1063 case CRYPT_RSA_PRIVATE_FORMAT_PKCS1:
1064 case CRYPT_RSA_PRIVATE_FORMAT_PKCS8:
1065 case CRYPT_RSA_PUBLIC_FORMAT_PKCS1:
1066 /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
1067 "outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
1068 protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding
1069 two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here:
1071 http://tools.ietf.org/html/rfc1421#section-4.6.1.1
1072 http://tools.ietf.org/html/rfc1421#section-4.6.1.3
1074 DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell.
1075 DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation
1076 function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's
1077 own implementation. ie. the implementation *is* the standard and any bugs that may exist in that
1078 implementation are part of the standard, as well.
1080 * OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */
1081 if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
1082 $iv = pack('H*', trim($matches[2]));
1083 $symkey = pack('H*', md5($this->password . substr($iv, 0, 8))); // symkey is short for symmetric key
1084 $symkey.= pack('H*', md5($symkey . $this->password . substr($iv, 0, 8)));
1085 // remove the Proc-Type / DEK-Info sections as they're no longer needed
1086 $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key);
1087 $ciphertext = $this->_extractBER($key);
1088 if ($ciphertext === false) {
1091 switch ($matches[1]) {
1093 if (!class_exists('Crypt_AES')) {
1094 include_once 'Crypt/AES.php';
1096 $crypto = new Crypt_AES();
1099 if (!class_exists('Crypt_AES')) {
1100 include_once 'Crypt/AES.php';
1102 $symkey = substr($symkey, 0, 16);
1103 $crypto = new Crypt_AES();
1105 case 'DES-EDE3-CFB':
1106 if (!class_exists('Crypt_TripleDES')) {
1107 include_once 'Crypt/TripleDES.php';
1109 $crypto = new Crypt_TripleDES(CRYPT_DES_MODE_CFB);
1111 case 'DES-EDE3-CBC':
1112 if (!class_exists('Crypt_TripleDES')) {
1113 include_once 'Crypt/TripleDES.php';
1115 $symkey = substr($symkey, 0, 24);
1116 $crypto = new Crypt_TripleDES();
1119 if (!class_exists('Crypt_DES')) {
1120 include_once 'Crypt/DES.php';
1122 $crypto = new Crypt_DES();
1127 $crypto->setKey($symkey);
1128 $crypto->setIV($iv);
1129 $decoded = $crypto->decrypt($ciphertext);
1131 $decoded = $this->_extractBER($key);
1134 if ($decoded !== false) {
1138 $components = array();
1140 if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
1143 if ($this->_decodeLength($key) != strlen($key)) {
1147 $tag = ord($this->_string_shift($key));
1148 /* intended for keys for which OpenSSL's asn1parse returns the following:
1150 0:d=0 hl=4 l= 631 cons: SEQUENCE
1151 4:d=1 hl=2 l= 1 prim: INTEGER :00
1152 7:d=1 hl=2 l= 13 cons: SEQUENCE
1153 9:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
1154 20:d=2 hl=2 l= 0 prim: NULL
1155 22:d=1 hl=4 l= 609 prim: OCTET STRING
1159 if ($tag == CRYPT_RSA_ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") {
1160 $this->_string_shift($key, 3);
1161 $tag = CRYPT_RSA_ASN1_SEQUENCE;
1164 if ($tag == CRYPT_RSA_ASN1_SEQUENCE) {
1165 $temp = $this->_string_shift($key, $this->_decodeLength($key));
1166 if (ord($this->_string_shift($temp)) != CRYPT_RSA_ASN1_OBJECT) {
1169 $length = $this->_decodeLength($temp);
1170 switch ($this->_string_shift($temp, $length)) {
1171 case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption
1173 case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC
1175 PBEParameter ::= SEQUENCE {
1176 salt OCTET STRING (SIZE(8)),
1177 iterationCount INTEGER }
1179 if (ord($this->_string_shift($temp)) != CRYPT_RSA_ASN1_SEQUENCE) {
1182 if ($this->_decodeLength($temp) != strlen($temp)) {
1185 $this->_string_shift($temp); // assume it's an octet string
1186 $salt = $this->_string_shift($temp, $this->_decodeLength($temp));
1187 if (ord($this->_string_shift($temp)) != CRYPT_RSA_ASN1_INTEGER) {
1190 $this->_decodeLength($temp);
1191 list(, $iterationCount) = unpack('N', str_pad($temp, 4, chr(0), STR_PAD_LEFT));
1192 $this->_string_shift($key); // assume it's an octet string
1193 $length = $this->_decodeLength($key);
1194 if (strlen($key) != $length) {
1198 if (!class_exists('Crypt_DES')) {
1199 include_once 'Crypt/DES.php';
1201 $crypto = new Crypt_DES();
1202 $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount);
1203 $key = $crypto->decrypt($key);
1204 if ($key === false) {
1207 return $this->_parseKey($key, CRYPT_RSA_PRIVATE_FORMAT_PKCS1);
1211 /* intended for keys for which OpenSSL's asn1parse returns the following:
1213 0:d=0 hl=4 l= 290 cons: SEQUENCE
1214 4:d=1 hl=2 l= 13 cons: SEQUENCE
1215 6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
1216 17:d=2 hl=2 l= 0 prim: NULL
1217 19:d=1 hl=4 l= 271 prim: BIT STRING */
1218 $tag = ord($this->_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag
1219 $this->_decodeLength($key); // skip over the BIT STRING / OCTET STRING length
1220 // "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of
1221 // unused bits in the final subsequent octet. The number shall be in the range zero to seven."
1222 // -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2)
1223 if ($tag == CRYPT_RSA_ASN1_BITSTRING) {
1224 $this->_string_shift($key);
1226 if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
1229 if ($this->_decodeLength($key) != strlen($key)) {
1232 $tag = ord($this->_string_shift($key));
1234 if ($tag != CRYPT_RSA_ASN1_INTEGER) {
1238 $length = $this->_decodeLength($key);
1239 $temp = $this->_string_shift($key, $length);
1240 if (strlen($temp) != 1 || ord($temp) > 2) {
1241 $components['modulus'] = new Math_BigInteger($temp, 256);
1242 $this->_string_shift($key); // skip over CRYPT_RSA_ASN1_INTEGER
1243 $length = $this->_decodeLength($key);
1244 $components[$type == CRYPT_RSA_PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1248 if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_INTEGER) {
1251 $length = $this->_decodeLength($key);
1252 $components['modulus'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1253 $this->_string_shift($key);
1254 $length = $this->_decodeLength($key);
1255 $components['publicExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1256 $this->_string_shift($key);
1257 $length = $this->_decodeLength($key);
1258 $components['privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1259 $this->_string_shift($key);
1260 $length = $this->_decodeLength($key);
1261 $components['primes'] = array(1 => new Math_BigInteger($this->_string_shift($key, $length), 256));
1262 $this->_string_shift($key);
1263 $length = $this->_decodeLength($key);
1264 $components['primes'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1265 $this->_string_shift($key);
1266 $length = $this->_decodeLength($key);
1267 $components['exponents'] = array(1 => new Math_BigInteger($this->_string_shift($key, $length), 256));
1268 $this->_string_shift($key);
1269 $length = $this->_decodeLength($key);
1270 $components['exponents'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1271 $this->_string_shift($key);
1272 $length = $this->_decodeLength($key);
1273 $components['coefficients'] = array(2 => new Math_BigInteger($this->_string_shift($key, $length), 256));
1276 if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
1279 $this->_decodeLength($key);
1280 while (!empty($key)) {
1281 if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
1284 $this->_decodeLength($key);
1285 $key = substr($key, 1);
1286 $length = $this->_decodeLength($key);
1287 $components['primes'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1288 $this->_string_shift($key);
1289 $length = $this->_decodeLength($key);
1290 $components['exponents'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1291 $this->_string_shift($key);
1292 $length = $this->_decodeLength($key);
1293 $components['coefficients'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1298 case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
1299 $parts = explode(' ', $key, 3);
1301 $key = isset($parts[1]) ? base64_decode($parts[1]) : false;
1302 if ($key === false) {
1306 $comment = isset($parts[2]) ? $parts[2] : false;
1308 $cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa";
1310 if (strlen($key) <= 4) {
1313 extract(unpack('Nlength', $this->_string_shift($key, 4)));
1314 $publicExponent = new Math_BigInteger($this->_string_shift($key, $length), -256);
1315 if (strlen($key) <= 4) {
1318 extract(unpack('Nlength', $this->_string_shift($key, 4)));
1319 $modulus = new Math_BigInteger($this->_string_shift($key, $length), -256);
1321 if ($cleanup && strlen($key)) {
1322 if (strlen($key) <= 4) {
1325 extract(unpack('Nlength', $this->_string_shift($key, 4)));
1326 $realModulus = new Math_BigInteger($this->_string_shift($key, $length), -256);
1327 return strlen($key) ? false : array(
1328 'modulus' => $realModulus,
1329 'publicExponent' => $modulus,
1330 'comment' => $comment
1333 return strlen($key) ? false : array(
1334 'modulus' => $modulus,
1335 'publicExponent' => $publicExponent,
1336 'comment' => $comment
1339 // http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue
1340 // http://en.wikipedia.org/wiki/XML_Signature
1341 case CRYPT_RSA_PRIVATE_FORMAT_XML:
1342 case CRYPT_RSA_PUBLIC_FORMAT_XML:
1343 $this->components = array();
1345 $xml = xml_parser_create('UTF-8');
1346 xml_set_object($xml, $this);
1347 xml_set_element_handler($xml, '_start_element_handler', '_stop_element_handler');
1348 xml_set_character_data_handler($xml, '_data_handler');
1349 // add <xml></xml> to account for "dangling" tags like <BitStrength>...</BitStrength> that are sometimes added
1350 if (!xml_parse($xml, '<xml>' . $key . '</xml>')) {
1354 return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false;
1355 // from PuTTY's SSHPUBK.C
1356 case CRYPT_RSA_PRIVATE_FORMAT_PUTTY:
1357 $components = array();
1358 $key = preg_split('#\r\n|\r|\n#', $key);
1359 $type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0]));
1360 if ($type != 'ssh-rsa') {
1363 $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1]));
1364 $comment = trim(preg_replace('#Comment: (.+)#', '$1', $key[2]));
1366 $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3]));
1367 $public = base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength))));
1368 $public = substr($public, 11);
1369 extract(unpack('Nlength', $this->_string_shift($public, 4)));
1370 $components['publicExponent'] = new Math_BigInteger($this->_string_shift($public, $length), -256);
1371 extract(unpack('Nlength', $this->_string_shift($public, 4)));
1372 $components['modulus'] = new Math_BigInteger($this->_string_shift($public, $length), -256);
1374 $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$publicLength + 4]));
1375 $private = base64_decode(implode('', array_map('trim', array_slice($key, $publicLength + 5, $privateLength))));
1377 switch ($encryption) {
1379 if (!class_exists('Crypt_AES')) {
1380 include_once 'Crypt/AES.php';
1384 while (strlen($symkey) < 32) {
1385 $temp = pack('Na*', $sequence++, $this->password);
1386 $symkey.= pack('H*', sha1($temp));
1388 $symkey = substr($symkey, 0, 32);
1389 $crypto = new Crypt_AES();
1392 if ($encryption != 'none') {
1393 $crypto->setKey($symkey);
1394 $crypto->disablePadding();
1395 $private = $crypto->decrypt($private);
1396 if ($private === false) {
1401 extract(unpack('Nlength', $this->_string_shift($private, 4)));
1402 if (strlen($private) < $length) {
1405 $components['privateExponent'] = new Math_BigInteger($this->_string_shift($private, $length), -256);
1406 extract(unpack('Nlength', $this->_string_shift($private, 4)));
1407 if (strlen($private) < $length) {
1410 $components['primes'] = array(1 => new Math_BigInteger($this->_string_shift($private, $length), -256));
1411 extract(unpack('Nlength', $this->_string_shift($private, 4)));
1412 if (strlen($private) < $length) {
1415 $components['primes'][] = new Math_BigInteger($this->_string_shift($private, $length), -256);
1417 $temp = $components['primes'][1]->subtract($this->one);
1418 $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp));
1419 $temp = $components['primes'][2]->subtract($this->one);
1420 $components['exponents'][] = $components['publicExponent']->modInverse($temp);
1422 extract(unpack('Nlength', $this->_string_shift($private, 4)));
1423 if (strlen($private) < $length) {
1426 $components['coefficients'] = array(2 => new Math_BigInteger($this->_string_shift($private, $length), -256));
1433 * Returns the key size
1435 * More specifically, this returns the size of the modulo in bits.
1442 return !isset($this->modulus) ? 0 : strlen($this->modulus->toBits());
1446 * Start Element Handler
1448 * Called by xml_set_element_handler()
1451 * @param Resource $parser
1452 * @param String $name
1453 * @param Array $attribs
1455 function _start_element_handler($parser, $name, $attribs)
1457 //$name = strtoupper($name);
1460 $this->current = &$this->components['modulus'];
1463 $this->current = &$this->components['publicExponent'];
1466 $this->current = &$this->components['primes'][1];
1469 $this->current = &$this->components['primes'][2];
1472 $this->current = &$this->components['exponents'][1];
1475 $this->current = &$this->components['exponents'][2];
1478 $this->current = &$this->components['coefficients'][2];
1481 $this->current = &$this->components['privateExponent'];
1483 $this->current = '';
1487 * Stop Element Handler
1489 * Called by xml_set_element_handler()
1492 * @param Resource $parser
1493 * @param String $name
1495 function _stop_element_handler($parser, $name)
1497 if (isset($this->current)) {
1498 $this->current = new Math_BigInteger(base64_decode($this->current), 256);
1499 unset($this->current);
1506 * Called by xml_set_character_data_handler()
1509 * @param Resource $parser
1510 * @param String $data
1512 function _data_handler($parser, $data)
1514 if (!isset($this->current) || is_object($this->current)) {
1517 $this->current.= trim($data);
1521 * Loads a public or private key
1523 * Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed)
1526 * @param String $key
1527 * @param Integer $type optional
1529 function loadKey($key, $type = false)
1531 if (is_object($key) && strtolower(get_class($key)) == 'crypt_rsa') {
1532 $this->privateKeyFormat = $key->privateKeyFormat;
1533 $this->publicKeyFormat = $key->publicKeyFormat;
1535 $this->hLen = $key->hLen;
1536 $this->sLen = $key->sLen;
1537 $this->mgfHLen = $key->mgfHLen;
1538 $this->encryptionMode = $key->encryptionMode;
1539 $this->signatureMode = $key->signatureMode;
1540 $this->password = $key->password;
1541 $this->configFile = $key->configFile;
1542 $this->comment = $key->comment;
1544 if (is_object($key->hash)) {
1545 $this->hash = new Crypt_Hash($key->hash->getHash());
1547 if (is_object($key->mgfHash)) {
1548 $this->mgfHash = new Crypt_Hash($key->mgfHash->getHash());
1551 if (is_object($key->modulus)) {
1552 $this->modulus = $key->modulus->copy();
1554 if (is_object($key->exponent)) {
1555 $this->exponent = $key->exponent->copy();
1557 if (is_object($key->publicExponent)) {
1558 $this->publicExponent = $key->publicExponent->copy();
1561 $this->primes = array();
1562 $this->exponents = array();
1563 $this->coefficients = array();
1565 foreach ($this->primes as $prime) {
1566 $this->primes[] = $prime->copy();
1568 foreach ($this->exponents as $exponent) {
1569 $this->exponents[] = $exponent->copy();
1571 foreach ($this->coefficients as $coefficient) {
1572 $this->coefficients[] = $coefficient->copy();
1578 if ($type === false) {
1580 CRYPT_RSA_PUBLIC_FORMAT_RAW,
1581 CRYPT_RSA_PRIVATE_FORMAT_PKCS1,
1582 CRYPT_RSA_PRIVATE_FORMAT_XML,
1583 CRYPT_RSA_PRIVATE_FORMAT_PUTTY,
1584 CRYPT_RSA_PUBLIC_FORMAT_OPENSSH
1586 foreach ($types as $type) {
1587 $components = $this->_parseKey($key, $type);
1588 if ($components !== false) {
1594 $components = $this->_parseKey($key, $type);
1597 if ($components === false) {
1601 if (isset($components['comment']) && $components['comment'] !== false) {
1602 $this->comment = $components['comment'];
1604 $this->modulus = $components['modulus'];
1605 $this->k = strlen($this->modulus->toBytes());
1606 $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent'];
1607 if (isset($components['primes'])) {
1608 $this->primes = $components['primes'];
1609 $this->exponents = $components['exponents'];
1610 $this->coefficients = $components['coefficients'];
1611 $this->publicExponent = $components['publicExponent'];
1613 $this->primes = array();
1614 $this->exponents = array();
1615 $this->coefficients = array();
1616 $this->publicExponent = false;
1620 case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
1621 case CRYPT_RSA_PUBLIC_FORMAT_RAW:
1622 $this->setPublicKey();
1624 case CRYPT_RSA_PRIVATE_FORMAT_PKCS1:
1626 case strpos($key, '-BEGIN PUBLIC KEY-') !== false:
1627 case strpos($key, '-BEGIN RSA PUBLIC KEY-') !== false:
1628 $this->setPublicKey();
1638 * Private keys can be encrypted with a password. To unset the password, pass in the empty string or false.
1639 * Or rather, pass in $password such that empty($password) && !is_string($password) is true.
1644 * @param String $password
1646 function setPassword($password = false)
1648 $this->password = $password;
1652 * Defines the public key
1654 * Some private key formats define the public exponent and some don't. Those that don't define it are problematic when
1655 * used in certain contexts. For example, in SSH-2, RSA authentication works by sending the public key along with a
1656 * message signed by the private key to the server. The SSH-2 server looks the public key up in an index of public keys
1657 * and if it's present then proceeds to verify the signature. Problem is, if your private key doesn't include the public
1658 * exponent this won't work unless you manually add the public exponent. phpseclib tries to guess if the key being used
1659 * is the public key but in the event that it guesses incorrectly you might still want to explicitly set the key as being
1662 * Do note that when a new key is loaded the index will be cleared.
1664 * Returns true on success, false on failure
1666 * @see getPublicKey()
1668 * @param String $key optional
1669 * @param Integer $type optional
1672 function setPublicKey($key = false, $type = false)
1674 // if a public key has already been loaded return false
1675 if (!empty($this->publicExponent)) {
1679 if ($key === false && !empty($this->modulus)) {
1680 $this->publicExponent = $this->exponent;
1684 if ($type === false) {
1686 CRYPT_RSA_PUBLIC_FORMAT_RAW,
1687 CRYPT_RSA_PUBLIC_FORMAT_PKCS1,
1688 CRYPT_RSA_PUBLIC_FORMAT_XML,
1689 CRYPT_RSA_PUBLIC_FORMAT_OPENSSH
1691 foreach ($types as $type) {
1692 $components = $this->_parseKey($key, $type);
1693 if ($components !== false) {
1698 $components = $this->_parseKey($key, $type);
1701 if ($components === false) {
1705 if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) {
1706 $this->modulus = $components['modulus'];
1707 $this->exponent = $this->publicExponent = $components['publicExponent'];
1711 $this->publicExponent = $components['publicExponent'];
1717 * Defines the private key
1719 * If phpseclib guessed a private key was a public key and loaded it as such it might be desirable to force
1720 * phpseclib to treat the key as a private key. This function will do that.
1722 * Do note that when a new key is loaded the index will be cleared.
1724 * Returns true on success, false on failure
1726 * @see getPublicKey()
1728 * @param String $key optional
1729 * @param Integer $type optional
1732 function setPrivateKey($key = false, $type = false)
1734 if ($key === false && !empty($this->publicExponent)) {
1735 unset($this->publicExponent);
1739 $rsa = new Crypt_RSA();
1740 if (!$rsa->loadKey($key, $type)) {
1743 unset($rsa->publicExponent);
1745 // don't overwrite the old key if the new key is invalid
1746 $this->loadKey($rsa);
1751 * Returns the public key
1753 * The public key is only returned under two circumstances - if the private key had the public key embedded within it
1754 * or if the public key was set via setPublicKey(). If the currently loaded key is supposed to be the public key this
1755 * function won't return it since this library, for the most part, doesn't distinguish between public and private keys.
1757 * @see getPublicKey()
1759 * @param String $key
1760 * @param Integer $type optional
1762 function getPublicKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS8)
1764 if (empty($this->modulus) || empty($this->publicExponent)) {
1768 $oldFormat = $this->publicKeyFormat;
1769 $this->publicKeyFormat = $type;
1770 $temp = $this->_convertPublicKey($this->modulus, $this->publicExponent);
1771 $this->publicKeyFormat = $oldFormat;
1776 * Returns the private key
1778 * The private key is only returned if the currently loaded key contains the constituent prime numbers.
1780 * @see getPublicKey()
1782 * @param String $key
1783 * @param Integer $type optional
1785 function getPrivateKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS1)
1787 if (empty($this->primes)) {
1791 $oldFormat = $this->privateKeyFormat;
1792 $this->privateKeyFormat = $type;
1793 $temp = $this->_convertPrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients);
1794 $this->privateKeyFormat = $oldFormat;
1799 * Returns a minimalistic private key
1801 * Returns the private key without the prime number constituants. Structurally identical to a public key that
1802 * hasn't been set as the public key
1804 * @see getPrivateKey()
1806 * @param String $key
1807 * @param Integer $type optional
1809 function _getPrivatePublicKey($mode = CRYPT_RSA_PUBLIC_FORMAT_PKCS8)
1811 if (empty($this->modulus) || empty($this->exponent)) {
1815 $oldFormat = $this->publicKeyFormat;
1816 $this->publicKeyFormat = $mode;
1817 $temp = $this->_convertPublicKey($this->modulus, $this->exponent);
1818 $this->publicKeyFormat = $oldFormat;
1823 * __toString() magic method
1827 function __toString()
1829 $key = $this->getPrivateKey($this->privateKeyFormat);
1830 if ($key !== false) {
1833 $key = $this->_getPrivatePublicKey($this->publicKeyFormat);
1834 return $key !== false ? $key : '';
1838 * __clone() magic method
1844 $key = new Crypt_RSA();
1845 $key->loadKey($this);
1850 * Generates the smallest and largest numbers requiring $bits bits
1853 * @param Integer $bits
1856 function _generateMinMax($bits)
1858 $bytes = $bits >> 3;
1859 $min = str_repeat(chr(0), $bytes);
1860 $max = str_repeat(chr(0xFF), $bytes);
1863 $min = chr(1 << ($msb - 1)) . $min;
1864 $max = chr((1 << $msb) - 1) . $max;
1866 $min[0] = chr(0x80);
1870 'min' => new Math_BigInteger($min, 256),
1871 'max' => new Math_BigInteger($max, 256)
1876 * DER-decode the length
1878 * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
1879 * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
1882 * @param String $string
1885 function _decodeLength(&$string)
1887 $length = ord($this->_string_shift($string));
1888 if ( $length & 0x80 ) { // definite length, long form
1890 $temp = $this->_string_shift($string, $length);
1891 list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4));
1897 * DER-encode the length
1899 * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
1900 * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
1903 * @param Integer $length
1906 function _encodeLength($length)
1908 if ($length <= 0x7F) {
1909 return chr($length);
1912 $temp = ltrim(pack('N', $length), chr(0));
1913 return pack('Ca*', 0x80 | strlen($temp), $temp);
1919 * Inspired by array_shift
1921 * @param String $string
1922 * @param optional Integer $index
1926 function _string_shift(&$string, $index = 1)
1928 $substr = substr($string, 0, $index);
1929 $string = substr($string, $index);
1934 * Determines the private key format
1938 * @param Integer $format
1940 function setPrivateKeyFormat($format)
1942 $this->privateKeyFormat = $format;
1946 * Determines the public key format
1950 * @param Integer $format
1952 function setPublicKeyFormat($format)
1954 $this->publicKeyFormat = $format;
1958 * Determines which hashing function should be used
1960 * Used with signature production / verification and (if the encryption mode is CRYPT_RSA_ENCRYPTION_OAEP) encryption and
1961 * decryption. If $hash isn't supported, sha1 is used.
1964 * @param String $hash
1966 function setHash($hash)
1968 // Crypt_Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example.
1976 $this->hash = new Crypt_Hash($hash);
1977 $this->hashName = $hash;
1980 $this->hash = new Crypt_Hash('sha1');
1981 $this->hashName = 'sha1';
1983 $this->hLen = $this->hash->getLength();
1987 * Determines which hashing function should be used for the mask generation function
1989 * The mask generation function is used by CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_SIGNATURE_PSS and although it's
1990 * best if Hash and MGFHash are set to the same thing this is not a requirement.
1993 * @param String $hash
1995 function setMGFHash($hash)
1997 // Crypt_Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example.
2005 $this->mgfHash = new Crypt_Hash($hash);
2008 $this->mgfHash = new Crypt_Hash('sha1');
2010 $this->mgfHLen = $this->mgfHash->getLength();
2014 * Determines the salt length
2016 * To quote from {@link http://tools.ietf.org/html/rfc3447#page-38 RFC3447#page-38}:
2018 * Typical salt lengths in octets are hLen (the length of the output
2019 * of the hash function Hash) and 0.
2022 * @param Integer $format
2024 function setSaltLength($sLen)
2026 $this->sLen = $sLen;
2030 * Integer-to-Octet-String primitive
2032 * See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}.
2035 * @param Math_BigInteger $x
2036 * @param Integer $xLen
2039 function _i2osp($x, $xLen)
2042 if (strlen($x) > $xLen) {
2043 user_error('Integer too large');
2046 return str_pad($x, $xLen, chr(0), STR_PAD_LEFT);
2050 * Octet-String-to-Integer primitive
2052 * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}.
2056 * @return Math_BigInteger
2060 return new Math_BigInteger($x, 256);
2064 * Exponentiate with or without Chinese Remainder Theorem
2066 * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.2}.
2069 * @param Math_BigInteger $x
2070 * @return Math_BigInteger
2072 function _exponentiate($x)
2074 if (empty($this->primes) || empty($this->coefficients) || empty($this->exponents)) {
2075 return $x->modPow($this->exponent, $this->modulus);
2078 $num_primes = count($this->primes);
2080 if (defined('CRYPT_RSA_DISABLE_BLINDING')) {
2082 1 => $x->modPow($this->exponents[1], $this->primes[1]),
2083 2 => $x->modPow($this->exponents[2], $this->primes[2])
2085 $h = $m_i[1]->subtract($m_i[2]);
2086 $h = $h->multiply($this->coefficients[2]);
2087 list(, $h) = $h->divide($this->primes[1]);
2088 $m = $m_i[2]->add($h->multiply($this->primes[2]));
2090 $r = $this->primes[1];
2091 for ($i = 3; $i <= $num_primes; $i++) {
2092 $m_i = $x->modPow($this->exponents[$i], $this->primes[$i]);
2094 $r = $r->multiply($this->primes[$i - 1]);
2096 $h = $m_i->subtract($m);
2097 $h = $h->multiply($this->coefficients[$i]);
2098 list(, $h) = $h->divide($this->primes[$i]);
2100 $m = $m->add($r->multiply($h));
2103 $smallest = $this->primes[1];
2104 for ($i = 2; $i <= $num_primes; $i++) {
2105 if ($smallest->compare($this->primes[$i]) > 0) {
2106 $smallest = $this->primes[$i];
2110 $one = new Math_BigInteger(1);
2112 $r = $one->random($one, $smallest->subtract($one));
2115 1 => $this->_blind($x, $r, 1),
2116 2 => $this->_blind($x, $r, 2)
2118 $h = $m_i[1]->subtract($m_i[2]);
2119 $h = $h->multiply($this->coefficients[2]);
2120 list(, $h) = $h->divide($this->primes[1]);
2121 $m = $m_i[2]->add($h->multiply($this->primes[2]));
2123 $r = $this->primes[1];
2124 for ($i = 3; $i <= $num_primes; $i++) {
2125 $m_i = $this->_blind($x, $r, $i);
2127 $r = $r->multiply($this->primes[$i - 1]);
2129 $h = $m_i->subtract($m);
2130 $h = $h->multiply($this->coefficients[$i]);
2131 list(, $h) = $h->divide($this->primes[$i]);
2133 $m = $m->add($r->multiply($h));
2141 * Performs RSA Blinding
2143 * Protects against timing attacks by employing RSA Blinding.
2144 * Returns $x->modPow($this->exponents[$i], $this->primes[$i])
2147 * @param Math_BigInteger $x
2148 * @param Math_BigInteger $r
2150 * @return Math_BigInteger
2152 function _blind($x, $r, $i)
2154 $x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i]));
2155 $x = $x->modPow($this->exponents[$i], $this->primes[$i]);
2157 $r = $r->modInverse($this->primes[$i]);
2158 $x = $x->multiply($r);
2159 list(, $x) = $x->divide($this->primes[$i]);
2165 * Performs blinded RSA equality testing
2167 * Protects against a particular type of timing attack described.
2169 * See {@link http://codahale.com/a-lesson-in-timing-attacks/ A Lesson In Timing Attacks (or, Don't use MessageDigest.isEquals)}
2171 * Thanks for the heads up singpolyma!
2178 function _equals($x, $y)
2180 if (strlen($x) != strlen($y)) {
2185 for ($i = 0; $i < strlen($x); $i++) {
2186 $result |= ord($x[$i]) ^ ord($y[$i]);
2189 return $result == 0;
2195 * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}.
2198 * @param Math_BigInteger $m
2199 * @return Math_BigInteger
2203 if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
2204 user_error('Message representative out of range');
2207 return $this->_exponentiate($m);
2213 * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}.
2216 * @param Math_BigInteger $c
2217 * @return Math_BigInteger
2221 if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) {
2222 user_error('Ciphertext representative out of range');
2225 return $this->_exponentiate($c);
2231 * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}.
2234 * @param Math_BigInteger $m
2235 * @return Math_BigInteger
2237 function _rsasp1($m)
2239 if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
2240 user_error('Message representative out of range');
2243 return $this->_exponentiate($m);
2249 * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}.
2252 * @param Math_BigInteger $s
2253 * @return Math_BigInteger
2255 function _rsavp1($s)
2257 if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) {
2258 user_error('Signature representative out of range');
2261 return $this->_exponentiate($s);
2267 * See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}.
2270 * @param String $mgfSeed
2271 * @param Integer $mgfLen
2274 function _mgf1($mgfSeed, $maskLen)
2276 // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output.
2279 $count = ceil($maskLen / $this->mgfHLen);
2280 for ($i = 0; $i < $count; $i++) {
2282 $t.= $this->mgfHash->hash($mgfSeed . $c);
2285 return substr($t, 0, $maskLen);
2289 * RSAES-OAEP-ENCRYPT
2291 * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1 RFC3447#section-7.1.1} and
2292 * {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}.
2299 function _rsaes_oaep_encrypt($m, $l = '')
2305 // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
2308 if ($mLen > $this->k - 2 * $this->hLen - 2) {
2309 user_error('Message too long');
2313 // EME-OAEP encoding
2315 $lHash = $this->hash->hash($l);
2316 $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2);
2317 $db = $lHash . $ps . chr(1) . $m;
2318 $seed = crypt_random_string($this->hLen);
2319 $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
2320 $maskedDB = $db ^ $dbMask;
2321 $seedMask = $this->_mgf1($maskedDB, $this->hLen);
2322 $maskedSeed = $seed ^ $seedMask;
2323 $em = chr(0) . $maskedSeed . $maskedDB;
2327 $m = $this->_os2ip($em);
2328 $c = $this->_rsaep($m);
2329 $c = $this->_i2osp($c, $this->k);
2331 // Output the ciphertext C
2337 * RSAES-OAEP-DECRYPT
2339 * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}. The fact that the error
2340 * messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2:
2342 * Note. Care must be taken to ensure that an opponent cannot
2343 * distinguish the different error conditions in Step 3.g, whether by
2344 * error message or timing, or, more generally, learn partial
2345 * information about the encoded message EM. Otherwise an opponent may
2346 * be able to obtain useful information about the decryption of the
2347 * ciphertext C, leading to a chosen-ciphertext attack such as the one
2348 * observed by Manger [36].
2350 * As for $l... to quote from {@link http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}:
2352 * Both the encryption and the decryption operations of RSAES-OAEP take
2353 * the value of a label L as input. In this version of PKCS #1, L is
2354 * the empty string; other uses of the label are outside the scope of
2362 function _rsaes_oaep_decrypt($c, $l = '')
2366 // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
2369 if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) {
2370 user_error('Decryption error');
2376 $c = $this->_os2ip($c);
2377 $m = $this->_rsadp($c);
2379 user_error('Decryption error');
2382 $em = $this->_i2osp($m, $this->k);
2384 // EME-OAEP decoding
2386 $lHash = $this->hash->hash($l);
2388 $maskedSeed = substr($em, 1, $this->hLen);
2389 $maskedDB = substr($em, $this->hLen + 1);
2390 $seedMask = $this->_mgf1($maskedDB, $this->hLen);
2391 $seed = $maskedSeed ^ $seedMask;
2392 $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
2393 $db = $maskedDB ^ $dbMask;
2394 $lHash2 = substr($db, 0, $this->hLen);
2395 $m = substr($db, $this->hLen);
2396 if ($lHash != $lHash2) {
2397 user_error('Decryption error');
2400 $m = ltrim($m, chr(0));
2401 if (ord($m[0]) != 1) {
2402 user_error('Decryption error');
2406 // Output the message M
2408 return substr($m, 1);
2412 * RSAES-PKCS1-V1_5-ENCRYPT
2414 * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}.
2420 function _rsaes_pkcs1_v1_5_encrypt($m)
2426 if ($mLen > $this->k - 11) {
2427 user_error('Message too long');
2431 // EME-PKCS1-v1_5 encoding
2433 $psLen = $this->k - $mLen - 3;
2435 while (strlen($ps) != $psLen) {
2436 $temp = crypt_random_string($psLen - strlen($ps));
2437 $temp = str_replace("\x00", '', $temp);
2441 // see the comments of _rsaes_pkcs1_v1_5_decrypt() to understand why this is being done
2442 if (defined('CRYPT_RSA_PKCS15_COMPAT') && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) {
2444 // "The padding string PS shall consist of k-3-||D|| octets. ... for block type 01, they shall have value FF"
2445 $ps = str_repeat("\xFF", $psLen);
2447 $em = chr(0) . chr($type) . $ps . chr(0) . $m;
2450 $m = $this->_os2ip($em);
2451 $c = $this->_rsaep($m);
2452 $c = $this->_i2osp($c, $this->k);
2454 // Output the ciphertext C
2460 * RSAES-PKCS1-V1_5-DECRYPT
2462 * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}.
2464 * For compatibility purposes, this function departs slightly from the description given in RFC3447.
2465 * The reason being that RFC2313#section-8.1 (PKCS#1 v1.5) states that ciphertext's encrypted by the
2466 * private key should have the second byte set to either 0 or 1 and that ciphertext's encrypted by the
2467 * public key should have the second byte set to 2. In RFC3447 (PKCS#1 v2.1), the second byte is supposed
2468 * to be 2 regardless of which key is used. For compatibility purposes, we'll just check to make sure the
2469 * second byte is 2 or less. If it is, we'll accept the decrypted string as valid.
2471 * As a consequence of this, a private key encrypted ciphertext produced with Crypt_RSA may not decrypt
2472 * with a strictly PKCS#1 v1.5 compliant RSA implementation. Public key encrypted ciphertext's should but
2473 * not private key encrypted ciphertext's.
2479 function _rsaes_pkcs1_v1_5_decrypt($c)
2483 if (strlen($c) != $this->k) { // or if k < 11
2484 user_error('Decryption error');
2490 $c = $this->_os2ip($c);
2491 $m = $this->_rsadp($c);
2494 user_error('Decryption error');
2497 $em = $this->_i2osp($m, $this->k);
2499 // EME-PKCS1-v1_5 decoding
2501 if (ord($em[0]) != 0 || ord($em[1]) > 2) {
2502 user_error('Decryption error');
2506 $ps = substr($em, 2, strpos($em, chr(0), 2) - 2);
2507 $m = substr($em, strlen($ps) + 3);
2509 if (strlen($ps) < 8) {
2510 user_error('Decryption error');
2522 * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}.
2526 * @param Integer $emBits
2528 function _emsa_pss_encode($m, $emBits)
2530 // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
2533 $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8)
2534 $sLen = $this->sLen == false ? $this->hLen : $this->sLen;
2536 $mHash = $this->hash->hash($m);
2537 if ($emLen < $this->hLen + $sLen + 2) {
2538 user_error('Encoding error');
2542 $salt = crypt_random_string($sLen);
2543 $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
2544 $h = $this->hash->hash($m2);
2545 $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2);
2546 $db = $ps . chr(1) . $salt;
2547 $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
2548 $maskedDB = $db ^ $dbMask;
2549 $maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0];
2550 $em = $maskedDB . $h . chr(0xBC);
2558 * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}.
2563 * @param Integer $emBits
2566 function _emsa_pss_verify($m, $em, $emBits)
2568 // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
2571 $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8);
2572 $sLen = $this->sLen == false ? $this->hLen : $this->sLen;
2574 $mHash = $this->hash->hash($m);
2575 if ($emLen < $this->hLen + $sLen + 2) {
2579 if ($em[strlen($em) - 1] != chr(0xBC)) {
2583 $maskedDB = substr($em, 0, -$this->hLen - 1);
2584 $h = substr($em, -$this->hLen - 1, $this->hLen);
2585 $temp = chr(0xFF << ($emBits & 7));
2586 if ((~$maskedDB[0] & $temp) != $temp) {
2589 $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
2590 $db = $maskedDB ^ $dbMask;
2591 $db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
2592 $temp = $emLen - $this->hLen - $sLen - 2;
2593 if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) {
2596 $salt = substr($db, $temp + 1); // should be $sLen long
2597 $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
2598 $h2 = $this->hash->hash($m2);
2599 return $this->_equals($h, $h2);
2605 * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}.
2611 function _rsassa_pss_sign($m)
2613 // EMSA-PSS encoding
2615 $em = $this->_emsa_pss_encode($m, 8 * $this->k - 1);
2619 $m = $this->_os2ip($em);
2620 $s = $this->_rsasp1($m);
2621 $s = $this->_i2osp($s, $this->k);
2623 // Output the signature S
2631 * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}.
2638 function _rsassa_pss_verify($m, $s)
2642 if (strlen($s) != $this->k) {
2643 user_error('Invalid signature');
2649 $modBits = 8 * $this->k;
2651 $s2 = $this->_os2ip($s);
2652 $m2 = $this->_rsavp1($s2);
2653 if ($m2 === false) {
2654 user_error('Invalid signature');
2657 $em = $this->_i2osp($m2, $modBits >> 3);
2658 if ($em === false) {
2659 user_error('Invalid signature');
2663 // EMSA-PSS verification
2665 return $this->_emsa_pss_verify($m, $em, $modBits - 1);
2669 * EMSA-PKCS1-V1_5-ENCODE
2671 * See {@link http://tools.ietf.org/html/rfc3447#section-9.2 RFC3447#section-9.2}.
2675 * @param Integer $emLen
2678 function _emsa_pkcs1_v1_5_encode($m, $emLen)
2680 $h = $this->hash->hash($m);
2685 // see http://tools.ietf.org/html/rfc3447#page-43
2686 switch ($this->hashName) {
2688 $t = pack('H*', '3020300c06082a864886f70d020205000410');
2691 $t = pack('H*', '3020300c06082a864886f70d020505000410');
2694 $t = pack('H*', '3021300906052b0e03021a05000414');
2697 $t = pack('H*', '3031300d060960864801650304020105000420');
2700 $t = pack('H*', '3041300d060960864801650304020205000430');
2703 $t = pack('H*', '3051300d060960864801650304020305000440');
2708 if ($emLen < $tLen + 11) {
2709 user_error('Intended encoded message length too short');
2713 $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
2715 $em = "\0\1$ps\0$t";
2721 * RSASSA-PKCS1-V1_5-SIGN
2723 * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}.
2729 function _rsassa_pkcs1_v1_5_sign($m)
2731 // EMSA-PKCS1-v1_5 encoding
2733 $em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
2734 if ($em === false) {
2735 user_error('RSA modulus too short');
2741 $m = $this->_os2ip($em);
2742 $s = $this->_rsasp1($m);
2743 $s = $this->_i2osp($s, $this->k);
2745 // Output the signature S
2751 * RSASSA-PKCS1-V1_5-VERIFY
2753 * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}.
2759 function _rsassa_pkcs1_v1_5_verify($m, $s)
2763 if (strlen($s) != $this->k) {
2764 user_error('Invalid signature');
2770 $s = $this->_os2ip($s);
2771 $m2 = $this->_rsavp1($s);
2772 if ($m2 === false) {
2773 user_error('Invalid signature');
2776 $em = $this->_i2osp($m2, $this->k);
2777 if ($em === false) {
2778 user_error('Invalid signature');
2782 // EMSA-PKCS1-v1_5 encoding
2784 $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
2785 if ($em2 === false) {
2786 user_error('RSA modulus too short');
2791 return $this->_equals($em, $em2);
2795 * Set Encryption Mode
2797 * Valid values include CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_ENCRYPTION_PKCS1.
2800 * @param Integer $mode
2802 function setEncryptionMode($mode)
2804 $this->encryptionMode = $mode;
2808 * Set Signature Mode
2810 * Valid values include CRYPT_RSA_SIGNATURE_PSS and CRYPT_RSA_SIGNATURE_PKCS1
2813 * @param Integer $mode
2815 function setSignatureMode($mode)
2817 $this->signatureMode = $mode;
2821 * Set public key comment.
2824 * @param String $comment
2826 function setComment($comment)
2828 $this->comment = $comment;
2832 * Get public key comment.
2837 function getComment()
2839 return $this->comment;
2845 * Both CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_ENCRYPTION_PKCS1 both place limits on how long $plaintext can be.
2846 * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will
2847 * be concatenated together.
2851 * @param String $plaintext
2854 function encrypt($plaintext)
2856 switch ($this->encryptionMode) {
2857 case CRYPT_RSA_ENCRYPTION_PKCS1:
2858 $length = $this->k - 11;
2863 $plaintext = str_split($plaintext, $length);
2865 foreach ($plaintext as $m) {
2866 $ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m);
2869 //case CRYPT_RSA_ENCRYPTION_OAEP:
2871 $length = $this->k - 2 * $this->hLen - 2;
2876 $plaintext = str_split($plaintext, $length);
2878 foreach ($plaintext as $m) {
2879 $ciphertext.= $this->_rsaes_oaep_encrypt($m);
2890 * @param String $plaintext
2893 function decrypt($ciphertext)
2895 if ($this->k <= 0) {
2899 $ciphertext = str_split($ciphertext, $this->k);
2900 $ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], $this->k, chr(0), STR_PAD_LEFT);
2904 switch ($this->encryptionMode) {
2905 case CRYPT_RSA_ENCRYPTION_PKCS1:
2906 $decrypt = '_rsaes_pkcs1_v1_5_decrypt';
2908 //case CRYPT_RSA_ENCRYPTION_OAEP:
2910 $decrypt = '_rsaes_oaep_decrypt';
2913 foreach ($ciphertext as $c) {
2914 $temp = $this->$decrypt($c);
2915 if ($temp === false) {
2925 * Create a signature
2929 * @param String $message
2932 function sign($message)
2934 if (empty($this->modulus) || empty($this->exponent)) {
2938 switch ($this->signatureMode) {
2939 case CRYPT_RSA_SIGNATURE_PKCS1:
2940 return $this->_rsassa_pkcs1_v1_5_sign($message);
2941 //case CRYPT_RSA_SIGNATURE_PSS:
2943 return $this->_rsassa_pss_sign($message);
2948 * Verifies a signature
2952 * @param String $message
2953 * @param String $signature
2956 function verify($message, $signature)
2958 if (empty($this->modulus) || empty($this->exponent)) {
2962 switch ($this->signatureMode) {
2963 case CRYPT_RSA_SIGNATURE_PKCS1:
2964 return $this->_rsassa_pkcs1_v1_5_verify($message, $signature);
2965 //case CRYPT_RSA_SIGNATURE_PSS:
2967 return $this->_rsassa_pss_verify($message, $signature);
2972 * Extract raw BER from Base64 encoding
2975 * @param String $str
2978 function _extractBER($str)
2980 /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them
2981 * above and beyond the ceritificate.
2982 * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line:
2985 * localKeyID: 01 00 00 00
2986 * subject=/O=organization/OU=org unit/CN=common name
2987 * issuer=/O=organization/CN=common name
2989 $temp = preg_replace('#.*?^-+[^-]+-+#ms', '', $str, 1);
2990 // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff
2991 $temp = preg_replace('#-+[^-]+-+#', '', $temp);
2993 $temp = str_replace(array("\r", "\n", ' '), '', $temp);
2994 $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
2995 return $temp != false ? $temp : $str;