forked from GNUsocial/gnu-social
		
	
		
			
	
	
		
			314 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
		
		
			
		
	
	
			314 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| 
								 | 
							
								<?php
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * PuTTY Formatted RSA Key Handler
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * PHP version 5
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @category  Crypt
							 | 
						||
| 
								 | 
							
								 * @package   RSA
							 | 
						||
| 
								 | 
							
								 * @author    Jim Wigginton <terrafrost@php.net>
							 | 
						||
| 
								 | 
							
								 * @copyright 2015 Jim Wigginton
							 | 
						||
| 
								 | 
							
								 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
							 | 
						||
| 
								 | 
							
								 * @link      http://phpseclib.sourceforge.net
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								namespace phpseclib\Crypt\RSA;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								use ParagonIE\ConstantTime\Base64;
							 | 
						||
| 
								 | 
							
								use ParagonIE\ConstantTime\Hex;
							 | 
						||
| 
								 | 
							
								use phpseclib\Crypt\AES;
							 | 
						||
| 
								 | 
							
								use phpseclib\Crypt\Hash;
							 | 
						||
| 
								 | 
							
								use phpseclib\Math\BigInteger;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * PuTTY Formatted RSA Key Handler
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @package RSA
							 | 
						||
| 
								 | 
							
								 * @author  Jim Wigginton <terrafrost@php.net>
							 | 
						||
| 
								 | 
							
								 * @access  public
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								class PuTTY
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Default comment
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @var string
							 | 
						||
| 
								 | 
							
								     * @access private
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    static $comment = 'phpseclib-generated-key';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Sets the default comment
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @access public
							 | 
						||
| 
								 | 
							
								     * @param string $comment
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    static function setComment($comment)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        self::$comment = str_replace(array("\r", "\n"), '', $comment);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Generate a symmetric key for PuTTY keys
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @access public
							 | 
						||
| 
								 | 
							
								     * @param string $password
							 | 
						||
| 
								 | 
							
								     * @param string $iv
							 | 
						||
| 
								 | 
							
								     * @param int $length
							 | 
						||
| 
								 | 
							
								     * @return string
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    static function generateSymmetricKey($password, $length)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        $symkey = '';
							 | 
						||
| 
								 | 
							
								        $sequence = 0;
							 | 
						||
| 
								 | 
							
								        while (strlen($symkey) < $length) {
							 | 
						||
| 
								 | 
							
								            $temp = pack('Na*', $sequence++, $password);
							 | 
						||
| 
								 | 
							
								            $symkey.= Hex::decode(sha1($temp));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return substr($symkey, 0, $length);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Break a public or private key down into its constituent components
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @access public
							 | 
						||
| 
								 | 
							
								     * @param string $key
							 | 
						||
| 
								 | 
							
								     * @param string $password optional
							 | 
						||
| 
								 | 
							
								     * @return array
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    static function load($key, $password = '')
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        if (!is_string($key)) {
							 | 
						||
| 
								 | 
							
								            return false;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        static $one;
							 | 
						||
| 
								 | 
							
								        if (!isset($one)) {
							 | 
						||
| 
								 | 
							
								            $one = new BigInteger(1);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (strpos($key, 'BEGIN SSH2 PUBLIC KEY')) {
							 | 
						||
| 
								 | 
							
								            $data = preg_split('#[\r\n]+#', $key);
							 | 
						||
| 
								 | 
							
								            $data = array_splice($data, 2, -1);
							 | 
						||
| 
								 | 
							
								            $data = implode('', $data);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            $components = OpenSSH::load($data);
							 | 
						||
| 
								 | 
							
								            if ($components === false) {
							 | 
						||
| 
								 | 
							
								                return false;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (!preg_match('#Comment: "(.+)"#', $key, $matches)) {
							 | 
						||
| 
								 | 
							
								                return false;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            $components['comment'] = str_replace(array('\\\\', '\"'), array('\\', '"'), $matches[1]);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            return $components;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        $components = array('isPublicKey' => false);
							 | 
						||
| 
								 | 
							
								        $key = preg_split('#\r\n|\r|\n#', $key);
							 | 
						||
| 
								 | 
							
								        $type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0]));
							 | 
						||
| 
								 | 
							
								        if ($type != 'ssh-rsa') {
							 | 
						||
| 
								 | 
							
								            return false;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1]));
							 | 
						||
| 
								 | 
							
								        $components['comment'] = trim(preg_replace('#Comment: (.+)#', '$1', $key[2]));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3]));
							 | 
						||
