PKCS8.php 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. <?php
  2. /**
  3. * PKCS#8 Formatted RSA Key Handler
  4. *
  5. * PHP version 5
  6. *
  7. * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set)
  8. *
  9. * Has the following header:
  10. *
  11. * -----BEGIN PUBLIC KEY-----
  12. *
  13. * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
  14. * is specific to private keys it's basically creating a DER-encoded wrapper
  15. * for keys. This just extends that same concept to public keys (much like ssh-keygen)
  16. *
  17. * @category Crypt
  18. * @package RSA
  19. * @author Jim Wigginton <terrafrost@php.net>
  20. * @copyright 2015 Jim Wigginton
  21. * @license http://www.opensource.org/licenses/mit-license.html MIT License
  22. * @link http://phpseclib.sourceforge.net
  23. */
  24. namespace phpseclib\Crypt\RSA;
  25. use ParagonIE\ConstantTime\Base64;
  26. use phpseclib\Crypt\DES;
  27. use phpseclib\Crypt\Random;
  28. use phpseclib\Math\BigInteger;
  29. /**
  30. * PKCS#8 Formatted RSA Key Handler
  31. *
  32. * @package RSA
  33. * @author Jim Wigginton <terrafrost@php.net>
  34. * @access public
  35. */
  36. class PKCS8 extends PKCS
  37. {
  38. /**
  39. * Convert a private key to the appropriate format.
  40. *
  41. * @access public
  42. * @param \phpseclib\Math\BigInteger $n
  43. * @param \phpseclib\Math\BigInteger $e
  44. * @param \phpseclib\Math\BigInteger $d
  45. * @param array $primes
  46. * @param array $exponents
  47. * @param array $coefficients
  48. * @param string $password optional
  49. * @return string
  50. */
  51. static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, $primes, $exponents, $coefficients, $password = '')
  52. {
  53. $num_primes = count($primes);
  54. $raw = array(
  55. 'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi
  56. 'modulus' => $n->toBytes(true),
  57. 'publicExponent' => $e->toBytes(true),
  58. 'privateExponent' => $d->toBytes(true),
  59. 'prime1' => $primes[1]->toBytes(true),
  60. 'prime2' => $primes[2]->toBytes(true),
  61. 'exponent1' => $exponents[1]->toBytes(true),
  62. 'exponent2' => $exponents[2]->toBytes(true),
  63. 'coefficient' => $coefficients[2]->toBytes(true)
  64. );
  65. $components = array();
  66. foreach ($raw as $name => $value) {
  67. $components[$name] = pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($value)), $value);
  68. }
  69. $RSAPrivateKey = implode('', $components);
  70. if ($num_primes > 2) {
  71. $OtherPrimeInfos = '';
  72. for ($i = 3; $i <= $num_primes; $i++) {
  73. // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
  74. //
  75. // OtherPrimeInfo ::= SEQUENCE {
  76. // prime INTEGER, -- ri
  77. // exponent INTEGER, -- di
  78. // coefficient INTEGER -- ti
  79. // }
  80. $OtherPrimeInfo = pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true));
  81. $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true));
  82. $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true));
  83. $OtherPrimeInfos.= pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo);
  84. }
  85. $RSAPrivateKey.= pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos);
  86. }
  87. $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
  88. $rsaOID = "\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00"; // hex version of MA0GCSqGSIb3DQEBAQUA
  89. $RSAPrivateKey = pack(
  90. 'Ca*a*Ca*a*',
  91. self::ASN1_INTEGER,
  92. "\01\00",
  93. $rsaOID,
  94. 4,
  95. self::_encodeLength(strlen($RSAPrivateKey)),
  96. $RSAPrivateKey
  97. );
  98. $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
  99. if (!empty($password) || is_string($password)) {
  100. $salt = Random::string(8);
  101. $iterationCount = 2048;
  102. $crypto = new DES(DES::MODE_CBC);
  103. $crypto->setPassword($password, 'pbkdf1', 'md5', $salt, $iterationCount);
  104. $RSAPrivateKey = $crypto->encrypt($RSAPrivateKey);
  105. $parameters = pack(
  106. 'Ca*a*Ca*N',
  107. self::ASN1_OCTETSTRING,
  108. self::_encodeLength(strlen($salt)),
  109. $salt,
  110. self::ASN1_INTEGER,
  111. self::_encodeLength(4),
  112. $iterationCount
  113. );
  114. $pbeWithMD5AndDES_CBC = "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03";
  115. $encryptionAlgorithm = pack(
  116. 'Ca*a*Ca*a*',
  117. self::ASN1_OBJECT,
  118. self::_encodeLength(strlen($pbeWithMD5AndDES_CBC)),
  119. $pbeWithMD5AndDES_CBC,
  120. self::ASN1_SEQUENCE,
  121. self::_encodeLength(strlen($parameters)),
  122. $parameters
  123. );
  124. $RSAPrivateKey = pack(
  125. 'Ca*a*Ca*a*',
  126. self::ASN1_SEQUENCE,
  127. self::_encodeLength(strlen($encryptionAlgorithm)),
  128. $encryptionAlgorithm,
  129. self::ASN1_OCTETSTRING,
  130. self::_encodeLength(strlen($RSAPrivateKey)),
  131. $RSAPrivateKey
  132. );
  133. $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
  134. $RSAPrivateKey = "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" .
  135. chunk_split(Base64::encode($RSAPrivateKey), 64) .
  136. '-----END ENCRYPTED PRIVATE KEY-----';
  137. } else {
  138. $RSAPrivateKey = "-----BEGIN PRIVATE KEY-----\r\n" .
  139. chunk_split(Base64::encode($RSAPrivateKey), 64) .
  140. '-----END PRIVATE KEY-----';
  141. }
  142. return $RSAPrivateKey;
  143. }
  144. /**
  145. * Convert a public key to the appropriate format
  146. *
  147. * @access public
  148. * @param \phpseclib\Math\BigInteger $n
  149. * @param \phpseclib\Math\BigInteger $e
  150. * @return string
  151. */
  152. static function savePublicKey(BigInteger $n, BigInteger $e)
  153. {
  154. $modulus = $n->toBytes(true);
  155. $publicExponent = $e->toBytes(true);
  156. // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.1>:
  157. // RSAPublicKey ::= SEQUENCE {
  158. // modulus INTEGER, -- n
  159. // publicExponent INTEGER -- e
  160. // }
  161. $components = array(
  162. 'modulus' => pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($modulus)), $modulus),
  163. 'publicExponent' => pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($publicExponent)), $publicExponent)
  164. );
  165. $RSAPublicKey = pack(
  166. 'Ca*a*a*',
  167. self::ASN1_SEQUENCE,
  168. self::_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])),
  169. $components['modulus'],
  170. $components['publicExponent']
  171. );
  172. // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
  173. $rsaOID = "\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00"; // hex version of MA0GCSqGSIb3DQEBAQUA
  174. $RSAPublicKey = chr(0) . $RSAPublicKey;
  175. $RSAPublicKey = chr(3) . self::_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey;
  176. $RSAPublicKey = pack(
  177. 'Ca*a*',
  178. self::ASN1_SEQUENCE,
  179. self::_encodeLength(strlen($rsaOID . $RSAPublicKey)),
  180. $rsaOID . $RSAPublicKey
  181. );
  182. $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
  183. chunk_split(Base64::encode($RSAPublicKey), 64) .
  184. '-----END PUBLIC KEY-----';
  185. return $RSAPublicKey;
  186. }
  187. }