This commit is contained in:
2024-08-05 22:57:28 +08:00
commit 0efda6c02a
1779 changed files with 171774 additions and 0 deletions
@@ -0,0 +1,55 @@
<?php
/*
* This file is part of the Acme PHP project.
*
* (c) Titouan Galopin <galopintitouan@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace AcmePhp\Ssl\Generator;
use AcmePhp\Ssl\PrivateKey;
/**
* Generate random RSA private key using OpenSSL.
*
* @author Jérémy Derussé <jeremy@derusse.com>
*/
class ChainPrivateKeyGenerator implements PrivateKeyGeneratorInterface
{
/** @var PrivateKeyGeneratorInterface[] */
private $generators;
/**
* @param PrivateKeyGeneratorInterface[] $generators
*/
public function __construct(iterable $generators)
{
$this->generators = $generators;
}
public function generatePrivateKey(KeyOption $keyOption): PrivateKey
{
foreach ($this->generators as $generator) {
if ($generator->supportsKeyOption($keyOption)) {
return $generator->generatePrivateKey($keyOption);
}
}
throw new \LogicException(sprintf('Unable to find a generator for a key option of type %s', \get_class($keyOption)));
}
public function supportsKeyOption(KeyOption $keyOption): bool
{
foreach ($this->generators as $generator) {
if ($generator->supportsKeyOption($keyOption)) {
return true;
}
}
return false;
}
}
@@ -0,0 +1,46 @@
<?php
/*
* This file is part of the Acme PHP project.
*
* (c) Titouan Galopin <galopintitouan@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace AcmePhp\Ssl\Generator\DhKey;
use AcmePhp\Ssl\Generator\KeyOption;
use AcmePhp\Ssl\Generator\OpensslPrivateKeyGeneratorTrait;
use AcmePhp\Ssl\Generator\PrivateKeyGeneratorInterface;
use AcmePhp\Ssl\PrivateKey;
use Webmozart\Assert\Assert;
/**
* Generate random DH private key using OpenSSL.
*
* @author Jérémy Derussé <jeremy@derusse.com>
*/
class DhKeyGenerator implements PrivateKeyGeneratorInterface
{
use OpensslPrivateKeyGeneratorTrait;
public function generatePrivateKey(KeyOption $keyOption): PrivateKey
{
Assert::isInstanceOf($keyOption, DhKeyOption::class);
return $this->generatePrivateKeyFromOpensslOptions([
'private_key_type' => OPENSSL_KEYTYPE_DH,
'dh' => [
'p' => $keyOption->getPrime(),
'g' => $keyOption->getGenerator(),
],
]);
}
public function supportsKeyOption(KeyOption $keyOption): bool
{
return $keyOption instanceof DhKeyOption;
}
}
@@ -0,0 +1,45 @@
<?php
/*
* This file is part of the Acme PHP project.
*
* (c) Titouan Galopin <galopintitouan@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace AcmePhp\Ssl\Generator\DhKey;
use AcmePhp\Ssl\Generator\KeyOption;
class DhKeyOption implements KeyOption
{
/** @var string */
private $generator;
/** @var string */
private $prime;
/**
* @param string $prime Hexadecimal representation of the prime
* @param string $generator Hexadecimal representation of the generator: ie. 02
*
* @see https://tools.ietf.org/html/rfc3526 how to choose a prime and generator numbers
*/
public function __construct(string $prime, string $generator = '02')
{
$this->generator = pack('H*', $generator);
$this->prime = pack('H*', $prime);
}
public function getGenerator(): string
{
return $this->generator;
}
public function getPrime(): string
{
return $this->prime;
}
}
@@ -0,0 +1,43 @@
<?php
/*
* This file is part of the Acme PHP project.
*
* (c) Titouan Galopin <galopintitouan@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace AcmePhp\Ssl\Generator\DsaKey;
use AcmePhp\Ssl\Generator\KeyOption;
use AcmePhp\Ssl\Generator\OpensslPrivateKeyGeneratorTrait;
use AcmePhp\Ssl\Generator\PrivateKeyGeneratorInterface;
use AcmePhp\Ssl\PrivateKey;
use Webmozart\Assert\Assert;
/**
* Generate random DSA private key using OpenSSL.
*
* @author Jérémy Derussé <jeremy@derusse.com>
*/
class DsaKeyGenerator implements PrivateKeyGeneratorInterface
{
use OpensslPrivateKeyGeneratorTrait;
public function generatePrivateKey(KeyOption $keyOption): PrivateKey
{
Assert::isInstanceOf($keyOption, DsaKeyOption::class);
return $this->generatePrivateKeyFromOpensslOptions([
'private_key_type' => OPENSSL_KEYTYPE_DSA,
'private_key_bits' => $keyOption->getBits(),
]);
}
public function supportsKeyOption(KeyOption $keyOption): bool
{
return $keyOption instanceof DsaKeyOption;
}
}
@@ -0,0 +1,30 @@
<?php
/*
* This file is part of the Acme PHP project.
*
* (c) Titouan Galopin <galopintitouan@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace AcmePhp\Ssl\Generator\DsaKey;
use AcmePhp\Ssl\Generator\KeyOption;
class DsaKeyOption implements KeyOption
{
/** @var int */
private $bits;
public function __construct(int $bits = 2048)
{
$this->bits = $bits;
}
public function getBits(): int
{
return $this->bits;
}
}
@@ -0,0 +1,43 @@
<?php
/*
* This file is part of the Acme PHP project.
*
* (c) Titouan Galopin <galopintitouan@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace AcmePhp\Ssl\Generator\EcKey;
use AcmePhp\Ssl\Generator\KeyOption;
use AcmePhp\Ssl\Generator\OpensslPrivateKeyGeneratorTrait;
use AcmePhp\Ssl\Generator\PrivateKeyGeneratorInterface;
use AcmePhp\Ssl\PrivateKey;
use Webmozart\Assert\Assert;
/**
* Generate random EC private key using OpenSSL.
*
* @author Jérémy Derussé <jeremy@derusse.com>
*/
class EcKeyGenerator implements PrivateKeyGeneratorInterface
{
use OpensslPrivateKeyGeneratorTrait;
public function generatePrivateKey(KeyOption $keyOption): PrivateKey
{
Assert::isInstanceOf($keyOption, EcKeyOption::class);
return $this->generatePrivateKeyFromOpensslOptions([
'private_key_type' => OPENSSL_KEYTYPE_EC,
'curve_name' => $keyOption->getCurveName(),
]);
}
public function supportsKeyOption(KeyOption $keyOption): bool
{
return $keyOption instanceof EcKeyOption;
}
}
@@ -0,0 +1,33 @@
<?php
/*
* This file is part of the Acme PHP project.
*
* (c) Titouan Galopin <galopintitouan@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace AcmePhp\Ssl\Generator\EcKey;
use AcmePhp\Ssl\Generator\KeyOption;
use Webmozart\Assert\Assert;
class EcKeyOption implements KeyOption
{
/** @var string */
private $curveName;
public function __construct(string $curveName = 'secp384r1')
{
Assert::oneOf($curveName, openssl_get_curve_names(), 'The given curve %s is not supported. Available curves are: %s');
$this->curveName = $curveName;
}
public function getCurveName(): string
{
return $this->curveName;
}
}
+16
View File
@@ -0,0 +1,16 @@
<?php
/*
* This file is part of the Acme PHP project.
*
* (c) Titouan Galopin <galopintitouan@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace AcmePhp\Ssl\Generator;
interface KeyOption
{
}
+66
View File
@@ -0,0 +1,66 @@
<?php
/*
* This file is part of the Acme PHP project.
*
* (c) Titouan Galopin <galopintitouan@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace AcmePhp\Ssl\Generator;
use AcmePhp\Ssl\Exception\KeyGenerationException;
use AcmePhp\Ssl\Exception\KeyPairGenerationException;
use AcmePhp\Ssl\Generator\DhKey\DhKeyGenerator;
use AcmePhp\Ssl\Generator\DsaKey\DsaKeyGenerator;
use AcmePhp\Ssl\Generator\EcKey\EcKeyGenerator;
use AcmePhp\Ssl\Generator\RsaKey\RsaKeyGenerator;
use AcmePhp\Ssl\Generator\RsaKey\RsaKeyOption;
use AcmePhp\Ssl\KeyPair;
/**
* Generate random KeyPair using OpenSSL.
*
* @author Jérémy Derussé <jeremy@derusse.com>
*/
class KeyPairGenerator
{
private $generator;
public function __construct(PrivateKeyGeneratorInterface $generator = null)
{
$this->generator = $generator ?: new ChainPrivateKeyGenerator(
[
new RsaKeyGenerator(),
new EcKeyGenerator(),
new DhKeyGenerator(),
new DsaKeyGenerator(),
]
);
}
/**
* @param KeyOption|null $keyOption configuration of the key to generate
*
* @throws KeyPairGenerationException when OpenSSL failed to generate keys
*/
public function generateKeyPair(KeyOption $keyOption = null): KeyPair
{
if (null === $keyOption) {
$keyOption = new RsaKeyOption();
}
try {
$privateKey = $this->generator->generatePrivateKey($keyOption);
} catch (KeyGenerationException $e) {
throw new KeyPairGenerationException('Fail to generate a KeyPair with the given options', 0, $e);
}
return new KeyPair(
$privateKey->getPublicKey(),
$privateKey
);
}
}
@@ -0,0 +1,38 @@
<?php
/*
* This file is part of the Acme PHP project.
*
* (c) Titouan Galopin <galopintitouan@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace AcmePhp\Ssl\Generator;
use AcmePhp\Ssl\Exception\KeyGenerationException;
use AcmePhp\Ssl\Exception\KeyPairGenerationException;
use AcmePhp\Ssl\PrivateKey;
trait OpensslPrivateKeyGeneratorTrait
{
private function generatePrivateKeyFromOpensslOptions(array $opensslOptions): PrivateKey
{
$resource = openssl_pkey_new($opensslOptions);
if (!$resource) {
throw new KeyGenerationException(sprintf('OpenSSL key creation failed during generation with error: %s', openssl_error_string()));
}
if (!openssl_pkey_export($resource, $privateKey)) {
throw new KeyPairGenerationException(sprintf('OpenSSL key export failed during generation with error: %s', openssl_error_string()));
}
// PHP 8 automatically frees the key instance and deprecates the function
if (\PHP_VERSION_ID < 80000) {
openssl_free_key($resource);
}
return new PrivateKey($privateKey);
}
}
@@ -0,0 +1,39 @@
<?php
/*
* This file is part of the Acme PHP project.
*
* (c) Titouan Galopin <galopintitouan@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace AcmePhp\Ssl\Generator;
use AcmePhp\Ssl\Exception\KeyGenerationException;
use AcmePhp\Ssl\PrivateKey;
/**
* Generate random private key.
*
* @author Jérémy Derussé <jeremy@derusse.com>
*/
interface PrivateKeyGeneratorInterface
{
/**
* Generate a PrivateKey.
*
* @param KeyOption $keyOption configuration of the key to generate
*
* @throws KeyGenerationException when OpenSSL failed to generate keys
*/
public function generatePrivateKey(KeyOption $keyOption): PrivateKey;
/**
* Returns whether the instance is able to generator a private key from the given option.
*
* @param KeyOption $keyOption configuration of the key to generate
*/
public function supportsKeyOption(KeyOption $keyOption): bool;
}
@@ -0,0 +1,43 @@
<?php
/*
* This file is part of the Acme PHP project.
*
* (c) Titouan Galopin <galopintitouan@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace AcmePhp\Ssl\Generator\RsaKey;
use AcmePhp\Ssl\Generator\KeyOption;
use AcmePhp\Ssl\Generator\OpensslPrivateKeyGeneratorTrait;
use AcmePhp\Ssl\Generator\PrivateKeyGeneratorInterface;
use AcmePhp\Ssl\PrivateKey;
use Webmozart\Assert\Assert;
/**
* Generate random RSA private key using OpenSSL.
*
* @author Jérémy Derussé <jeremy@derusse.com>
*/
class RsaKeyGenerator implements PrivateKeyGeneratorInterface
{
use OpensslPrivateKeyGeneratorTrait;
public function generatePrivateKey(KeyOption $keyOption): PrivateKey
{
Assert::isInstanceOf($keyOption, RsaKeyOption::class);
return $this->generatePrivateKeyFromOpensslOptions([
'private_key_type' => OPENSSL_KEYTYPE_RSA,
'private_key_bits' => $keyOption->getBits(),
]);
}
public function supportsKeyOption(KeyOption $keyOption): bool
{
return $keyOption instanceof RsaKeyOption;
}
}
@@ -0,0 +1,30 @@
<?php
/*
* This file is part of the Acme PHP project.
*
* (c) Titouan Galopin <galopintitouan@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace AcmePhp\Ssl\Generator\RsaKey;
use AcmePhp\Ssl\Generator\KeyOption;
class RsaKeyOption implements KeyOption
{
/** @var int */
private $bits;
public function __construct(int $bits = 4096)
{
$this->bits = $bits;
}
public function getBits(): int
{
return $this->bits;
}
}