* @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\DES; use phpseclib\Crypt\Random; use phpseclib\Crypt\TripleDES; use phpseclib\Math\BigInteger; use phpseclib\Common\Functions\ASN1; /** * PKCS#1 Formatted RSA Key Handler * * @package RSA * @author Jim Wigginton * @access public */ class PKCS1 extends PKCS { /** * Default encryption algorithm * * @var string * @access private */ static $defaultEncryptionAlgorithm = 'DES-EDE3-CBC'; /** * Sets the default encryption algorithm * * @access public * @param string $algo */ static function setEncryptionAlgorithm($algo) { self::$defaultEncryptionAlgorithm = $algo; } /** * 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, ASN1::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, ASN1::encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true)); $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true)); $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true)); $OtherPrimeInfos.= pack('Ca*a*', self::ASN1_SEQUENCE, ASN1::encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo); } $RSAPrivateKey.= pack('Ca*a*', self::ASN1_SEQUENCE, ASN1::encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos); } $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, ASN1::encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); if (!empty($password) || is_string($password)) { $cipher = self::getEncryptionObject(self::$defaultEncryptionAlgorithm); $iv = Random::string($cipher->getBlockLength() >> 3); $cipher->setKey(self::generateSymmetricKey($password, $iv, $cipher->getKeyLength() >> 3)); $cipher->setIV($iv); $iv = strtoupper(Hex::encode($iv)); $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" . "Proc-Type: 4,ENCRYPTED\r\n" . "DEK-Info: " . self::$defaultEncryptionAlgorithm . ",$iv\r\n" . "\r\n" . chunk_split(Base64::encode($cipher->encrypt($RSAPrivateKey)), 64) . '-----END RSA PRIVATE KEY-----'; } else { $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" . chunk_split(Base64::encode($RSAPrivateKey), 64) . '-----END RSA 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 : // RSAPublicKey ::= SEQUENCE { // modulus INTEGER, -- n // publicExponent INTEGER -- e // } $components = array( 'modulus' => pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($modulus)), $modulus), 'publicExponent' => pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($publicExponent)), $publicExponent) ); $RSAPublicKey = pack( 'Ca*a*a*', self::ASN1_SEQUENCE, ASN1::encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])), $components['modulus'], $components['publicExponent'] ); $RSAPublicKey = "-----BEGIN RSA PUBLIC KEY-----\r\n" . chunk_split(Base64::encode($RSAPublicKey), 64) . '-----END RSA PUBLIC KEY-----'; return $RSAPublicKey; } }