PageRenderTime 49ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/zendframework/zend-crypt/src/PublicKey/Rsa.php

https://github.com/tmccormi/openemr
PHP | 342 lines | 205 code | 33 blank | 104 comment | 34 complexity | d498976ef2849bf0136f92a12bd2f141 MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework (http://framework.zend.com/)
  4. *
  5. * @link http://github.com/zendframework/zf2 for the canonical source repository
  6. * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. */
  9. namespace Zend\Crypt\PublicKey;
  10. use Traversable;
  11. use Zend\Crypt\PublicKey\Rsa\Exception;
  12. use Zend\Stdlib\ArrayUtils;
  13. /**
  14. * Implementation of the RSA public key encryption algorithm.
  15. */
  16. class Rsa
  17. {
  18. const MODE_AUTO = 1;
  19. const MODE_BASE64 = 2;
  20. const MODE_RAW = 3;
  21. /**
  22. * @var RsaOptions
  23. */
  24. protected $options = null;
  25. /**
  26. * RSA instance factory
  27. *
  28. * @param array|Traversable $options
  29. * @return Rsa
  30. * @throws Rsa\Exception\RuntimeException
  31. * @throws Rsa\Exception\InvalidArgumentException
  32. */
  33. public static function factory($options)
  34. {
  35. if (!extension_loaded('openssl')) {
  36. throw new Exception\RuntimeException(
  37. 'Can not create Zend\Crypt\PublicKey\Rsa; openssl extension to be loaded'
  38. );
  39. }
  40. if ($options instanceof Traversable) {
  41. $options = ArrayUtils::iteratorToArray($options);
  42. } elseif (!is_array($options)) {
  43. throw new Exception\InvalidArgumentException(
  44. 'The options parameter must be an array or a Traversable'
  45. );
  46. }
  47. $privateKey = null;
  48. $passPhrase = isset($options['pass_phrase']) ? $options['pass_phrase'] : null;
  49. if (isset($options['private_key'])) {
  50. if (is_file($options['private_key'])) {
  51. $privateKey = Rsa\PrivateKey::fromFile($options['private_key'], $passPhrase);
  52. } elseif (is_string($options['private_key'])) {
  53. $privateKey = new Rsa\PrivateKey($options['private_key'], $passPhrase);
  54. } else {
  55. throw new Exception\InvalidArgumentException(
  56. 'Parameter "private_key" must be PEM formatted string or path to key file'
  57. );
  58. }
  59. unset($options['private_key']);
  60. }
  61. $publicKey = null;
  62. if (isset($options['public_key'])) {
  63. if (is_file($options['public_key'])) {
  64. $publicKey = Rsa\PublicKey::fromFile($options['public_key']);
  65. } elseif (is_string($options['public_key'])) {
  66. $publicKey = new Rsa\PublicKey($options['public_key']);
  67. } else {
  68. throw new Exception\InvalidArgumentException(
  69. 'Parameter "public_key" must be PEM/certificate string or path to key/certificate file'
  70. );
  71. }
  72. unset($options['public_key']);
  73. }
  74. $options = new RsaOptions($options);
  75. if ($privateKey instanceof Rsa\PrivateKey) {
  76. $options->setPrivateKey($privateKey);
  77. }
  78. if ($publicKey instanceof Rsa\PublicKey) {
  79. $options->setPublicKey($publicKey);
  80. }
  81. return new Rsa($options);
  82. }
  83. /**
  84. * Class constructor
  85. *
  86. * @param RsaOptions $options
  87. * @throws Rsa\Exception\RuntimeException
  88. */
  89. public function __construct(RsaOptions $options = null)
  90. {
  91. if (!extension_loaded('openssl')) {
  92. throw new Exception\RuntimeException(
  93. 'Zend\Crypt\PublicKey\Rsa requires openssl extension to be loaded'
  94. );
  95. }
  96. if ($options === null) {
  97. $this->options = new RsaOptions();
  98. } else {
  99. $this->options = $options;
  100. }
  101. }
  102. /**
  103. * Set options
  104. *
  105. * @param RsaOptions $options
  106. * @return Rsa
  107. */
  108. public function setOptions(RsaOptions $options)
  109. {
  110. $this->options = $options;
  111. return $this;
  112. }
  113. /**
  114. * Get options
  115. *
  116. * @return RsaOptions
  117. */
  118. public function getOptions()
  119. {
  120. return $this->options;
  121. }
  122. /**
  123. * Return last openssl error(s)
  124. *
  125. * @return string
  126. */
  127. public function getOpensslErrorString()
  128. {
  129. $message = '';
  130. while (false !== ($error = openssl_error_string())) {
  131. $message .= $error . "\n";
  132. }
  133. return trim($message);
  134. }
  135. /**
  136. * Sign with private key
  137. *
  138. * @param string $data
  139. * @param Rsa\PrivateKey $privateKey
  140. * @return string
  141. * @throws Rsa\Exception\RuntimeException
  142. */
  143. public function sign($data, Rsa\PrivateKey $privateKey = null)
  144. {
  145. $signature = '';
  146. if (null === $privateKey) {
  147. $privateKey = $this->options->getPrivateKey();
  148. }
  149. $result = openssl_sign(
  150. $data,
  151. $signature,
  152. $privateKey->getOpensslKeyResource(),
  153. $this->options->getOpensslSignatureAlgorithm()
  154. );
  155. if (false === $result) {
  156. throw new Exception\RuntimeException(
  157. 'Can not generate signature; openssl ' . $this->getOpensslErrorString()
  158. );
  159. }
  160. if ($this->options->getBinaryOutput()) {
  161. return $signature;
  162. }
  163. return base64_encode($signature);
  164. }
  165. /**
  166. * Verify signature with public key
  167. *
  168. * $signature can be encoded in base64 or not. $mode sets how the input must be processed:
  169. * - MODE_AUTO: Check if the $signature is encoded in base64. Not recommended for performance.
  170. * - MODE_BASE64: Decode $signature using base64 algorithm.
  171. * - MODE_RAW: $signature is not encoded.
  172. *
  173. * @param string $data
  174. * @param string $signature
  175. * @param null|Rsa\PublicKey $publicKey
  176. * @param int $mode Input encoding
  177. * @return bool
  178. * @throws Rsa\Exception\RuntimeException
  179. * @see Rsa::MODE_AUTO
  180. * @see Rsa::MODE_BASE64
  181. * @see Rsa::MODE_RAW
  182. */
  183. public function verify(
  184. $data,
  185. $signature,
  186. Rsa\PublicKey $publicKey = null,
  187. $mode = self::MODE_AUTO
  188. ) {
  189. if (null === $publicKey) {
  190. $publicKey = $this->options->getPublicKey();
  191. }
  192. switch ($mode) {
  193. case self::MODE_AUTO:
  194. // check if data is encoded in Base64
  195. $output = base64_decode($signature, true);
  196. if ((false !== $output) && ($signature === base64_encode($output))) {
  197. $signature = $output;
  198. }
  199. break;
  200. case self::MODE_BASE64:
  201. $signature = base64_decode($signature);
  202. break;
  203. case self::MODE_RAW:
  204. default:
  205. break;
  206. }
  207. $result = openssl_verify(
  208. $data,
  209. $signature,
  210. $publicKey->getOpensslKeyResource(),
  211. $this->options->getOpensslSignatureAlgorithm()
  212. );
  213. if (-1 === $result) {
  214. throw new Exception\RuntimeException(
  215. 'Can not verify signature; openssl ' . $this->getOpensslErrorString()
  216. );
  217. }
  218. return ($result === 1);
  219. }
  220. /**
  221. * Encrypt with private/public key
  222. *
  223. * @param string $data
  224. * @param Rsa\AbstractKey $key
  225. * @return string
  226. * @throws Rsa\Exception\InvalidArgumentException
  227. */
  228. public function encrypt($data, Rsa\AbstractKey $key = null, $padding = null)
  229. {
  230. if (null === $key) {
  231. $key = $this->options->getPublicKey();
  232. }
  233. if (null === $key) {
  234. throw new Exception\InvalidArgumentException('No key specified for the decryption');
  235. }
  236. if (null === $padding) {
  237. $encrypted = $key->encrypt($data);
  238. } else {
  239. $encrypted = $key->encrypt($data, $padding);
  240. }
  241. if ($this->options->getBinaryOutput()) {
  242. return $encrypted;
  243. }
  244. return base64_encode($encrypted);
  245. }
  246. /**
  247. * Decrypt with private/public key
  248. *
  249. * $data can be encoded in base64 or not. $mode sets how the input must be processed:
  250. * - MODE_AUTO: Check if the $signature is encoded in base64. Not recommended for performance.
  251. * - MODE_BASE64: Decode $data using base64 algorithm.
  252. * - MODE_RAW: $data is not encoded.
  253. *
  254. * @param string $data
  255. * @param Rsa\AbstractKey $key
  256. * @param int $mode Input encoding
  257. * @return string
  258. * @throws Rsa\Exception\InvalidArgumentException
  259. * @see Rsa::MODE_AUTO
  260. * @see Rsa::MODE_BASE64
  261. * @see Rsa::MODE_RAW
  262. */
  263. public function decrypt(
  264. $data,
  265. Rsa\AbstractKey $key = null,
  266. $mode = self::MODE_AUTO,
  267. $padding = null
  268. ) {
  269. if (null === $key) {
  270. $key = $this->options->getPrivateKey();
  271. }
  272. if (null === $key) {
  273. throw new Exception\InvalidArgumentException('No key specified for the decryption');
  274. }
  275. switch ($mode) {
  276. case self::MODE_AUTO:
  277. // check if data is encoded in Base64
  278. $output = base64_decode($data, true);
  279. if ((false !== $output) && ($data === base64_encode($output))) {
  280. $data = $output;
  281. }
  282. break;
  283. case self::MODE_BASE64:
  284. $data = base64_decode($data);
  285. break;
  286. case self::MODE_RAW:
  287. default:
  288. break;
  289. }
  290. if (null === $padding) {
  291. return $key->decrypt($data);
  292. } else {
  293. return $key->decrypt($data, $padding);
  294. }
  295. }
  296. /**
  297. * Generate new private/public key pair
  298. * @see RsaOptions::generateKeys()
  299. *
  300. * @param array $opensslConfig
  301. * @return Rsa
  302. * @throws Rsa\Exception\RuntimeException
  303. */
  304. public function generateKeys(array $opensslConfig = [])
  305. {
  306. $this->options->generateKeys($opensslConfig);
  307. return $this;
  308. }
  309. }