210 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			210 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| /**
 | |
|  * PKCS#8 Formatted RSA Key Handler
 | |
|  *
 | |
|  * PHP version 5
 | |
|  *
 | |
|  * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set)
 | |
|  *
 | |
|  * Has the following header:
 | |
|  *
 | |
|  * -----BEGIN PUBLIC KEY-----
 | |
|  *
 | |
|  * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
 | |
|  * is specific to private keys it's basically creating a DER-encoded wrapper
 | |
|  * for keys. This just extends that same concept to public keys (much like ssh-keygen)
 | |
|  *
 | |
|  * @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 phpseclib\Crypt\DES;
 | |
| use phpseclib\Crypt\Random;
 | |
| use phpseclib\Math\BigInteger;
 | |
| 
 | |
| /**
 | |
|  * PKCS#8 Formatted RSA Key Handler
 | |
|  *
 | |
|  * @package RSA
 | |
|  * @author  Jim Wigginton <terrafrost@php.net>
 | |
|  * @access  public
 | |
|  */
 | |
| class PKCS8 extends PKCS
 | |
| {
 | |
|     /**
 | |
|      * 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 = '')
 | |
|     {
 | |
|         $num_primes = count($primes);
 | |
|         $raw = array(
 | |
|             'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi
 | |
|             '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)
 | |
|         );
 | |
| 
 | |
|         $components = array();
 | |
|         foreach ($raw as $name => $value) {
 | |
|             $components[$name] = pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($value)), $value);
 | |
|         }
 | |
| 
 | |
|         $RSAPrivateKey = implode('', $components);
 | |
| 
 | |
|         if ($num_primes > 2) {
 | |
|             $OtherPrimeInfos = '';
 | |
|             for ($i = 3; $i <= $num_primes; $i++) {
 | |
|                 // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
 | |
|                 //
 | |
|                 // OtherPrimeInfo ::= SEQUENCE {
 | |
|                 //     prime             INTEGER,  -- ri
 | |
|                 //     exponent          INTEGER,  -- di
 | |
|                 //     coefficient       INTEGER   -- ti
 | |
|                 // }
 | |
|                 $OtherPrimeInfo = pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true));
 | |
|                 $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true));
 | |
|                 $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true));
 | |
|                 $OtherPrimeInfos.= pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo);
 | |
|             }
 | |
|             $RSAPrivateKey.= pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos);
 | |
|         }
 | |
| 
 | |
|         $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
 | |
| 
 | |
|         $rsaOID = "\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00"; // hex version of MA0GCSqGSIb3DQEBAQUA
 | |
|         $RSAPrivateKey = pack(
 | |
|             'Ca*a*Ca*a*',
 | |
|             self::ASN1_INTEGER,
 | |
|             "\01\00",
 | |
|             $rsaOID,
 | |
|             4,
 | |
|             self::_encodeLength(strlen($RSAPrivateKey)),
 | |
|             $RSAPrivateKey
 | |
|         );
 | |
|         $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
 | |
|         if (!empty($password) || is_string($password)) {
 | |
|             $salt = Random::string(8);
 | |
|             $iterationCount = 2048;
 | |
| 
 | |
|             $crypto = new DES(DES::MODE_CBC);
 | |
|             $crypto->setPassword($password, 'pbkdf1', 'md5', $salt, $iterationCount);
 | |
|             $RSAPrivateKey = $crypto->encrypt($RSAPrivateKey);
 | |
| 
 | |
|             $parameters = pack(
 | |
|                 'Ca*a*Ca*N',
 | |
|                 self::ASN1_OCTETSTRING,
 | |
|                 self::_encodeLength(strlen($salt)),
 | |
|                 $salt,
 | |
|                 self::ASN1_INTEGER,
 | |
|                 self::_encodeLength(4),
 | |
|                 $iterationCount
 | |
|             );
 | |
|             $pbeWithMD5AndDES_CBC = "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03";
 | |
| 
 | |
|             $encryptionAlgorithm = pack(
 | |
|                 'Ca*a*Ca*a*',
 | |
|                 self::ASN1_OBJECT,
 | |
|                 self::_encodeLength(strlen($pbeWithMD5AndDES_CBC)),
 | |
|                 $pbeWithMD5AndDES_CBC,
 | |
|                 self::ASN1_SEQUENCE,
 | |
|                 self::_encodeLength(strlen($parameters)),
 | |
|                 $parameters
 | |
|             );
 | |
| 
 | |
|             $RSAPrivateKey = pack(
 | |
|                 'Ca*a*Ca*a*',
 | |
|                 self::ASN1_SEQUENCE,
 | |
|                 self::_encodeLength(strlen($encryptionAlgorithm)),
 | |
|                 $encryptionAlgorithm,
 | |
|                 self::ASN1_OCTETSTRING,
 | |
|                 self::_encodeLength(strlen($RSAPrivateKey)),
 | |
|                 $RSAPrivateKey
 | |
|             );
 | |
| 
 | |
|             $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
 | |
| 
 | |
|             $RSAPrivateKey = "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" .
 | |
|                  chunk_split(Base64::encode($RSAPrivateKey), 64) .
 | |
|                  '-----END ENCRYPTED PRIVATE KEY-----';
 | |
|         } else {
 | |
|             $RSAPrivateKey = "-----BEGIN PRIVATE KEY-----\r\n" .
 | |
|                  chunk_split(Base64::encode($RSAPrivateKey), 64) .
 | |
|                  '-----END PRIVATE KEY-----';
 | |
|         }
 | |
| 
 | |
|         return $RSAPrivateKey;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * 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)
 | |
|     {
 | |
|         $modulus = $n->toBytes(true);
 | |
|         $publicExponent = $e->toBytes(true);
 | |
| 
 | |
|         // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.1>:
 | |
|         // RSAPublicKey ::= SEQUENCE {
 | |
|         //     modulus           INTEGER,  -- n
 | |
|         //     publicExponent    INTEGER   -- e
 | |
|         // }
 | |
|         $components = array(
 | |
|             'modulus' => pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($modulus)), $modulus),
 | |
|             'publicExponent' => pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($publicExponent)), $publicExponent)
 | |
|         );
 | |
| 
 | |
|         $RSAPublicKey = pack(
 | |
|             'Ca*a*a*',
 | |
|             self::ASN1_SEQUENCE,
 | |
|             self::_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])),
 | |
|             $components['modulus'],
 | |
|             $components['publicExponent']
 | |
|         );
 | |
| 
 | |
|         // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
 | |
|         $rsaOID = "\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00"; // hex version of MA0GCSqGSIb3DQEBAQUA
 | |
|         $RSAPublicKey = chr(0) . $RSAPublicKey;
 | |
|         $RSAPublicKey = chr(3) . self::_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey;
 | |
| 
 | |
|         $RSAPublicKey = pack(
 | |
|             'Ca*a*',
 | |
|             self::ASN1_SEQUENCE,
 | |
|             self::_encodeLength(strlen($rsaOID . $RSAPublicKey)),
 | |
|             $rsaOID . $RSAPublicKey
 | |
|         );
 | |
| 
 | |
|         $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
 | |
|                         chunk_split(Base64::encode($RSAPublicKey), 64) .
 | |
|                         '-----END PUBLIC KEY-----';
 | |
| 
 | |
|         return $RSAPublicKey;
 | |
|     }
 | |
| }
 |