| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 |
- <?php
- /**
- * PKCS1 Formatted Key Handler
- *
- * PHP version 5
- *
- * @category Crypt
- * @package Common
- * @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\Common\Keys;
- use ParagonIE\ConstantTime\Base64;
- use ParagonIE\ConstantTime\Hex;
- use phpseclib\Crypt\Common\BlockCipher;
- use phpseclib\Crypt\Random;
- use phpseclib\Crypt\AES;
- use phpseclib\Crypt\Base;
- use phpseclib\Crypt\DES;
- use phpseclib\Crypt\TripleDES;
- use phpseclib\File\ASN1;
- /**
- * PKCS1 Formatted Key Handler
- *
- * @package RSA
- * @author Jim Wigginton <terrafrost@php.net>
- * @access public
- */
- abstract class PKCS1 extends PKCS
- {
- /**
- * Default encryption algorithm
- *
- * @var string
- * @access private
- */
- private static $defaultEncryptionAlgorithm = 'AES-128-CBC';
- /**
- * Sets the default encryption algorithm
- *
- * @access public
- * @param string $algo
- */
- public static function setEncryptionAlgorithm($algo)
- {
- self::$defaultEncryptionAlgorithm = $algo;
- }
- /**
- * Returns the mode constant corresponding to the mode string
- *
- * @access public
- * @param string $mode
- * @return int
- * @throws \UnexpectedValueException if the block cipher mode is unsupported
- */
- private static function getEncryptionMode($mode)
- {
- switch ($mode) {
- case 'CBC':
- return BlockCipher::MODE_CBC;
- case 'ECB':
- return BlockCipher::MODE_ECB;
- case 'CFB':
- return BlockCipher::MODE_CFB;
- case 'OFB':
- return BlockCipher::MODE_OFB;
- case 'CTR':
- return BlockCipher::MODE_CTR;
- }
- throw new \UnexpectedValueException('Unsupported block cipher mode of operation');
- }
- /**
- * Returns a cipher object corresponding to a string
- *
- * @access public
- * @param string $algo
- * @return string
- * @throws \UnexpectedValueException if the encryption algorithm is unsupported
- */
- private static function getEncryptionObject($algo)
- {
- $modes = '(CBC|ECB|CFB|OFB|CTR)';
- switch (true) {
- case preg_match("#^AES-(128|192|256)-$modes$#", $algo, $matches):
- $cipher = new AES(self::getEncryptionMode($matches[2]));
- $cipher->setKeyLength($matches[1]);
- return $cipher;
- case preg_match("#^DES-EDE3-$modes$#", $algo, $matches):
- return new TripleDES(self::getEncryptionMode($matches[1]));
- case preg_match("#^DES-$modes$#", $algo, $matches):
- return new DES(self::getEncryptionMode($matches[1]));
- default:
- throw new \UnexpectedValueException('Unsupported encryption algorithmn');
- }
- }
- /**
- * Generate a symmetric key for PKCS#1 keys
- *
- * @access private
- * @param string $password
- * @param string $iv
- * @param int $length
- * @return string
- */
- private static function generateSymmetricKey($password, $iv, $length)
- {
- $symkey = '';
- $iv = substr($iv, 0, 8);
- while (strlen($symkey) < $length) {
- $symkey.= md5($symkey . $password . $iv, true);
- }
- 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
- */
- protected static function load($key, $password)
- {
- if (!is_string($key)) {
- return false;
- }
- /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
- "outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
- protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding
- two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here:
- http://tools.ietf.org/html/rfc1421#section-4.6.1.1
- http://tools.ietf.org/html/rfc1421#section-4.6.1.3
- DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell.
- DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation
- function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's
- own implementation. ie. the implementation *is* the standard and any bugs that may exist in that
- implementation are part of the standard, as well.
- * OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */
- if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
- $iv = Hex::decode(trim($matches[2]));
- // remove the Proc-Type / DEK-Info sections as they're no longer needed
- $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key);
- $ciphertext = ASN1::extractBER($key);
- if ($ciphertext === false) {
- $ciphertext = $key;
- }
- $crypto = self::getEncryptionObject($matches[1]);
- $crypto->setKey(self::generateSymmetricKey($password, $iv, $crypto->getKeyLength() >> 3));
- $crypto->setIV($iv);
- $key = $crypto->decrypt($ciphertext);
- } else {
- if (self::$format != self::MODE_DER) {
- $decoded = ASN1::extractBER($key);
- if ($decoded !== false) {
- $key = $decoded;
- } elseif (self::$format == self::MODE_PEM) {
- return false;
- }
- }
- }
- return $key;
- }
- /**
- * Wrap a private key appropriately
- *
- * @access public
- * @param string $key
- * @param string $type
- * @param string $password
- * @return string
- */
- protected static function wrapPrivateKey($key, $type, $password)
- {
- if (empty($password) || !is_string($password)) {
- return "-----BEGIN $type PRIVATE KEY-----\r\n" .
- chunk_split(Base64::encode($key), 64) .
- "-----END $type PRIVATE KEY-----";
- }
- $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));
- return "-----BEGIN $type 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($key)), 64) .
- "-----END $type PRIVATE KEY-----";
- }
- /**
- * Wrap a public key appropriately
- *
- * @access public
- * @param string $key
- * @param string $type
- * @return string
- */
- protected static function wrapPublicKey($key, $type)
- {
- return "-----BEGIN $type PUBLIC KEY-----\r\n" .
- chunk_split(Base64::encode($key), 64) .
- "-----END $type PUBLIC KEY-----";
- }
- }
|