MSBLOB.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. <?php
  2. /**
  3. * Miccrosoft BLOB Formatted RSA Key Handler
  4. *
  5. * More info:
  6. *
  7. * https://msdn.microsoft.com/en-us/library/windows/desktop/aa375601(v=vs.85).aspx
  8. *
  9. * PHP version 5
  10. *
  11. * @category Crypt
  12. * @package RSA
  13. * @author Jim Wigginton <terrafrost@php.net>
  14. * @copyright 2015 Jim Wigginton
  15. * @license http://www.opensource.org/licenses/mit-license.html MIT License
  16. * @link http://phpseclib.sourceforge.net
  17. */
  18. namespace phpseclib\Crypt\RSA;
  19. use ParagonIE\ConstantTime\Base64;
  20. use phpseclib\Math\BigInteger;
  21. /**
  22. * Microsoft BLOB Formatted RSA Key Handler
  23. *
  24. * @package RSA
  25. * @author Jim Wigginton <terrafrost@php.net>
  26. * @access public
  27. */
  28. class MSBLOB
  29. {
  30. /**#@+
  31. * @access private
  32. */
  33. /**
  34. * Public/Private Key Pair
  35. */
  36. const PRIVATEKEYBLOB = 0x7;
  37. /**
  38. * Public Key
  39. */
  40. const PUBLICKEYBLOB = 0x6;
  41. /**
  42. * Public Key
  43. */
  44. const PUBLICKEYBLOBEX = 0xA;
  45. /**
  46. * RSA public key exchange algorithm
  47. */
  48. const CALG_RSA_KEYX = 0x0000A400;
  49. /**
  50. * RSA public key exchange algorithm
  51. */
  52. const CALG_RSA_SIGN = 0x00002400;
  53. /**
  54. * Public Key
  55. */
  56. const RSA1 = 0x31415352;
  57. /**
  58. * Private Key
  59. */
  60. const RSA2 = 0x32415352;
  61. /**#@-*/
  62. /**
  63. * Break a public or private key down into its constituent components
  64. *
  65. * @access public
  66. * @param string $key
  67. * @param string $password optional
  68. * @return array
  69. */
  70. static function load($key, $password = '')
  71. {
  72. if (!is_string($key)) {
  73. return false;
  74. }
  75. $key = Base64::decode($key);
  76. if (!is_string($key) || strlen($key) < 20) {
  77. return false;
  78. }
  79. // PUBLICKEYSTRUC publickeystruc
  80. // https://msdn.microsoft.com/en-us/library/windows/desktop/aa387453(v=vs.85).aspx
  81. extract(unpack('atype/aversion/vreserved/Valgo', self::_string_shift($key, 8)));
  82. switch (ord($type)) {
  83. case self::PUBLICKEYBLOB:
  84. case self::PUBLICKEYBLOBEX:
  85. $publickey = true;
  86. break;
  87. case self::PRIVATEKEYBLOB:
  88. $publickey = false;
  89. break;
  90. default:
  91. return false;
  92. }
  93. $components = array('isPublicKey' => $publickey);
  94. // https://msdn.microsoft.com/en-us/library/windows/desktop/aa375549(v=vs.85).aspx
  95. switch ($algo) {
  96. case self::CALG_RSA_KEYX:
  97. case self::CALG_RSA_SIGN:
  98. break;
  99. default:
  100. return false;
  101. }
  102. // RSAPUBKEY rsapubkey
  103. // https://msdn.microsoft.com/en-us/library/windows/desktop/aa387685(v=vs.85).aspx
  104. // could do V for pubexp but that's unsigned 32-bit whereas some PHP installs only do signed 32-bit
  105. extract(unpack('Vmagic/Vbitlen/a4pubexp', self::_string_shift($key, 12)));
  106. switch ($magic) {
  107. case self::RSA2:
  108. $components['isPublicKey'] = false;
  109. case self::RSA1:
  110. break;
  111. default:
  112. return false;
  113. }
  114. $baseLength = $bitlen / 16;
  115. if (strlen($key) != 2 * $baseLength && strlen($key) != 9 * $baseLength) {
  116. return false;
  117. }
  118. $components[$components['isPublicKey'] ? 'publicExponent' : 'privateExponent'] = new BigInteger(strrev($pubexp), 256);
  119. // BYTE modulus[rsapubkey.bitlen/8]
  120. $components['modulus'] = new BigInteger(strrev(self::_string_shift($key, $bitlen / 8)), 256);
  121. if ($publickey) {
  122. return $components;
  123. }
  124. $components['isPublicKey'] = false;
  125. // BYTE prime1[rsapubkey.bitlen/16]
  126. $components['primes'] = array(1 => new BigInteger(strrev(self::_string_shift($key, $bitlen / 16)), 256));
  127. // BYTE prime2[rsapubkey.bitlen/16]
  128. $components['primes'][] = new BigInteger(strrev(self::_string_shift($key, $bitlen / 16)), 256);
  129. // BYTE exponent1[rsapubkey.bitlen/16]
  130. $components['exponents'] = array(1 => new BigInteger(strrev(self::_string_shift($key, $bitlen / 16)), 256));
  131. // BYTE exponent2[rsapubkey.bitlen/16]
  132. $components['exponents'][] = new BigInteger(strrev(self::_string_shift($key, $bitlen / 16)), 256);
  133. // BYTE coefficient[rsapubkey.bitlen/16]
  134. $components['coefficients'] = array(2 => new BigInteger(strrev(self::_string_shift($key, $bitlen / 16)), 256));
  135. if (isset($components['privateExponent'])) {
  136. $components['publicExponent'] = $components['privateExponent'];
  137. }
  138. // BYTE privateExponent[rsapubkey.bitlen/8]
  139. $components['privateExponent'] = new BigInteger(strrev(self::_string_shift($key, $bitlen / 8)), 256);
  140. return $components;
  141. }
  142. /**
  143. * Convert a private key to the appropriate format.
  144. *
  145. * @access public
  146. * @param \phpseclib\Math\BigInteger $n
  147. * @param \phpseclib\Math\BigInteger $e
  148. * @param \phpseclib\Math\BigInteger $d
  149. * @param array $primes
  150. * @param array $exponents
  151. * @param array $coefficients
  152. * @param string $password optional
  153. * @return string
  154. */
  155. static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, $primes, $exponents, $coefficients, $password = '')
  156. {
  157. $n = strrev($n->toBytes());
  158. $e = str_pad(strrev($e->toBytes()), 4, "\0");
  159. $key = pack('aavV', chr(self::PRIVATEKEYBLOB), chr(2), 0, self::CALG_RSA_KEYX);
  160. $key.= pack('VVa*', self::RSA2, 8 * strlen($n), $e);
  161. $key.= $n;
  162. $key.= strrev($primes[1]->toBytes());
  163. $key.= strrev($primes[2]->toBytes());
  164. $key.= strrev($exponents[1]->toBytes());
  165. $key.= strrev($exponents[2]->toBytes());
  166. $key.= strrev($coefficients[1]->toBytes());
  167. $key.= strrev($d->toBytes());
  168. return Base64::encode($key);
  169. }
  170. /**
  171. * Convert a public key to the appropriate format
  172. *
  173. * @access public
  174. * @param \phpseclib\Math\BigInteger $n
  175. * @param \phpseclib\Math\BigInteger $e
  176. * @return string
  177. */
  178. static function savePublicKey(BigInteger $n, BigInteger $e)
  179. {
  180. $n = strrev($n->toBytes());
  181. $e = str_pad(strrev($e->toBytes()), 4, "\0");
  182. $key = pack('aavV', chr(self::PUBLICKEYBLOB), chr(2), 0, self::CALG_RSA_KEYX);
  183. $key.= pack('VVa*', self::RSA1, 8 * strlen($n), $e);
  184. $key.= $n;
  185. return Base64::encode($key);
  186. }
  187. /**
  188. * String Shift
  189. *
  190. * Inspired by array_shift
  191. *
  192. * @param string $string
  193. * @param int $index
  194. * @return string
  195. * @access private
  196. */
  197. static function _string_shift(&$string, $index = 1)
  198. {
  199. $substr = substr($string, 0, $index);
  200. $string = substr($string, $index);
  201. return $substr;
  202. }
  203. }