PKCS.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. <?php
  2. /**
  3. * PKCS Formatted RSA Key Handler
  4. *
  5. * PHP version 5
  6. *
  7. * @category Crypt
  8. * @package RSA
  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\RSA;
  15. use ParagonIE\ConstantTime\Base64;
  16. use ParagonIE\ConstantTime\Hex;
  17. use phpseclib\Crypt\AES;
  18. use phpseclib\Crypt\Base;
  19. use phpseclib\Crypt\DES;
  20. use phpseclib\Crypt\TripleDES;
  21. use phpseclib\Math\BigInteger;
  22. /**
  23. * PKCS Formatted RSA Key Handler
  24. *
  25. * @package RSA
  26. * @author Jim Wigginton <terrafrost@php.net>
  27. * @access public
  28. */
  29. abstract class PKCS
  30. {
  31. /**#@+
  32. * @access private
  33. * @see \phpseclib\Crypt\RSA::createKey()
  34. */
  35. /**
  36. * ASN1 Integer
  37. */
  38. const ASN1_INTEGER = 2;
  39. /**
  40. * ASN1 Bit String
  41. */
  42. const ASN1_BITSTRING = 3;
  43. /**
  44. * ASN1 Octet String
  45. */
  46. const ASN1_OCTETSTRING = 4;
  47. /**
  48. * ASN1 Object Identifier
  49. */
  50. const ASN1_OBJECT = 6;
  51. /**
  52. * ASN1 Sequence (with the constucted bit set)
  53. */
  54. const ASN1_SEQUENCE = 48;
  55. /**#@-*/
  56. /**#@+
  57. * @access private
  58. */
  59. /**
  60. * Auto-detect the format
  61. */
  62. const MODE_ANY = 0;
  63. /**
  64. * Require base64-encoded PEM's be supplied
  65. */
  66. const MODE_PEM = 1;
  67. /**
  68. * Require raw DER's be supplied
  69. */
  70. const MODE_DER = 2;
  71. /**#@-*/
  72. /**
  73. * Is the key a base-64 encoded PEM, DER or should it be auto-detected?
  74. *
  75. * @access private
  76. * @param int
  77. */
  78. static $format = self::MODE_ANY;
  79. /**
  80. * Returns the mode constant corresponding to the mode string
  81. *
  82. * @access public
  83. * @param string $mode
  84. * @return int
  85. * @throws \UnexpectedValueException if the block cipher mode is unsupported
  86. */
  87. static function getEncryptionMode($mode)
  88. {
  89. switch ($mode) {
  90. case 'CBC':
  91. return Base::MODE_CBC;
  92. case 'ECB':
  93. return Base::MODE_ECB;
  94. case 'CFB':
  95. return Base::MODE_CFB;
  96. case 'OFB':
  97. return Base::MODE_OFB;
  98. case 'CTR':
  99. return Base::MODE_CTR;
  100. }
  101. throw new \UnexpectedValueException('Unsupported block cipher mode of operation');
  102. }
  103. /**
  104. * Returns a cipher object corresponding to a string
  105. *
  106. * @access public
  107. * @param string $algo
  108. * @return string
  109. * @throws \UnexpectedValueException if the encryption algorithm is unsupported
  110. */
  111. static function getEncryptionObject($algo)
  112. {
  113. $modes = '(CBC|ECB|CFB|OFB|CTR)';
  114. switch (true) {
  115. case preg_match("#^AES-(128|192|256)-$modes$#", $algo, $matches):
  116. $cipher = new AES(self::getEncryptionMode($matches[2]));
  117. $cipher->setKeyLength($matches[1]);
  118. return $cipher;
  119. case preg_match("#^DES-EDE3-$modes$#", $algo, $matches):
  120. return new TripleDES(self::getEncryptionMode($matches[1]));
  121. case preg_match("#^DES-$modes$#", $algo, $matches):
  122. return new DES(self::getEncryptionMode($matches[1]));
  123. default:
  124. throw new \UnexpectedValueException('Unsupported encryption algorithmn');
  125. }
  126. }
  127. /**
  128. * Generate a symmetric key for PKCS#1 keys
  129. *
  130. * @access public
  131. * @param string $password
  132. * @param string $iv
  133. * @param int $length
  134. * @return string
  135. */
  136. static function generateSymmetricKey($password, $iv, $length)
  137. {
  138. $symkey = '';
  139. $iv = substr($iv, 0, 8);
  140. while (strlen($symkey) < $length) {
  141. $symkey.= md5($symkey . $password . $iv, true);
  142. }
  143. return substr($symkey, 0, $length);
  144. }
  145. /**
  146. * Break a public or private key down into its constituent components
  147. *
  148. * @access public
  149. * @param string $key
  150. * @param string $password optional
  151. * @return array
  152. */
  153. static function load($key, $password = '')
  154. {
  155. if (!is_string($key)) {
  156. return false;
  157. }
  158. $components = array('isPublicKey' => strpos($key, 'PUBLIC') !== false);
  159. /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
  160. "outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
  161. protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding
  162. two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here:
  163. http://tools.ietf.org/html/rfc1421#section-4.6.1.1
  164. http://tools.ietf.org/html/rfc1421#section-4.6.1.3
  165. DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell.
  166. DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation
  167. function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's
  168. own implementation. ie. the implementation *is* the standard and any bugs that may exist in that
  169. implementation are part of the standard, as well.
  170. * OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */
  171. if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
  172. $iv = Hex::decode(trim($matches[2]));
  173. // remove the Proc-Type / DEK-Info sections as they're no longer needed
  174. $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key);
  175. $ciphertext = self::_extractBER($key);
  176. if ($ciphertext === false) {
  177. $ciphertext = $key;
  178. }
  179. $crypto = self::getEncryptionObject($matches[1]);
  180. $crypto->setKey(self::generateSymmetricKey($password, $iv, $crypto->getKeyLength() >> 3));
  181. $crypto->setIV($iv);
  182. $key = $crypto->decrypt($ciphertext);
  183. if ($key === false) {
  184. return false;
  185. }
  186. } else {
  187. if (self::$format != self::MODE_DER) {
  188. $decoded = self::_extractBER($key);
  189. if ($decoded !== false) {
  190. $key = $decoded;
  191. } elseif (self::$format == self::MODE_PEM) {
  192. return false;
  193. }
  194. }
  195. }
  196. if (ord(self::_string_shift($key)) != self::ASN1_SEQUENCE) {
  197. return false;
  198. }
  199. if (self::_decodeLength($key) != strlen($key)) {
  200. return false;
  201. }
  202. $tag = ord(self::_string_shift($key));
  203. /* intended for keys for which OpenSSL's asn1parse returns the following:
  204. 0:d=0 hl=4 l= 631 cons: SEQUENCE
  205. 4:d=1 hl=2 l= 1 prim: INTEGER :00
  206. 7:d=1 hl=2 l= 13 cons: SEQUENCE
  207. 9:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
  208. 20:d=2 hl=2 l= 0 prim: NULL
  209. 22:d=1 hl=4 l= 609 prim: OCTET STRING
  210. ie. PKCS8 keys */
  211. if ($tag == self::ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") {
  212. self::_string_shift($key, 3);
  213. $tag = self::ASN1_SEQUENCE;
  214. }
  215. if ($tag == self::ASN1_SEQUENCE) {
  216. $temp = self::_string_shift($key, self::_decodeLength($key));
  217. if (ord(self::_string_shift($temp)) != self::ASN1_OBJECT) {
  218. return false;
  219. }
  220. $length = self::_decodeLength($temp);
  221. switch (self::_string_shift($temp, $length)) {
  222. case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption
  223. break;
  224. case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC
  225. /*
  226. PBEParameter ::= SEQUENCE {
  227. salt OCTET STRING (SIZE(8)),
  228. iterationCount INTEGER }
  229. */
  230. if (ord(self::_string_shift($temp)) != self::ASN1_SEQUENCE) {
  231. return false;
  232. }
  233. if (self::_decodeLength($temp) != strlen($temp)) {
  234. return false;
  235. }
  236. self::_string_shift($temp); // assume it's an octet string
  237. $salt = self::_string_shift($temp, self::_decodeLength($temp));
  238. if (ord(self::_string_shift($temp)) != self::ASN1_INTEGER) {
  239. return false;
  240. }
  241. self::_decodeLength($temp);
  242. list(, $iterationCount) = unpack('N', str_pad($temp, 4, chr(0), STR_PAD_LEFT));
  243. self::_string_shift($key); // assume it's an octet string
  244. $length = self::_decodeLength($key);
  245. if (strlen($key) != $length) {
  246. return false;
  247. }
  248. $crypto = new DES(DES::MODE_CBC);
  249. $crypto->setPassword($password, 'pbkdf1', 'md5', $salt, $iterationCount);
  250. $key = $crypto->decrypt($key);
  251. if ($key === false) {
  252. return false;
  253. }
  254. return self::load($key);
  255. default:
  256. return false;
  257. }
  258. /* intended for keys for which OpenSSL's asn1parse returns the following:
  259. 0:d=0 hl=4 l= 290 cons: SEQUENCE
  260. 4:d=1 hl=2 l= 13 cons: SEQUENCE
  261. 6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
  262. 17:d=2 hl=2 l= 0 prim: NULL
  263. 19:d=1 hl=4 l= 271 prim: BIT STRING */
  264. $tag = ord(self::_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag
  265. self::_decodeLength($key); // skip over the BIT STRING / OCTET STRING length
  266. // "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of
  267. // unused bits in the final subsequent octet. The number shall be in the range zero to seven."
  268. // -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2)
  269. if ($tag == self::ASN1_BITSTRING) {
  270. self::_string_shift($key);
  271. }
  272. if (ord(self::_string_shift($key)) != self::ASN1_SEQUENCE) {
  273. return false;
  274. }
  275. if (self::_decodeLength($key) != strlen($key)) {
  276. return false;
  277. }
  278. $tag = ord(self::_string_shift($key));
  279. }
  280. if ($tag != self::ASN1_INTEGER) {
  281. return false;
  282. }
  283. $length = self::_decodeLength($key);
  284. $temp = self::_string_shift($key, $length);
  285. if (strlen($temp) != 1 || ord($temp) > 2) {
  286. $components['modulus'] = new BigInteger($temp, 256);
  287. self::_string_shift($key); // skip over self::ASN1_INTEGER
  288. $length = self::_decodeLength($key);
  289. $components[$components['isPublicKey'] ? 'publicExponent' : 'privateExponent'] = new BigInteger(self::_string_shift($key, $length), 256);
  290. return $components;
  291. }
  292. if (ord(self::_string_shift($key)) != self::ASN1_INTEGER) {
  293. return false;
  294. }
  295. $length = self::_decodeLength($key);
  296. $components['modulus'] = new BigInteger(self::_string_shift($key, $length), 256);
  297. self::_string_shift($key);
  298. $length = self::_decodeLength($key);
  299. $components['publicExponent'] = new BigInteger(self::_string_shift($key, $length), 256);
  300. self::_string_shift($key);
  301. $length = self::_decodeLength($key);
  302. $components['privateExponent'] = new BigInteger(self::_string_shift($key, $length), 256);
  303. self::_string_shift($key);
  304. $length = self::_decodeLength($key);
  305. $components['primes'] = array(1 => new BigInteger(self::_string_shift($key, $length), 256));
  306. self::_string_shift($key);
  307. $length = self::_decodeLength($key);
  308. $components['primes'][] = new BigInteger(self::_string_shift($key, $length), 256);
  309. self::_string_shift($key);
  310. $length = self::_decodeLength($key);
  311. $components['exponents'] = array(1 => new BigInteger(self::_string_shift($key, $length), 256));
  312. self::_string_shift($key);
  313. $length = self::_decodeLength($key);
  314. $components['exponents'][] = new BigInteger(self::_string_shift($key, $length), 256);
  315. self::_string_shift($key);
  316. $length = self::_decodeLength($key);
  317. $components['coefficients'] = array(2 => new BigInteger(self::_string_shift($key, $length), 256));
  318. if (!empty($key)) {
  319. if (ord(self::_string_shift($key)) != self::ASN1_SEQUENCE) {
  320. return false;
  321. }
  322. self::_decodeLength($key);
  323. while (!empty($key)) {
  324. if (ord(self::_string_shift($key)) != self::ASN1_SEQUENCE) {
  325. return false;
  326. }
  327. self::_decodeLength($key);
  328. $key = substr($key, 1);
  329. $length = self::_decodeLength($key);
  330. $components['primes'][] = new BigInteger(self::_string_shift($key, $length), 256);
  331. self::_string_shift($key);
  332. $length = self::_decodeLength($key);
  333. $components['exponents'][] = new BigInteger(self::_string_shift($key, $length), 256);
  334. self::_string_shift($key);
  335. $length = self::_decodeLength($key);
  336. $components['coefficients'][] = new BigInteger(self::_string_shift($key, $length), 256);
  337. }
  338. }
  339. return $components;
  340. }
  341. /**
  342. * Require base64-encoded PEM's be supplied
  343. *
  344. * @see self::load()
  345. * @access public
  346. */
  347. static function requirePEM()
  348. {
  349. self::$format = self::MODE_PEM;
  350. }
  351. /**
  352. * Require raw DER's be supplied
  353. *
  354. * @see self::load()
  355. * @access public
  356. */
  357. static function requireDER()
  358. {
  359. self::$format = self::MODE_DER;
  360. }
  361. /**
  362. * Accept any format and auto detect the format
  363. *
  364. * This is the default setting
  365. *
  366. * @see self::load()
  367. * @access public
  368. */
  369. static function requireAny()
  370. {
  371. self::$format = self::MODE_ANY;
  372. }
  373. /**
  374. * DER-decode the length
  375. *
  376. * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
  377. * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
  378. *
  379. * @access private
  380. * @param string $string
  381. * @return int
  382. */
  383. static function _decodeLength(&$string)
  384. {
  385. $length = ord(self::_string_shift($string));
  386. if ($length & 0x80) { // definite length, long form
  387. $length&= 0x7F;
  388. $temp = self::_string_shift($string, $length);
  389. list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4));
  390. }
  391. return $length;
  392. }
  393. /**
  394. * DER-encode the length
  395. *
  396. * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
  397. * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
  398. *
  399. * @access private
  400. * @param int $length
  401. * @return string
  402. */
  403. static function _encodeLength($length)
  404. {
  405. if ($length <= 0x7F) {
  406. return chr($length);
  407. }
  408. $temp = ltrim(pack('N', $length), chr(0));
  409. return pack('Ca*', 0x80 | strlen($temp), $temp);
  410. }
  411. /**
  412. * String Shift
  413. *
  414. * Inspired by array_shift
  415. *
  416. * @param string $string
  417. * @param int $index
  418. * @return string
  419. * @access private
  420. */
  421. static function _string_shift(&$string, $index = 1)
  422. {
  423. $substr = substr($string, 0, $index);
  424. $string = substr($string, $index);
  425. return $substr;
  426. }
  427. /**
  428. * Extract raw BER from Base64 encoding
  429. *
  430. * @access private
  431. * @param string $str
  432. * @return string
  433. */
  434. static function _extractBER($str)
  435. {
  436. /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them
  437. * above and beyond the ceritificate.
  438. * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line:
  439. *
  440. * Bag Attributes
  441. * localKeyID: 01 00 00 00
  442. * subject=/O=organization/OU=org unit/CN=common name
  443. * issuer=/O=organization/CN=common name
  444. */
  445. $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1);
  446. // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff
  447. $temp = preg_replace('#-+[^-]+-+#', '', $temp);
  448. // remove new lines
  449. $temp = str_replace(array("\r", "\n", ' '), '', $temp);
  450. $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? Base64::decode($temp) : false;
  451. return $temp != false ? $temp : $str;
  452. }
  453. }