diff --git a/.gitignore b/.gitignore
index b99bef9..1090ab5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@
.phpunit.result.cache
composer.lock
vendor
+coverage.xml
diff --git a/.travis.yml b/.travis.yml
index e2bb3fd..639ddcb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,8 +2,15 @@ language: php
php:
- 7.2
+ - 7.3
sudo: false
-before_script: composer install
-script: phpunit
+before_script:
+ - composer install
+
+script:
+ - phpunit
+
+after_success:
+ - bash <(curl -s https://codecov.io/bash)
diff --git a/README.md b/README.md
index 973c408..700a167 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,120 @@
# php-jwk
+
A small PHP library to handle JWKs (Json Web Keys)
+
+This library helps to create json web key sets from PEM and is also able to pull out PEMs from json web key sets.
+
+Please note that **only RSA keys are supported at the moment!**
+
+See [JSON Web Key RFC](https://tools.ietf.org/html/rfc7517) for reference.
+
+## Installation
+
+This library requires PHP version 7.2 or higher and can be installed with composer:
+
+```bash
+composer require strobotti/php-jwk
+```
+
+## Example usage
+
+See full example [here](blob/master/examples/full-flow.php).
+
+### Create a key-object from PEM
+
+```php
+ 'sig',
+ 'alg' => 'RS256',
+ 'kid' => 'eXaunmL',
+];
+
+$keyFactory = new Strobotti\JWK\KeyFactory();
+$key = $keyFactory->createFromPem($pem, $options);
+
+echo "$key";
+```
+
+Outputs:
+
+```json
+{
+ "kty": "RSA",
+ "use": "sig",
+ "alg": "RS256",
+ "kid": "eXaunmL",
+ "n": "4dGQ7bQK8LgILOdLsYzfZjkEAoQeVC_aqyc8GC6RX7dq_KvRAQAWPvkam8VQv4GK5T4ogklEKEvj5ISBamdDNq1n52TpxQwI2EqxSk7I9fKPKhRt4F8-2yETlYvye-2s6NeWJim0KBtOVrk0gWvEDgd6WOqJl_yt5WBISvILNyVg1qAAM8JeX6dRPosahRVDjA52G2X-Tip84wqwyRpUlq2ybzcLh3zyhCitBOebiRWDQfG26EH9lTlJhll-p_Dg8vAXxJLIJ4SNLcqgFeZe4OfHLgdzMvxXZJnPp_VgmkcpUdRotazKZumj6dBPcXI_XID4Z4Z3OM1KrZPJNdUhxw",
+ "e": "AQAB"
+}
+```
+
+### Create a JWK set (jwks) from a key
+
+```php
+addKey($key);
+
+echo "$keySet" ;
+
+```
+
+Outputs:
+
+```json
+{
+ "keys": [
+ {
+ "kty": "RSA",
+ "use": "sig",
+ "alg": "RS256",
+ "kid": "eXaunmL",
+ "n": "4dGQ7bQK8LgILOdLsYzfZjkEAoQeVC_aqyc8GC6RX7dq_KvRAQAWPvkam8VQv4GK5T4ogklEKEvj5ISBamdDNq1n52TpxQwI2EqxSk7I9fKPKhRt4F8-2yETlYvye-2s6NeWJim0KBtOVrk0gWvEDgd6WOqJl_yt5WBISvILNyVg1qAAM8JeX6dRPosahRVDjA52G2X-Tip84wqwyRpUlq2ybzcLh3zyhCitBOebiRWDQfG26EH9lTlJhll-p_Dg8vAXxJLIJ4SNLcqgFeZe4OfHLgdzMvxXZJnPp_VgmkcpUdRotazKZumj6dBPcXI_XID4Z4Z3OM1KrZPJNdUhxw",
+ "e": "AQAB"
+ }
+ ]
+}
+```
+
+### Get a key from keyset by `kid` and convert it to PEM
+
+```php
+getKeyById('eXaunmL');
+$pem = (new \Strobotti\JWK\KeyConverter())->keyToPem($key);
+
+echo "$pem";
+
+```
+
+Outputs:
+
+```text
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4dGQ7bQK8LgILOdLsYzf
+ZjkEAoQeVC/aqyc8GC6RX7dq/KvRAQAWPvkam8VQv4GK5T4ogklEKEvj5ISBamdD
+Nq1n52TpxQwI2EqxSk7I9fKPKhRt4F8+2yETlYvye+2s6NeWJim0KBtOVrk0gWvE
+Dgd6WOqJl/yt5WBISvILNyVg1qAAM8JeX6dRPosahRVDjA52G2X+Tip84wqwyRpU
+lq2ybzcLh3zyhCitBOebiRWDQfG26EH9lTlJhll+p/Dg8vAXxJLIJ4SNLcqgFeZe
+4OfHLgdzMvxXZJnPp/VgmkcpUdRotazKZumj6dBPcXI/XID4Z4Z3OM1KrZPJNdUh
+xwIDAQAB
+-----END PUBLIC KEY-----
+```
diff --git a/examples/full-flow.php b/examples/full-flow.php
new file mode 100644
index 0000000..ab37401
--- /dev/null
+++ b/examples/full-flow.php
@@ -0,0 +1,44 @@
+ 'sig',
+ 'alg' => 'RS256',
+ 'kid' => 'eXaunmL',
+];
+
+$keyFactory = new KeyFactory();
+$key = $keyFactory->createFromPem($pem, $options);
+
+echo $key . PHP_EOL . PHP_EOL;
+
+echo "Adding the key to the KeySet:" . PHP_EOL;
+
+$keySet = new \Strobotti\JWK\KeySet();
+$keySet->addKey($key);
+
+echo $keySet . PHP_EOL . PHP_EOL;
+
+echo "Fetching the key by it's ID (`kid`) and convert it back to PEM:" . PHP_EOL;
+
+$key = $keySet->getKeyById('eXaunmL');
+$pem = (new \Strobotti\JWK\KeyConverter())->keyToPem($key);
+
+echo $pem . PHP_EOL . PHP_EOL;
diff --git a/phpunit.xml b/phpunit.xml
index 8a97eff..80dd7a5 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -6,4 +6,12 @@
tests
+
+
+ ./src/
+
+
+
+
+
diff --git a/src/Converter.php b/src/Converter.php
deleted file mode 100644
index 36f769b..0000000
--- a/src/Converter.php
+++ /dev/null
@@ -1,86 +0,0 @@
-
- * @license https://opensource.org/licenses/MIT MIT
- * @link https://github.com/Strobotti/php-jwk
- */
-class Converter
-{
- /**
- * @param Key $key
- *
- * @return string
- */
- public function keyToPem(Key $key): string
- {
- // TODO implement strategies to support different key types
- $rsa = new RSA();
-
- $modulus = $this->base64UrlDecode($key->getRsaModulus(), true);
-
- $rsa->loadKey([
- 'e' => new BigInteger(\base64_decode($key->getRsaExponent(), true), 256),
- 'n' => new BigInteger($modulus, 256),
- ]);
-
- return $rsa->getPublicKey();
- }
-
- /**
- * @param string $pem A PEM encoded (RSA) Public Key
- * @param array $options An array of key-options, such as ['kid' => 'eXaunmL', 'use' => 'sig', ...]
- *
- * @return Key
- */
- public function pemToKey(string $pem, array $options = []): Key
- {
- $keyInfo = openssl_pkey_get_details(openssl_pkey_get_public($pem));
-
- $jsonData = array_merge(
- $options,
- [
- 'kty' => 'RSA',
- 'n' => $this->base64UrlEncode($keyInfo['rsa']['n']),
- 'e' => $this->base64UrlEncode($keyInfo['rsa']['e']),
- ]
- );
-
- return Key::createFromJSON(json_encode($jsonData));
- }
-
- /**
- * https://tools.ietf.org/html/rfc4648#section-5.
- *
- * @param string $data
- * @param bool $strict
- *
- * @return string
- */
- private function base64UrlDecode(string $data, $strict = false): string
- {
- $b64 = \strtr($data, '-_', '+/');
-
- return \base64_decode($b64, $strict);
- }
-
- /**
- * https://tools.ietf.org/html/rfc4648#section-5.
- *
- * @param string $data
- *
- * @return string
- */
- private function base64UrlEncode(string $data): string
- {
- return rtrim(\strtr(\base64_encode($data), '+/', '-_'), '=');
- }
-}
diff --git a/src/Key.php b/src/Key/AbstractKey.php
similarity index 54%
rename from src/Key.php
rename to src/Key/AbstractKey.php
index b9b9484..764de9d 100644
--- a/src/Key.php
+++ b/src/Key/AbstractKey.php
@@ -2,7 +2,7 @@
declare(strict_types=1);
-namespace Strobotti\JWK;
+namespace Strobotti\JWK\Key;
/**
* @package Strobotti\JWK
@@ -10,15 +10,8 @@
* @license https://opensource.org/licenses/MIT MIT
* @link https://github.com/Strobotti/php-jwk
*/
-class Key implements \JsonSerializable
+abstract class AbstractKey implements KeyInterface
{
- public const KEY_TYPE_RSA = 'RSA';
-
- public const PUBLIC_KEY_USE_SIGNATURE = 'sig';
- public const PUBLIC_KEY_USE_ENCRYPTION = 'enc';
-
- public const ALGORITHM_RS256 = 'RS256';
-
/**
* The key type.
*
@@ -48,21 +41,7 @@ class Key implements \JsonSerializable
private $alg;
/**
- * The modulus for the RSA public key.
- *
- * @var null|string
- */
- private $n;
-
- /**
- * The exponent for the RSA public key.
- *
- * @var null|string
- */
- private $e;
-
- /**
- * @return string
+ * {@inheritdoc}
*/
public function getKeyType(): string
{
@@ -70,7 +49,7 @@ public function getKeyType(): string
}
/**
- * @return string
+ * {@inheritdoc}
*/
public function getKeyId(): string
{
@@ -78,7 +57,7 @@ public function getKeyId(): string
}
/**
- * @return string
+ * {@inheritdoc}
*/
public function getPublicKeyUse(): string
{
@@ -86,7 +65,7 @@ public function getPublicKeyUse(): string
}
/**
- * @return string
+ * {@inheritdoc}
*/
public function getAlgorithm(): string
{
@@ -94,27 +73,15 @@ public function getAlgorithm(): string
}
/**
- * Returns the modulus for the RSA public key.
+ * @param string $kty
*
- * @todo Implement different key types through inheritance
- *
- * @return null|string
+ * @return self
*/
- public function getRsaModulus(): ?string
+ protected function setKeyType(string $kty): self
{
- return $this->n;
- }
+ $this->kty = $kty;
- /**
- * Returns the exponent for the RSA public key.
- *
- * @todo Implement different key types through inheritance
- *
- * @return null|string
- */
- public function getRsaExponent(): ?string
- {
- return $this->e;
+ return $this;
}
/**
@@ -126,41 +93,53 @@ public function jsonSerialize(): array
{
$assoc = [
'kty' => $this->kty,
- 'kid' => $this->kid,
'use' => $this->use,
'alg' => $this->alg,
];
- if (null !== $this->n) {
- $assoc['n'] = $this->n;
- }
-
- if (null !== $this->e) {
- $assoc['e'] = $this->e;
+ if (null !== $this->kid) {
+ $assoc['kid'] = $this->kid;
}
return $assoc;
}
/**
- * @param string $json
+ * @param string $json
+ * @param KeyInterface|null $prototype
*
- * @return static
+ * @return KeyInterface
*/
- public static function createFromJSON(string $json): self
+ public static function createFromJSON(string $json, KeyInterface $prototype = null): KeyInterface
{
$assoc = \json_decode($json, true);
- $instance = new static();
+ if ($prototype) {
+ $instance = clone $prototype;
+ } else {
+ $instance = new static();
+ }
foreach ($assoc as $key => $value) {
if (!\property_exists($instance, $key)) {
continue;
}
- $instance->{$key} = $value;
+ try {
+ $instance->{$key} = $value;
+ } catch (\Throwable $e) {
+ // only set what you can
+ }
}
return $instance;
}
+
+ /**
+ * @return false|string
+ */
+ public function __toString()
+ {
+ return json_encode($this->jsonSerialize(), JSON_PRETTY_PRINT);
+ }
}
diff --git a/src/Key/KeyInterface.php b/src/Key/KeyInterface.php
new file mode 100644
index 0000000..5c1591e
--- /dev/null
+++ b/src/Key/KeyInterface.php
@@ -0,0 +1,58 @@
+
+ * @license https://opensource.org/licenses/MIT MIT
+ * @link https://github.com/Strobotti/php-jwk
+ *
+ * @method array jsonSerialize()
+ */
+interface KeyInterface extends \JsonSerializable
+{
+ public const KEY_TYPE_RSA = 'RSA';
+ public const KEY_TYPE_OKP = 'OKP';
+ public const KEY_TYPE_EC = 'EC';
+
+ public const PUBLIC_KEY_USE_SIGNATURE = 'sig';
+ public const PUBLIC_KEY_USE_ENCRYPTION = 'enc';
+
+ public const ALGORITHM_RS256 = 'RS256';
+
+ /**
+ * Gets the key type, ie. the value of the `kty` field
+ *
+ * @return string
+ */
+ public function getKeyType(): string;
+
+ /**
+ * Gets the key id, ie. the value of the `kid` field
+ *
+ * @return null|string
+ */
+ public function getKeyId(): ?string;
+
+ /**
+ * Gets the public key use, ie. the value of the `use` field
+ *
+ * @return string
+ */
+ public function getPublicKeyUse(): string;
+
+ /**
+ * Gets the cryptographic algorithm used to sign the key, ie. the value of the `alg` field
+ *
+ * @return string
+ */
+ public function getAlgorithm(): string;
+
+ /**
+ * @return string|bool
+ */
+ public function __toString();
+}
diff --git a/src/Key/Rsa.php b/src/Key/Rsa.php
new file mode 100644
index 0000000..cd2a3d4
--- /dev/null
+++ b/src/Key/Rsa.php
@@ -0,0 +1,94 @@
+
+ * @license https://opensource.org/licenses/MIT MIT
+ * @link https://github.com/Strobotti/php-jwk
+ */
+class Rsa extends AbstractKey
+{
+ /**
+ * The modulus for the RSA public key.
+ *
+ * @var string
+ */
+ private $n;
+
+ /**
+ * The exponent for the RSA public key.
+ *
+ * @var string
+ */
+ private $e;
+
+ /**
+ * Rsa key constructor.
+ */
+ public function __construct()
+ {
+ $this->setKeyType(KeyInterface::KEY_TYPE_RSA);
+ }
+
+ /**
+ * Returns the exponent for the RSA public key.
+ *
+ * @return string
+ */
+ public function getExponent(): string
+ {
+ return $this->e;
+ }
+
+ /**
+ * Returns the modulus for the RSA public key.
+ *
+ * @return string
+ */
+ public function getModulus(): string
+ {
+ return $this->n;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function jsonSerialize(): array
+ {
+ $assoc = parent::jsonSerialize();
+ $assoc['n'] = $this->n;
+ $assoc['e'] = $this->e;
+
+ return $assoc;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return self
+ */
+ public static function createFromJSON(string $json, KeyInterface $prototype = null): KeyInterface
+ {
+ if (!$prototype instanceof Rsa) {
+ $prototype = new static();
+ }
+
+ $instance = parent::createFromJSON($json, $prototype);
+
+ $assoc = \json_decode($json, true);
+
+ foreach ($assoc as $key => $value) {
+ if (!\property_exists($instance, $key)) {
+ continue;
+ }
+
+ $instance->{$key} = $value;
+ }
+
+ return $instance;
+ }
+}
diff --git a/src/KeyConverter.php b/src/KeyConverter.php
new file mode 100644
index 0000000..6131795
--- /dev/null
+++ b/src/KeyConverter.php
@@ -0,0 +1,58 @@
+
+ * @license https://opensource.org/licenses/MIT MIT
+ * @link https://github.com/Strobotti/php-jwk
+ */
+class KeyConverter
+{
+ /**
+ * @var Base64UrlConverterInterface
+ */
+ private $base64UrlConverter;
+
+ /**
+ * KeyConverter constructor.
+ */
+ public function __construct()
+ {
+ $this->base64UrlConverter = new Base64UrlConverter();
+ }
+
+ /**
+ * @param KeyInterface $key
+ *
+ * @return string
+ */
+ public function keyToPem(KeyInterface $key): string
+ {
+ if (!$key instanceof \Strobotti\JWK\Key\Rsa) {
+ throw new \InvalidArgumentException();
+ }
+
+ /** @var \Strobotti\JWK\Key\Rsa $key */
+
+ $rsa = new RSA();
+
+ $modulus = $this->base64UrlConverter->decode($key->getModulus(), true);
+
+ $rsa->loadKey([
+ 'e' => new BigInteger(\base64_decode($key->getExponent(), true), 256),
+ 'n' => new BigInteger($modulus, 256),
+ ]);
+
+ return $rsa->getPublicKey();
+ }
+}
diff --git a/src/KeyFactory.php b/src/KeyFactory.php
new file mode 100644
index 0000000..3f4564b
--- /dev/null
+++ b/src/KeyFactory.php
@@ -0,0 +1,85 @@
+
+ * @license https://opensource.org/licenses/MIT MIT
+ * @link https://github.com/Strobotti/php-jwk
+ */
+class KeyFactory
+{
+ /**
+ * @var Base64UrlConverterInterface
+ */
+ private $base64UrlConverter;
+
+ /**
+ * KeyFactory constructor.
+ */
+ public function __construct()
+ {
+ $this->base64UrlConverter = new Base64UrlConverter();
+ }
+
+ /**
+ * @param Base64UrlConverterInterface $base64UrlConverter
+ *
+ * @return KeyFactory
+ */
+ public function setBase64UrlConverter(Base64UrlConverterInterface $base64UrlConverter): self
+ {
+ $this->base64UrlConverter = $base64UrlConverter;
+
+ return $this;
+ }
+
+ /**
+ * @return Base64UrlConverterInterface
+ */
+ public function getBase64UrlConverter(): Base64UrlConverterInterface
+ {
+ return $this->base64UrlConverter;
+ }
+
+ /**
+ * @param string $pem
+ * @param array $options
+ *
+ * @return KeyInterface
+ */
+ public function createFromPem(string $pem, array $options = []): KeyInterface
+ {
+ $keyInfo = openssl_pkey_get_details(openssl_pkey_get_public($pem));
+
+ $jsonData = array_merge(
+ $options,
+ [
+ 'kty' => 'RSA',
+ 'n' => $this->base64UrlConverter->encode($keyInfo['rsa']['n']),
+ 'e' => $this->base64UrlConverter->encode($keyInfo['rsa']['e']),
+ ]
+ );
+
+ // TODO Only RSA is supported atm
+ return Key\Rsa::createFromJSON(json_encode($jsonData));
+ }
+
+ /**
+ * @param string $json
+ *
+ * @return KeyInterface
+ */
+ public function createFromJson(string $json): KeyInterface
+ {
+ // TODO Only RSA is supported atm
+ return Key\Rsa::createFromJSON($json);
+ }
+}
diff --git a/src/KeySet.php b/src/KeySet.php
index 2a34b5f..f4f1f50 100644
--- a/src/KeySet.php
+++ b/src/KeySet.php
@@ -4,6 +4,8 @@
namespace Strobotti\JWK;
+use Strobotti\JWK\Key\KeyInterface;
+
/**
* @package Strobotti\JWK
* @author Juha Jantunen
@@ -13,10 +15,35 @@
class KeySet implements \JsonSerializable
{
/**
- * @var Key[]
+ * @var KeyFactory
+ */
+ private $keyFactory;
+
+ /**
+ * @var KeyInterface[]
*/
private $keys = [];
+ /**
+ * KeySet constructor.
+ */
+ public function __construct()
+ {
+ $this->keyFactory = new KeyFactory();
+ }
+
+ /**
+ * @param KeyFactory $keyFactory
+ *
+ * @return self
+ */
+ public function setKeyFactory(KeyFactory $keyFactory): self
+ {
+ $this->keyFactory = $keyFactory;
+
+ return $this;
+ }
+
/**
* @param string $kid
*
@@ -30,9 +57,9 @@ public function containsKey(string $kid): bool
/**
* @param string $kid
*
- * @return null|Key
+ * @return null|KeyInterface
*/
- public function getKeyById(string $kid): ?Key
+ public function getKeyById(string $kid): ?KeyInterface
{
if (!$this->containsKey($kid)) {
return null;
@@ -42,11 +69,11 @@ public function getKeyById(string $kid): ?Key
}
/**
- * @param Key $key
+ * @param KeyInterface $key
*
* @return KeySet
*/
- public function addKey(Key $key): self
+ public function addKey(KeyInterface $key): self
{
if ($this->containsKey($key->getKeyId())) {
throw new \InvalidArgumentException(\sprintf(
@@ -77,20 +104,10 @@ public function jsonSerialize(): array
}
/**
- * @param string $json
- *
- * @return static
+ * @return false|string
*/
- public static function createFromJSON(string $json): self
+ public function __toString()
{
- $assoc = \json_decode($json, true);
-
- $instance = new static();
-
- foreach ($assoc['keys'] as $key) {
- $instance->addKey(Key::createFromJSON(\json_encode($key)));
- }
-
- return $instance;
+ return json_encode($this->jsonSerialize(), JSON_PRETTY_PRINT);
}
}
diff --git a/src/KeySetFactory.php b/src/KeySetFactory.php
new file mode 100644
index 0000000..cc5d641
--- /dev/null
+++ b/src/KeySetFactory.php
@@ -0,0 +1,60 @@
+
+ * @license https://opensource.org/licenses/MIT MIT
+ * @link https://github.com/Strobotti/php-jwk
+ */
+class KeySetFactory
+{
+ /**
+ * @var KeyFactory
+ */
+ private $keyFactory;
+
+ /**
+ * KeySet constructor.
+ */
+ public function __construct()
+ {
+ $this->keyFactory = new KeyFactory();
+ }
+
+ /**
+ * @param KeyFactory $keyFactory
+ *
+ * @return self
+ */
+ public function setKeyFactory(KeyFactory $keyFactory): self
+ {
+ $this->keyFactory = $keyFactory;
+
+ return $this;
+ }
+
+ /**
+ * @param string $json
+ *
+ * @return KeySet
+ */
+ public function createFromJSON(string $json): KeySet
+ {
+ $assoc = \json_decode($json, true);
+
+ $instance = new KeySet();
+
+ foreach ($assoc['keys'] as $keyData) {
+ $key = $this->keyFactory->createFromJson(\json_encode($keyData));
+
+ $instance->addKey($key);
+ }
+
+ return $instance;
+ }
+
+}
diff --git a/src/Util/Base64UrlConverter.php b/src/Util/Base64UrlConverter.php
new file mode 100644
index 0000000..6998761
--- /dev/null
+++ b/src/Util/Base64UrlConverter.php
@@ -0,0 +1,36 @@
+
+ * @license https://opensource.org/licenses/MIT MIT
+ * @link https://github.com/Strobotti/php-jwk
+ */
+class Base64UrlConverter implements Base64UrlConverterInterface
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function decode(string $data, $strict = false): string
+ {
+ $b64 = \strtr($data, '-_', '+/');
+
+ return \base64_decode($b64, $strict);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function encode(string $data): string
+ {
+ return rtrim(\strtr(\base64_encode($data), '+/', '-_'), '=');
+ }
+}
diff --git a/src/Util/Base64UrlConverterInterface.php b/src/Util/Base64UrlConverterInterface.php
new file mode 100644
index 0000000..51b0ea1
--- /dev/null
+++ b/src/Util/Base64UrlConverterInterface.php
@@ -0,0 +1,37 @@
+
+ * @license https://opensource.org/licenses/MIT MIT
+ * @link https://github.com/Strobotti/php-jwk
+ */
+interface Base64UrlConverterInterface
+{
+ /**
+ * Decodes Base64url formatted data to a string
+ *
+ * @param string $data
+ * @param bool $strict
+ *
+ * @return string
+ */
+ public function decode(string $data, $strict = false): string;
+
+ /**
+ * Encodes a string to a base64url formatted data
+ *
+ * @param string $data
+ *
+ * @return string
+ */
+ public function encode(string $data): string;
+}
diff --git a/tests/ConverterTest.php b/tests/ConverterTest.php
deleted file mode 100644
index c473ef8..0000000
--- a/tests/ConverterTest.php
+++ /dev/null
@@ -1,101 +0,0 @@
-keyToPem($key))
- );
- }
-
- /**
- * @return \Generator
- */
- public function provideKeyToPem(): \Generator
- {
- yield [
- 'key' => Key::createFromJSON('{
- "kty": "RSA",
- "kid": "eXaunmL",
- "use": "sig",
- "alg": "RS256",
- "n": "4dGQ7bQK8LgILOdLsYzfZjkEAoQeVC_aqyc8GC6RX7dq_KvRAQAWPvkam8VQv4GK5T4ogklEKEvj5ISBamdDNq1n52TpxQwI2EqxSk7I9fKPKhRt4F8-2yETlYvye-2s6NeWJim0KBtOVrk0gWvEDgd6WOqJl_yt5WBISvILNyVg1qAAM8JeX6dRPosahRVDjA52G2X-Tip84wqwyRpUlq2ybzcLh3zyhCitBOebiRWDQfG26EH9lTlJhll-p_Dg8vAXxJLIJ4SNLcqgFeZe4OfHLgdzMvxXZJnPp_VgmkcpUdRotazKZumj6dBPcXI_XID4Z4Z3OM1KrZPJNdUhxw",
- "e": "AQAB"
- }'),
- 'expected' => <<<'EOT'
------BEGIN PUBLIC KEY-----
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4dGQ7bQK8LgILOdLsYzf
-ZjkEAoQeVC/aqyc8GC6RX7dq/KvRAQAWPvkam8VQv4GK5T4ogklEKEvj5ISBamdD
-Nq1n52TpxQwI2EqxSk7I9fKPKhRt4F8+2yETlYvye+2s6NeWJim0KBtOVrk0gWvE
-Dgd6WOqJl/yt5WBISvILNyVg1qAAM8JeX6dRPosahRVDjA52G2X+Tip84wqwyRpU
-lq2ybzcLh3zyhCitBOebiRWDQfG26EH9lTlJhll+p/Dg8vAXxJLIJ4SNLcqgFeZe
-4OfHLgdzMvxXZJnPp/VgmkcpUdRotazKZumj6dBPcXI/XID4Z4Z3OM1KrZPJNdUh
-xwIDAQAB
------END PUBLIC KEY-----
-EOT
- ];
- }
-
- /**
- * @param string $pem
- * @param array $key
- *
- * @dataProvider providePemToKey
- */
- public function testPemToKey(string $pem, array $options, array $key)
- {
- $converter = new Converter();
- static::assertSame($key, $converter->pemToKey($pem, $options)->jsonSerialize());
- }
-
- /**
- * @return \Generator
- */
- public function providePemToKey(): \Generator
- {
- yield [
- 'pem' => <<<'EOT'
------BEGIN PUBLIC KEY-----
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4dGQ7bQK8LgILOdLsYzf
-ZjkEAoQeVC/aqyc8GC6RX7dq/KvRAQAWPvkam8VQv4GK5T4ogklEKEvj5ISBamdD
-Nq1n52TpxQwI2EqxSk7I9fKPKhRt4F8+2yETlYvye+2s6NeWJim0KBtOVrk0gWvE
-Dgd6WOqJl/yt5WBISvILNyVg1qAAM8JeX6dRPosahRVDjA52G2X+Tip84wqwyRpU
-lq2ybzcLh3zyhCitBOebiRWDQfG26EH9lTlJhll+p/Dg8vAXxJLIJ4SNLcqgFeZe
-4OfHLgdzMvxXZJnPp/VgmkcpUdRotazKZumj6dBPcXI/XID4Z4Z3OM1KrZPJNdUh
-xwIDAQAB
------END PUBLIC KEY-----
-EOT
- ,
- 'options' => [
- 'kid' => 'eXaunmL',
- 'use' => 'sig',
- 'alg' => 'RS256',
- ],
- 'key' => [
- 'kty' => 'RSA',
- 'kid' => 'eXaunmL',
- 'use' => 'sig',
- 'alg' => 'RS256',
- 'n' => '4dGQ7bQK8LgILOdLsYzfZjkEAoQeVC_aqyc8GC6RX7dq_KvRAQAWPvkam8VQv4GK5T4ogklEKEvj5ISBamdDNq1n52TpxQwI2EqxSk7I9fKPKhRt4F8-2yETlYvye-2s6NeWJim0KBtOVrk0gWvEDgd6WOqJl_yt5WBISvILNyVg1qAAM8JeX6dRPosahRVDjA52G2X-Tip84wqwyRpUlq2ybzcLh3zyhCitBOebiRWDQfG26EH9lTlJhll-p_Dg8vAXxJLIJ4SNLcqgFeZe4OfHLgdzMvxXZJnPp_VgmkcpUdRotazKZumj6dBPcXI_XID4Z4Z3OM1KrZPJNdUhxw',
- 'e' => 'AQAB',
- ],
- ];
- }
-}
diff --git a/tests/KeyTest.php b/tests/Key/RsaTest.php
similarity index 88%
rename from tests/KeyTest.php
rename to tests/Key/RsaTest.php
index e4c5a7e..6044c8c 100644
--- a/tests/KeyTest.php
+++ b/tests/Key/RsaTest.php
@@ -2,10 +2,10 @@
declare(strict_types=1);
-namespace Strobotti\JWK\Tests;
+namespace Strobotti\JWK\Key\Tests;
use PHPUnit\Framework\TestCase;
-use Strobotti\JWK\Key;
+use Strobotti\JWK\Key\Rsa;
final class KeyTest extends TestCase
{
@@ -17,14 +17,14 @@ final class KeyTest extends TestCase
*/
public function testCreateFromJSON(array $expected, string $input): void
{
- $key = Key::createFromJSON($input);
+ $key = Rsa::createFromJSON($input);
static::assertSame($expected['kty'], $key->getKeyType());
static::assertSame($expected['kid'], $key->getKeyId());
static::assertSame($expected['use'], $key->getPublicKeyUse());
static::assertSame($expected['alg'], $key->getAlgorithm());
- static::assertSame($expected['n'], $key->getRsaModulus());
- static::assertSame($expected['e'], $key->getRsaExponent());
+ static::assertSame($expected['n'], $key->getModulus());
+ static::assertSame($expected['e'], $key->getExponent());
}
/**
diff --git a/tests/KeyConverterTest.php b/tests/KeyConverterTest.php
new file mode 100644
index 0000000..e76006c
--- /dev/null
+++ b/tests/KeyConverterTest.php
@@ -0,0 +1,56 @@
+keyToPem($key))
+ );
+ }
+
+ /**
+ * @return \Generator
+ */
+ public function provideKeyToPem(): \Generator
+ {
+ yield [
+ 'key' => Rsa::createFromJSON('{
+ "kty": "RSA",
+ "kid": "eXaunmL",
+ "use": "sig",
+ "alg": "RS256",
+ "n": "4dGQ7bQK8LgILOdLsYzfZjkEAoQeVC_aqyc8GC6RX7dq_KvRAQAWPvkam8VQv4GK5T4ogklEKEvj5ISBamdDNq1n52TpxQwI2EqxSk7I9fKPKhRt4F8-2yETlYvye-2s6NeWJim0KBtOVrk0gWvEDgd6WOqJl_yt5WBISvILNyVg1qAAM8JeX6dRPosahRVDjA52G2X-Tip84wqwyRpUlq2ybzcLh3zyhCitBOebiRWDQfG26EH9lTlJhll-p_Dg8vAXxJLIJ4SNLcqgFeZe4OfHLgdzMvxXZJnPp_VgmkcpUdRotazKZumj6dBPcXI_XID4Z4Z3OM1KrZPJNdUhxw",
+ "e": "AQAB"
+ }'),
+ 'expected' => <<<'EOT'
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4dGQ7bQK8LgILOdLsYzf
+ZjkEAoQeVC/aqyc8GC6RX7dq/KvRAQAWPvkam8VQv4GK5T4ogklEKEvj5ISBamdD
+Nq1n52TpxQwI2EqxSk7I9fKPKhRt4F8+2yETlYvye+2s6NeWJim0KBtOVrk0gWvE
+Dgd6WOqJl/yt5WBISvILNyVg1qAAM8JeX6dRPosahRVDjA52G2X+Tip84wqwyRpU
+lq2ybzcLh3zyhCitBOebiRWDQfG26EH9lTlJhll+p/Dg8vAXxJLIJ4SNLcqgFeZe
+4OfHLgdzMvxXZJnPp/VgmkcpUdRotazKZumj6dBPcXI/XID4Z4Z3OM1KrZPJNdUh
+xwIDAQAB
+-----END PUBLIC KEY-----
+EOT
+ ];
+ }
+}
diff --git a/tests/KeyFactoryTest.php b/tests/KeyFactoryTest.php
new file mode 100644
index 0000000..9264a99
--- /dev/null
+++ b/tests/KeyFactoryTest.php
@@ -0,0 +1,63 @@
+createFromPem($pem, $options);
+
+ $this->assertInstanceOf($expectedInstance, $key);
+ static::assertEquals($json, $key->jsonSerialize());
+ }
+
+ /**
+ * @return \Generator
+ */
+ public function provideCreateFromPem(): \Generator
+ {
+ yield [
+ 'pem' => <<<'EOT'
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4dGQ7bQK8LgILOdLsYzf
+ZjkEAoQeVC/aqyc8GC6RX7dq/KvRAQAWPvkam8VQv4GK5T4ogklEKEvj5ISBamdD
+Nq1n52TpxQwI2EqxSk7I9fKPKhRt4F8+2yETlYvye+2s6NeWJim0KBtOVrk0gWvE
+Dgd6WOqJl/yt5WBISvILNyVg1qAAM8JeX6dRPosahRVDjA52G2X+Tip84wqwyRpU
+lq2ybzcLh3zyhCitBOebiRWDQfG26EH9lTlJhll+p/Dg8vAXxJLIJ4SNLcqgFeZe
+4OfHLgdzMvxXZJnPp/VgmkcpUdRotazKZumj6dBPcXI/XID4Z4Z3OM1KrZPJNdUh
+xwIDAQAB
+-----END PUBLIC KEY-----
+EOT
+ ,
+ 'options' => [
+ 'use' => 'sig',
+ 'alg' => 'RS256',
+ 'kid' => 'eXaunmL',
+ ],
+ 'json' => [
+ 'kty' => 'RSA',
+ 'use' => 'sig',
+ 'alg' => 'RS256',
+ 'kid' => 'eXaunmL',
+ 'n' => '4dGQ7bQK8LgILOdLsYzfZjkEAoQeVC_aqyc8GC6RX7dq_KvRAQAWPvkam8VQv4GK5T4ogklEKEvj5ISBamdDNq1n52TpxQwI2EqxSk7I9fKPKhRt4F8-2yETlYvye-2s6NeWJim0KBtOVrk0gWvEDgd6WOqJl_yt5WBISvILNyVg1qAAM8JeX6dRPosahRVDjA52G2X-Tip84wqwyRpUlq2ybzcLh3zyhCitBOebiRWDQfG26EH9lTlJhll-p_Dg8vAXxJLIJ4SNLcqgFeZe4OfHLgdzMvxXZJnPp_VgmkcpUdRotazKZumj6dBPcXI_XID4Z4Z3OM1KrZPJNdUhxw',
+ 'e' => 'AQAB',
+ ],
+ 'expectedInstance' => Rsa::class,
+ ];
+ }
+}
diff --git a/tests/KeySetTest.php b/tests/KeySetFactoryTest.php
similarity index 73%
rename from tests/KeySetTest.php
rename to tests/KeySetFactoryTest.php
index 27d2f94..32bd36a 100644
--- a/tests/KeySetTest.php
+++ b/tests/KeySetFactoryTest.php
@@ -5,23 +5,28 @@
namespace Strobotti\JWK\Tests;
use PHPUnit\Framework\TestCase;
-use Strobotti\JWK\KeySet;
+use Strobotti\JWK\KeySetFactory;
-final class KeySetTest extends TestCase
+final class KeySetFactoryTest extends TestCase
{
/**
* @param string $input
- * @dataProvider provideJSONConversion
+ * @dataProvider provideCreateFromJSON
*/
- public function testJSONConversion(string $input): void
+ public function testCreateFromJSON(string $input): void
{
- $keys = KeySet::createFromJSON($input);
+ $factory = new KeySetFactory();
+
+ $keys = $factory->createFromJSON($input);
$json = $keys->jsonSerialize();
- static::assertSame(\json_decode($input, true), $json);
+ static::assertEquals(\json_decode($input, true), $json);
}
- public function provideJSONConversion(): \Generator
+ /**
+ * @return \Generator
+ */
+ public function provideCreateFromJSON(): \Generator
{
yield [
'input' => <<<'EOT'
diff --git a/tests/Util/Base64UrlConverterTest.php b/tests/Util/Base64UrlConverterTest.php
new file mode 100644
index 0000000..17591c8
--- /dev/null
+++ b/tests/Util/Base64UrlConverterTest.php
@@ -0,0 +1,59 @@
+assertSame($expected, $converter->decode($input));
+ }
+
+ /**
+ * @return \Generator
+ */
+ public function provideDecode(): \Generator
+ {
+ yield [
+ 'expected' => '/a+quick+brown+fox/jumped-over/the_lazy_dog/',
+ 'input' => 'L2ErcXVpY2srYnJvd24rZm94L2p1bXBlZC1vdmVyL3RoZV9sYXp5X2RvZy8',
+ ];
+ }
+
+ /**
+ * @param string $expected
+ * @param string $input
+ *
+ * @dataProvider provideEncode
+ */
+ public function testEncode(string $expected, string $input): void
+ {
+ $converter = new Base64UrlConverter();
+
+ $this->assertSame($expected, $converter->encode($input));
+ }
+
+ /**
+ * @return \Generator
+ */
+ public function provideEncode(): \Generator
+ {
+ yield [
+ 'expected' => 'L2ErcXVpY2srYnJvd24rZm94L2p1bXBlZC1vdmVyL3RoZV9sYXp5X2RvZy8',
+ 'input' => '/a+quick+brown+fox/jumped-over/the_lazy_dog/',
+ ];
+ }
+}