| 
								 | 
							
								        $public = Base64::decode(implode('', array_map('trim', array_slice($key, 4, $publicLength))));
							 | 
						||
| 
								 | 
							
								        $public = substr($public, 11);
							 | 
						||
| 
								 | 
							
								        extract(unpack('Nlength', self::_string_shift($public, 4)));
							 | 
						||
| 
								 | 
							
								        $components['publicExponent'] = new BigInteger(self::_string_shift($public, $length), -256);
							 | 
						||
| 
								 | 
							
								        extract(unpack('Nlength', self::_string_shift($public, 4)));
							 | 
						||
| 
								 | 
							
								        $components['modulus'] = new BigInteger(self::_string_shift($public, $length), -256);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$publicLength + 4]));
							 | 
						||
| 
								 | 
							
								        $private = Base64::decode(implode('', array_map('trim', array_slice($key, $publicLength + 5, $privateLength))));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        switch ($encryption) {
							 | 
						||
| 
								 | 
							
								            case 'aes256-cbc':
							 | 
						||
| 
								 | 
							
								                $symkey = static::generateSymmetricKey($password, 32);
							 | 
						||
| 
								 | 
							
								                $crypto = new AES(AES::MODE_CBC);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if ($encryption != 'none') {
							 | 
						||
| 
								 | 
							
								            $crypto->setKey($symkey);
							 | 
						||
| 
								 | 
							
								            $crypto->setIV(str_repeat("\0", $crypto->getBlockLength() >> 3));
							 | 
						||
| 
								 | 
							
								            $crypto->disablePadding();
							 | 
						||
| 
								 | 
							
								            $private = $crypto->decrypt($private);
							 | 
						||
| 
								 | 
							
								            if ($private === false) {
							 | 
						||
| 
								 | 
							
								                return false;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        extract(unpack('Nlength', self::_string_shift($private, 4)));
							 | 
						||
| 
								 | 
							
								        if (strlen($private) < $length) {
							 | 
						||
| 
								 | 
							
								            return false;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        $components['privateExponent'] = new BigInteger(self::_string_shift($private, $length), -256);
							 | 
						||
| 
								 | 
							
								        extract(unpack('Nlength', self::_string_shift($private, 4)));
							 | 
						||
| 
								 | 
							
								        if (strlen($private) < $length) {
							 | 
						||
| 
								 | 
							
								            return false;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        $components['primes'] = array(1 => new BigInteger(self::_string_shift($private, $length), -256));
							 | 
						||
| 
								 | 
							
								        extract(unpack('Nlength', self::_string_shift($private, 4)));
							 | 
						||
| 
								 | 
							
								        if (strlen($private) < $length) {
							 | 
						||
| 
								 | 
							
								            return false;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        $components['primes'][] = new BigInteger(self::_string_shift($private, $length), -256);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        $temp = $components['primes'][1]->subtract($one);
							 | 
						||
| 
								 | 
							
								        $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp));
							 | 
						||
| 
								 | 
							
								        $temp = $components['primes'][2]->subtract($one);
							 | 
						||
| 
								 | 
							
								        $components['exponents'][] = $components['publicExponent']->modInverse($temp);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        extract(unpack('Nlength', self::_string_shift($private, 4)));
							 | 
						||
| 
								 | 
							
								        if (strlen($private) < $length) {
							 | 
						||
| 
								 | 
							
								            return false;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        $components['coefficients'] = array(2 => new BigInteger(self::_string_shift($private, $length), -256));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return $components;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * String Shift
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * Inspired by array_shift
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @param string $string
							 | 
						||
| 
								 | 
							
								     * @param int $index
							 | 
						||
| 
								 | 
							
								     * @return string
							 | 
						||
| 
								 | 
							
								     * @access private
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    static function _string_shift(&$string, $index = 1)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        $substr = substr($string, 0, $index);
							 | 
						||
| 
								 | 
							
								        $string = substr($string, $index);
							 | 
						||
| 
								 | 
							
								        return $substr;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Convert a private key to the appropriate format.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @access public
							 | 
						||
| 
								 | 
							
								     * @param \phpseclib\Math\BigInteger $n
							 | 
						||
| 
								 | 
							
								     * @param \phpseclib\Math\BigInteger $e
							 | 
						||
| 
								 | 
							
								     * @param \phpseclib\Math\BigInteger $d
							 | 
						||
| 
								 | 
							
								     * @param array $primes
							 | 
						||
| 
								 | 
							
								     * @param array $exponents
							 | 
						||
| 
								 | 
							
								     * @param array $coefficients
							 | 
						||
| 
								 | 
							
								     * @param string $password optional
							 | 
						||
| 
								 | 
							
								     * @return string
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, $primes, $exponents, $coefficients, $password = '')
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        if (count($primes) != 2) {
							 | 
						||
| 
								 | 
							
								            return false;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        $raw = array(
							 | 
						||
| 
								 | 
							
								            'modulus' => $n->toBytes(true),
							 | 
						||
| 
								 | 
							
								            'publicExponent' => $e->toBytes(true),
							 | 
						||
| 
								 | 
							
								            'privateExponent' => $d->toBytes(true),
							 | 
						||
| 
								 | 
							
								            'prime1' => $primes[1]->toBytes(true),
							 | 
						||
| 
								 | 
							
								            'prime2' => $primes[2]->toBytes(true),
							 | 
						||
| 
								 | 
							
								            'exponent1' => $exponents[1]->toBytes(true),
							 | 
						||
| 
								 | 
							
								            'exponent2' => $exponents[2]->toBytes(true),
							 | 
						||
| 
								 | 
							
								            'coefficient' => $coefficients[2]->toBytes(true)
							 | 
						||
| 
								 | 
							
								        );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        $key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: ";
							 | 
						||
| 
								 | 
							
								        $encryption = (!empty($password) || is_string($password)) ? 'aes256-cbc' : 'none';
							 | 
						||
| 
								 | 
							
								        $key.= $encryption;
							 | 
						||
| 
								 | 
							
								        $key.= "\r\nComment: " . self::$comment . "\r\n";
							 | 
						||
| 
								 | 
							
								        $public = pack(
							 | 
						||
| 
								 | 
							
								            'Na*Na*Na*',
							 | 
						||
| 
								 | 
							
								            strlen('ssh-rsa'),
							 | 
						||
| 
								 | 
							
								            'ssh-rsa',
							 | 
						||
| 
								 | 
							
								            strlen($raw['publicExponent']),
							 | 
						||
| 
								 | 
							
								            $raw['publicExponent'],
							 | 
						||
| 
								 | 
							
								            strlen($raw['modulus']),
							 | 
						||
| 
								 | 
							
								            $raw['modulus']
							 | 
						||
| 
								 | 
							
								        );
							 | 
						||
| 
								 | 
							
								        $source = pack(
							 | 
						||
| 
								 | 
							
								            'Na*Na*Na*Na*',
							 | 
						||
| 
								 | 
							
								            strlen('ssh-rsa'),
							 | 
						||
| 
								 | 
							
								            'ssh-rsa',
							 | 
						||
| 
								 | 
							
								            strlen($encryption),
							 | 
						||
| 
								 | 
							
								            $encryption,
							 | 
						||
| 
								 | 
							
								            strlen(self::$comment),
							 | 
						||
| 
								 | 
							
								            self::$comment,
							 | 
						||
| 
								 | 
							
								            strlen($public),
							 | 
						||
| 
								 | 
							
								            $public
							 | 
						||
| 
								 | 
							
								        );
							 | 
						||
| 
								 | 
							
								        $public = Base64::encode($public);
							 | 
						||
| 
								 | 
							
								        $key.= "Public-Lines: " . ((strlen($public) + 63) >> 6) . "\r\n";
							 | 
						||
| 
								 | 
							
								        $key.= chunk_split($public, 64);
							 | 
						||
| 
								 | 
							
								        $private = pack(
							 | 
						||
| 
								 | 
							
								            'Na*Na*Na*Na*',
							 | 
						||
| 
								 | 
							
								            strlen($raw['privateExponent']),
							 | 
						||
| 
								 | 
							
								            $raw['privateExponent'],
							 | 
						||
| 
								 | 
							
								            strlen($raw['prime1']),
							 | 
						||
| 
								 | 
							
								            $raw['prime1'],
							 | 
						||
| 
								 | 
							
								            strlen($raw['prime2']),
							 | 
						||
| 
								 | 
							
								            $raw['prime2'],
							 | 
						||
| 
								 | 
							
								            strlen($raw['coefficient']),
							 | 
						||
| 
								 | 
							
								            $raw['coefficient']
							 | 
						||
| 
								 | 
							
								        );
							 | 
						||
| 
								 | 
							
								        if (empty($password) && !is_string($password)) {
							 | 
						||
| 
								 | 
							
								            $source.= pack('Na*', strlen($private), $private);
							 | 
						||
| 
								 | 
							
								            $hashkey = 'putty-private-key-file-mac-key';
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            $private.= Random::string(16 - (strlen($private) & 15));
							 | 
						||
| 
								 | 
							
								            $source.= pack('Na*', strlen($private), $private);
							 | 
						||
| 
								 | 
							
								            $crypto = new AES();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            $crypto->setKey(static::generateSymmetricKey($password, 32));
							 | 
						||
| 
								 | 
							
								            $crypto->setIV(str_repeat("\0", $crypto->getBlockLength() >> 3));
							 | 
						||
| 
								 | 
							
								            $crypto->disablePadding();
							 | 
						||
| 
								 | 
							
								            $private = $crypto->encrypt($private);
							 | 
						||
| 
								 | 
							
								            $hashkey = 'putty-private-key-file-mac-key' . $password;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        $private = Base64::encode($private);
							 | 
						||
| 
								 | 
							
								        $key.= 'Private-Lines: ' . ((strlen($private) + 63) >> 6) . "\r\n";
							 | 
						||
| 
								 | 
							
								        $key.= chunk_split($private, 64);
							 | 
						||
| 
								 | 
							
								        $hash = new Hash('sha1');
							 | 
						||
| 
								 | 
							
								        $hash->setKey(sha1($hashkey, true));
							 | 
						||
| 
								 | 
							
								        $key.= 'Private-MAC: ' . Hex::encode($hash->hash($source)) . "\r\n";
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return $key;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Convert a public key to the appropriate format
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @access public
							 | 
						||
| 
								 | 
							
								     * @param \phpseclib\Math\BigInteger $n
							 | 
						||
| 
								 | 
							
								     * @param \phpseclib\Math\BigInteger $e
							 | 
						||
| 
								 | 
							
								     * @return string
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    static function savePublicKey(BigInteger $n, BigInteger $e)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        $n = $n->toBytes(true);
							 | 
						||
| 
								 | 
							
								        $e = $e->toBytes(true);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        $key = pack(
							 | 
						||
| 
								 | 
							
								            'Na*Na*Na*',
							 | 
						||
| 
								 | 
							
								            strlen('ssh-rsa'),
							 | 
						||
| 
								 | 
							
								            'ssh-rsa',
							 | 
						||
| 
								 | 
							
								            strlen($e),
							 | 
						||
| 
								 | 
							
								            $e,
							 | 
						||
| 
								 | 
							
								            strlen($n),
							 | 
						||
| 
								 | 
							
								            $n
							 | 
						||
| 
								 | 
							
								        );
							 | 
						||
| 
								 | 
							
								        $key = "---- BEGIN SSH2 PUBLIC KEY ----\r\n" .
							 | 
						||
| 
								 | 
							
								               'Comment: "' . str_replace(array('\\', '"'), array('\\\\', '\"'), self::$comment) . "\"\r\n";
							 | 
						||
| 
								 | 
							
								               chunk_split(Base64::encode($key), 64) .
							 | 
						||
| 
								 | 
							
								               '---- END SSH2 PUBLIC KEY ----';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return $key;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 |