mirror of
https://github.com/torrentpier/torrentpier-lts.git
synced 2025-03-01 15:21:02 +03:00
334 lines
9.5 KiB
PHP
334 lines
9.5 KiB
PHP
|
<?php
|
||
|
/**
|
||
|
* Zend Framework (http://framework.zend.com/)
|
||
|
*
|
||
|
* @link http://github.com/zendframework/zf2 for the canonical source repository
|
||
|
* @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
|
||
|
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||
|
*/
|
||
|
|
||
|
namespace Zend\Crypt\PublicKey;
|
||
|
|
||
|
use Traversable;
|
||
|
use Zend\Crypt\PublicKey\Rsa\Exception;
|
||
|
use Zend\Stdlib\ArrayUtils;
|
||
|
|
||
|
/**
|
||
|
* Implementation of the RSA public key encryption algorithm.
|
||
|
*/
|
||
|
class Rsa
|
||
|
{
|
||
|
const MODE_AUTO = 1;
|
||
|
const MODE_BASE64 = 2;
|
||
|
const MODE_RAW = 3;
|
||
|
|
||
|
/**
|
||
|
* @var RsaOptions
|
||
|
*/
|
||
|
protected $options = null;
|
||
|
|
||
|
/**
|
||
|
* RSA instance factory
|
||
|
*
|
||
|
* @param array|Traversable $options
|
||
|
* @return Rsa
|
||
|
* @throws Rsa\Exception\RuntimeException
|
||
|
* @throws Rsa\Exception\InvalidArgumentException
|
||
|
*/
|
||
|
public static function factory($options)
|
||
|
{
|
||
|
if (!extension_loaded('openssl')) {
|
||
|
throw new Exception\RuntimeException(
|
||
|
'Can not create Zend\Crypt\PublicKey\Rsa; openssl extension to be loaded'
|
||
|
);
|
||
|
}
|
||
|
|
||
|
if ($options instanceof Traversable) {
|
||
|
$options = ArrayUtils::iteratorToArray($options);
|
||
|
} elseif (!is_array($options)) {
|
||
|
throw new Exception\InvalidArgumentException(
|
||
|
'The options parameter must be an array or a Traversable'
|
||
|
);
|
||
|
}
|
||
|
|
||
|
$privateKey = null;
|
||
|
$passPhrase = isset($options['pass_phrase']) ? $options['pass_phrase'] : null;
|
||
|
if (isset($options['private_key'])) {
|
||
|
if (is_file($options['private_key'])) {
|
||
|
$privateKey = Rsa\PrivateKey::fromFile($options['private_key'], $passPhrase);
|
||
|
} elseif (is_string($options['private_key'])) {
|
||
|
$privateKey = new Rsa\PrivateKey($options['private_key'], $passPhrase);
|
||
|
} else {
|
||
|
throw new Exception\InvalidArgumentException(
|
||
|
'Parameter "private_key" must be PEM formatted string or path to key file'
|
||
|
);
|
||
|
}
|
||
|
unset($options['private_key']);
|
||
|
}
|
||
|
|
||
|
$publicKey = null;
|
||
|
if (isset($options['public_key'])) {
|
||
|
if (is_file($options['public_key'])) {
|
||
|
$publicKey = Rsa\PublicKey::fromFile($options['public_key']);
|
||
|
} elseif (is_string($options['public_key'])) {
|
||
|
$publicKey = new Rsa\PublicKey($options['public_key']);
|
||
|
} else {
|
||
|
throw new Exception\InvalidArgumentException(
|
||
|
'Parameter "public_key" must be PEM/certificate string or path to key/certificate file'
|
||
|
);
|
||
|
}
|
||
|
unset($options['public_key']);
|
||
|
}
|
||
|
|
||
|
$options = new RsaOptions($options);
|
||
|
if ($privateKey instanceof Rsa\PrivateKey) {
|
||
|
$options->setPrivateKey($privateKey);
|
||
|
}
|
||
|
if ($publicKey instanceof Rsa\PublicKey) {
|
||
|
$options->setPublicKey($publicKey);
|
||
|
}
|
||
|
|
||
|
return new Rsa($options);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Class constructor
|
||
|
*
|
||
|
* @param RsaOptions $options
|
||
|
* @throws Rsa\Exception\RuntimeException
|
||
|
*/
|
||
|
public function __construct(RsaOptions $options = null)
|
||
|
{
|
||
|
if (!extension_loaded('openssl')) {
|
||
|
throw new Exception\RuntimeException(
|
||
|
'Zend\Crypt\PublicKey\Rsa requires openssl extension to be loaded'
|
||
|
);
|
||
|
}
|
||
|
|
||
|
if ($options === null) {
|
||
|
$this->options = new RsaOptions();
|
||
|
} else {
|
||
|
$this->options = $options;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set options
|
||
|
*
|
||
|
* @param RsaOptions $options
|
||
|
* @return Rsa
|
||
|
*/
|
||
|
public function setOptions(RsaOptions $options)
|
||
|
{
|
||
|
$this->options = $options;
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get options
|
||
|
*
|
||
|
* @return RsaOptions
|
||
|
*/
|
||
|
public function getOptions()
|
||
|
{
|
||
|
return $this->options;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return last openssl error(s)
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public function getOpensslErrorString()
|
||
|
{
|
||
|
$message = '';
|
||
|
while (false !== ($error = openssl_error_string())) {
|
||
|
$message .= $error . "\n";
|
||
|
}
|
||
|
return trim($message);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sign with private key
|
||
|
*
|
||
|
* @param string $data
|
||
|
* @param Rsa\PrivateKey $privateKey
|
||
|
* @return string
|
||
|
* @throws Rsa\Exception\RuntimeException
|
||
|
*/
|
||
|
public function sign($data, Rsa\PrivateKey $privateKey = null)
|
||
|
{
|
||
|
$signature = '';
|
||
|
if (null === $privateKey) {
|
||
|
$privateKey = $this->options->getPrivateKey();
|
||
|
}
|
||
|
|
||
|
$result = openssl_sign(
|
||
|
$data,
|
||
|
$signature,
|
||
|
$privateKey->getOpensslKeyResource(),
|
||
|
$this->options->getOpensslSignatureAlgorithm()
|
||
|
);
|
||
|
if (false === $result) {
|
||
|
throw new Exception\RuntimeException(
|
||
|
'Can not generate signature; openssl ' . $this->getOpensslErrorString()
|
||
|
);
|
||
|
}
|
||
|
|
||
|
if ($this->options->getBinaryOutput()) {
|
||
|
return $signature;
|
||
|
}
|
||
|
|
||
|
return base64_encode($signature);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Verify signature with public key
|
||
|
*
|
||
|
* $signature can be encoded in base64 or not. $mode sets how the input must be processed:
|
||
|
* - MODE_AUTO: Check if the $signature is encoded in base64. Not recommended for performance.
|
||
|
* - MODE_BASE64: Decode $signature using base64 algorithm.
|
||
|
* - MODE_RAW: $signature is not encoded.
|
||
|
*
|
||
|
* @param string $data
|
||
|
* @param string $signature
|
||
|
* @param null|Rsa\PublicKey $publicKey
|
||
|
* @param int $mode Input encoding
|
||
|
* @return bool
|
||
|
* @throws Rsa\Exception\RuntimeException
|
||
|
* @see Rsa::MODE_AUTO
|
||
|
* @see Rsa::MODE_BASE64
|
||
|
* @see Rsa::MODE_RAW
|
||
|
*/
|
||
|
public function verify(
|
||
|
$data,
|
||
|
$signature,
|
||
|
Rsa\PublicKey $publicKey = null,
|
||
|
$mode = self::MODE_AUTO
|
||
|
) {
|
||
|
if (null === $publicKey) {
|
||
|
$publicKey = $this->options->getPublicKey();
|
||
|
}
|
||
|
|
||
|
switch ($mode) {
|
||
|
case self::MODE_AUTO:
|
||
|
// check if data is encoded in Base64
|
||
|
$output = base64_decode($signature, true);
|
||
|
if ((false !== $output) && ($signature === base64_encode($output))) {
|
||
|
$signature = $output;
|
||
|
}
|
||
|
break;
|
||
|
case self::MODE_BASE64:
|
||
|
$signature = base64_decode($signature);
|
||
|
break;
|
||
|
case self::MODE_RAW:
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
$result = openssl_verify(
|
||
|
$data,
|
||
|
$signature,
|
||
|
$publicKey->getOpensslKeyResource(),
|
||
|
$this->options->getOpensslSignatureAlgorithm()
|
||
|
);
|
||
|
if (-1 === $result) {
|
||
|
throw new Exception\RuntimeException(
|
||
|
'Can not verify signature; openssl ' . $this->getOpensslErrorString()
|
||
|
);
|
||
|
}
|
||
|
|
||
|
return ($result === 1);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Encrypt with private/public key
|
||
|
*
|
||
|
* @param string $data
|
||
|
* @param Rsa\AbstractKey $key
|
||
|
* @return string
|
||
|
* @throws Rsa\Exception\InvalidArgumentException
|
||
|
*/
|
||
|
public function encrypt($data, Rsa\AbstractKey $key = null)
|
||
|
{
|
||
|
if (null === $key) {
|
||
|
$key = $this->options->getPublicKey();
|
||
|
}
|
||
|
|
||
|
if (null === $key) {
|
||
|
throw new Exception\InvalidArgumentException('No key specified for the decryption');
|
||
|
}
|
||
|
|
||
|
$encrypted = $key->encrypt($data);
|
||
|
|
||
|
if ($this->options->getBinaryOutput()) {
|
||
|
return $encrypted;
|
||
|
}
|
||
|
|
||
|
return base64_encode($encrypted);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Decrypt with private/public key
|
||
|
*
|
||
|
* $data can be encoded in base64 or not. $mode sets how the input must be processed:
|
||
|
* - MODE_AUTO: Check if the $signature is encoded in base64. Not recommended for performance.
|
||
|
* - MODE_BASE64: Decode $data using base64 algorithm.
|
||
|
* - MODE_RAW: $data is not encoded.
|
||
|
*
|
||
|
* @param string $data
|
||
|
* @param Rsa\AbstractKey $key
|
||
|
* @param int $mode Input encoding
|
||
|
* @return string
|
||
|
* @throws Rsa\Exception\InvalidArgumentException
|
||
|
* @see Rsa::MODE_AUTO
|
||
|
* @see Rsa::MODE_BASE64
|
||
|
* @see Rsa::MODE_RAW
|
||
|
*/
|
||
|
public function decrypt(
|
||
|
$data,
|
||
|
Rsa\AbstractKey $key = null,
|
||
|
$mode = self::MODE_AUTO
|
||
|
) {
|
||
|
if (null === $key) {
|
||
|
$key = $this->options->getPrivateKey();
|
||
|
}
|
||
|
|
||
|
if (null === $key) {
|
||
|
throw new Exception\InvalidArgumentException('No key specified for the decryption');
|
||
|
}
|
||
|
|
||
|
switch ($mode) {
|
||
|
case self::MODE_AUTO:
|
||
|
// check if data is encoded in Base64
|
||
|
$output = base64_decode($data, true);
|
||
|
if ((false !== $output) && ($data === base64_encode($output))) {
|
||
|
$data = $output;
|
||
|
}
|
||
|
break;
|
||
|
case self::MODE_BASE64:
|
||
|
$data = base64_decode($data);
|
||
|
break;
|
||
|
case self::MODE_RAW:
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return $key->decrypt($data);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Generate new private/public key pair
|
||
|
* @see RsaOptions::generateKeys()
|
||
|
*
|
||
|
* @param array $opensslConfig
|
||
|
* @return Rsa
|
||
|
* @throws Rsa\Exception\RuntimeException
|
||
|
*/
|
||
|
public function generateKeys(array $opensslConfig = array())
|
||
|
{
|
||
|
$this->options->generateKeys($opensslConfig);
|
||
|
return $this;
|
||
|
}
|
||
|
}
|