PKCS1.php 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. <?php
  2. /**
  3. * PKCS1 Formatted Key Handler
  4. *
  5. * PHP version 5
  6. *
  7. * @category Crypt
  8. * @package Common
  9. * @author Jim Wigginton <terrafrost@php.net>
  10. * @copyright 2015 Jim Wigginton
  11. * @license http://www.opensource.org/licenses/mit-license.html MIT License
  12. * @link http://phpseclib.sourceforge.net
  13. */
  14. namespace phpseclib\Crypt\Common\Keys;
  15. use ParagonIE\ConstantTime\Base64;
  16. use ParagonIE\ConstantTime\Hex;
  17. use phpseclib\Crypt\Common\BlockCipher;
  18. use phpseclib\Crypt\Random;
  19. use phpseclib\Crypt\AES;
  20. use phpseclib\Crypt\Base;
  21. use phpseclib\Crypt\DES;
  22. use phpseclib\Crypt\TripleDES;
  23. use phpseclib\File\ASN1;
  24. /**
  25. * PKCS1 Formatted Key Handler
  26. *
  27. * @package RSA
  28. * @author Jim Wigginton <terrafrost@php.net>
  29. * @access public
  30. */
  31. abstract class PKCS1 extends PKCS
  32. {
  33. /**
  34. * Default encryption algorithm
  35. *
  36. * @var string
  37. * @access private
  38. */
  39. private static $defaultEncryptionAlgorithm = 'AES-128-CBC';
  40. /**
  41. * Sets the default encryption algorithm
  42. *
  43. * @access public
  44. * @param string $algo
  45. */
  46. public static function setEncryptionAlgorithm($algo)
  47. {
  48. self::$defaultEncryptionAlgorithm = $algo;
  49. }
  50. /**
  51. * Returns the mode constant corresponding to the mode string
  52. *
  53. * @access public
  54. * @param string $mode
  55. * @return int
  56. * @throws \UnexpectedValueException if the block cipher mode is unsupported
  57. */
  58. private static function getEncryptionMode($mode)
  59. {
  60. switch ($mode) {
  61. case 'CBC':
  62. return BlockCipher::MODE_CBC;
  63. case 'ECB':
  64. return BlockCipher::MODE_ECB;
  65. case 'CFB':
  66. return BlockCipher::MODE_CFB;
  67. case 'OFB':
  68. return BlockCipher::MODE_OFB;
  69. case 'CTR':
  70. return BlockCipher::MODE_CTR;
  71. }
  72. throw new \UnexpectedValueException('Unsupported block cipher mode of operation');
  73. }
  74. /**
  75. * Returns a cipher object corresponding to a string
  76. *
  77. * @access public
  78. * @param string $algo
  79. * @return string
  80. * @throws \UnexpectedValueException if the encryption algorithm is unsupported
  81. */
  82. private static function getEncryptionObject($algo)
  83. {
  84. $modes = '(CBC|ECB|CFB|OFB|CTR)';
  85. switch (true) {
  86. case preg_match("#^AES-(128|192|256)-$modes$#", $algo, $matches):
  87. $cipher = new AES(self::getEncryptionMode($matches[2]));
  88. $cipher->setKeyLength($matches[1]);
  89. return $cipher;
  90. case preg_match("#^DES-EDE3-$modes$#", $algo, $matches):
  91. return new TripleDES(self::getEncryptionMode($matches[1]));
  92. case preg_match("#^DES-$modes$#", $algo, $matches):
  93. return new DES(self::getEncryptionMode($matches[1]));
  94. default:
  95. throw new \UnexpectedValueException('Unsupported encryption algorithmn');
  96. }
  97. }
  98. /**
  99. * Generate a symmetric key for PKCS#1 keys
  100. *
  101. * @access private
  102. * @param string $password
  103. * @param string $iv
  104. * @param int $length
  105. * @return string
  106. */
  107. private static function generateSymmetricKey($password, $iv, $length)
  108. {
  109. $symkey = '';
  110. $iv = substr($iv, 0, 8);
  111. while (strlen($symkey) < $length) {
  112. $symkey.= md5($symkey . $password . $iv, true);
  113. }
  114. return substr($symkey, 0, $length);
  115. }
  116. /**
  117. * Break a public or private key down into its constituent components
  118. *
  119. * @access public
  120. * @param string $key
  121. * @param string $password optional
  122. * @return array
  123. */
  124. protected static function load($key, $password)
  125. {
  126. if (!is_string($key)) {
  127. return false;
  128. }
  129. /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
  130. "outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
  131. protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding
  132. two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here:
  133. http://tools.ietf.org/html/rfc1421#section-4.6.1.1
  134. http://tools.ietf.org/html/rfc1421#section-4.6.1.3
  135. DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell.
  136. DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation
  137. function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's
  138. own implementation. ie. the implementation *is* the standard and any bugs that may exist in that
  139. implementation are part of the standard, as well.
  140. * OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */
  141. if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
  142. $iv = Hex::decode(trim($matches[2]));
  143. // remove the Proc-Type / DEK-Info sections as they're no longer needed
  144. $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key);
  145. $ciphertext = ASN1::extractBER($key);
  146. if ($ciphertext === false) {
  147. $ciphertext = $key;
  148. }
  149. $crypto = self::getEncryptionObject($matches[1]);
  150. $crypto->setKey(self::generateSymmetricKey($password, $iv, $crypto->getKeyLength() >> 3));
  151. $crypto->setIV($iv);
  152. $key = $crypto->decrypt($ciphertext);
  153. } else {
  154. if (self::$format != self::MODE_DER) {
  155. $decoded = ASN1::extractBER($key);
  156. if ($decoded !== false) {
  157. $key = $decoded;
  158. } elseif (self::$format == self::MODE_PEM) {
  159. return false;
  160. }
  161. }
  162. }
  163. return $key;
  164. }
  165. /**
  166. * Wrap a private key appropriately
  167. *
  168. * @access public
  169. * @param string $key
  170. * @param string $type
  171. * @param string $password
  172. * @return string
  173. */
  174. protected static function wrapPrivateKey($key, $type, $password)
  175. {
  176. if (empty($password) || !is_string($password)) {
  177. return "-----BEGIN $type PRIVATE KEY-----\r\n" .
  178. chunk_split(Base64::encode($key), 64) .
  179. "-----END $type PRIVATE KEY-----";
  180. }
  181. $cipher = self::getEncryptionObject(self::$defaultEncryptionAlgorithm);
  182. $iv = Random::string($cipher->getBlockLength() >> 3);
  183. $cipher->setKey(self::generateSymmetricKey($password, $iv, $cipher->getKeyLength() >> 3));
  184. $cipher->setIV($iv);
  185. $iv = strtoupper(Hex::encode($iv));
  186. return "-----BEGIN $type PRIVATE KEY-----\r\n" .
  187. "Proc-Type: 4,ENCRYPTED\r\n" .
  188. "DEK-Info: " . self::$defaultEncryptionAlgorithm . ",$iv\r\n" .
  189. "\r\n" .
  190. chunk_split(Base64::encode($cipher->encrypt($key)), 64) .
  191. "-----END $type PRIVATE KEY-----";
  192. }
  193. /**
  194. * Wrap a public key appropriately
  195. *
  196. * @access public
  197. * @param string $key
  198. * @param string $type
  199. * @return string
  200. */
  201. protected static function wrapPublicKey($key, $type)
  202. {
  203. return "-----BEGIN $type PUBLIC KEY-----\r\n" .
  204. chunk_split(Base64::encode($key), 64) .
  205. "-----END $type PUBLIC KEY-----";
  206. }
  207. }