121 lines
3.2 KiB
PHP
121 lines
3.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Jose\Component\KeyManagement\KeyConverter;
|
|
|
|
use InvalidArgumentException;
|
|
use Jose\Component\Core\Util\Base64UrlSafe;
|
|
use SpomkyLabs\Pki\CryptoEncoding\PEM;
|
|
use SpomkyLabs\Pki\CryptoTypes\Asymmetric\EC\ECPrivateKey;
|
|
use SpomkyLabs\Pki\CryptoTypes\Asymmetric\EC\ECPublicKey;
|
|
use Throwable;
|
|
use function array_key_exists;
|
|
use function is_string;
|
|
|
|
/**
|
|
* @internal
|
|
*/
|
|
final class ECKey
|
|
{
|
|
private array $values = [];
|
|
|
|
private function __construct(array $data)
|
|
{
|
|
$this->loadJWK($data);
|
|
}
|
|
|
|
public static function createFromPEM(string $pem): self
|
|
{
|
|
$data = self::loadPEM($pem);
|
|
|
|
return new self($data);
|
|
}
|
|
|
|
public static function toPublic(self $private): self
|
|
{
|
|
$data = $private->toArray();
|
|
if (array_key_exists('d', $data)) {
|
|
unset($data['d']);
|
|
}
|
|
|
|
return new self($data);
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
*/
|
|
public function toArray()
|
|
{
|
|
return $this->values;
|
|
}
|
|
|
|
private static function loadPEM(string $data): array
|
|
{
|
|
$pem = PEM::fromString($data);
|
|
try {
|
|
$key = ECPrivateKey::fromPEM($pem);
|
|
|
|
return [
|
|
'kty' => 'EC',
|
|
'crv' => self::getCurve($key->namedCurve()),
|
|
'd' => Base64UrlSafe::encodeUnpadded($key->privateKeyOctets()),
|
|
'x' => Base64UrlSafe::encodeUnpadded($key->publicKey()->curvePointOctets()[0]),
|
|
'y' => Base64UrlSafe::encodeUnpadded($key->publicKey()->curvePointOctets()[1]),
|
|
];
|
|
} catch (Throwable) {
|
|
}
|
|
try {
|
|
$key = ECPublicKey::fromPEM($pem);
|
|
return [
|
|
'kty' => 'EC',
|
|
'crv' => self::getCurve($key->namedCurve()),
|
|
'x' => Base64UrlSafe::encodeUnpadded($key->curvePointOctets()[0]),
|
|
'y' => Base64UrlSafe::encodeUnpadded($key->curvePointOctets()[1]),
|
|
];
|
|
} catch (Throwable) {
|
|
}
|
|
throw new InvalidArgumentException('Unable to load the key.');
|
|
}
|
|
|
|
private static function getCurve(string $oid): string
|
|
{
|
|
$curves = self::getSupportedCurves();
|
|
$curve = array_search($oid, $curves, true);
|
|
if (! is_string($curve)) {
|
|
throw new InvalidArgumentException('Unsupported OID.');
|
|
}
|
|
|
|
return $curve;
|
|
}
|
|
|
|
private static function getSupportedCurves(): array
|
|
{
|
|
return [
|
|
'P-256' => '1.2.840.10045.3.1.7',
|
|
'P-384' => '1.3.132.0.34',
|
|
'P-521' => '1.3.132.0.35',
|
|
];
|
|
}
|
|
|
|
private function loadJWK(array $jwk): void
|
|
{
|
|
$keys = [
|
|
'kty' => 'The key parameter "kty" is missing.',
|
|
'crv' => 'Curve parameter is missing',
|
|
'x' => 'Point parameters are missing.',
|
|
'y' => 'Point parameters are missing.',
|
|
];
|
|
foreach ($keys as $k => $v) {
|
|
if (! array_key_exists($k, $jwk)) {
|
|
throw new InvalidArgumentException($v);
|
|
}
|
|
}
|
|
|
|
if ($jwk['kty'] !== 'EC') {
|
|
throw new InvalidArgumentException('JWK is not an Elliptic Curve key.');
|
|
}
|
|
$this->values = $jwk;
|
|
}
|
|
}
|