Skip to content

Commit

Permalink
Feature: Introduce RSA::fromRSAPublicKeyFile, rework JWKSet
Browse files Browse the repository at this point in the history
  • Loading branch information
ovr committed Jul 18, 2019
1 parent 8c6921f commit 452ca3d
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 18 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Implementation:

- JWT (JSON Web Token) [RFC 7519](https://tools.ietf.org/html/rfc7519)
- JWK (JSON Web Keys) [RFC 7517](https://tools.ietf.org/html/rfc7517)
- JWKs (JSON Web Key Set) [RFC 7517](https://tools.ietf.org/html/rfc7517#section-8.4)

## Encode

Expand Down
14 changes: 14 additions & 0 deletions src/JWK.php
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,20 @@ protected function parseRSAKey(array $parameters)
}
}

/**
* @param string $file
* @return JWK
*/
public static function fromRSAPublicKeyFile(string $file): JWK
{
$contentOrFalse = file_get_contents($file);
if ($contentOrFalse === false) {
throw new RuntimeException('Unable to read public key');
}

return self::fromRSAPublicKey($contentOrFalse);
}

/**
* @param string $content
* @return JWK
Expand Down
20 changes: 11 additions & 9 deletions src/JWKSet.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,26 @@ class JWKSet
protected $keys;

/**
* @param array<string, array> $keys
* @param array<string, array> $parameters
*/
public function __construct(array $keys)
public function __construct(array $parameters)
{
$this->keys = array_map(static function ($key) {
return new JWK($key);
}, $keys);
if (isset($parameters['keys'])) {
$this->keys = $parameters['keys'];
} else {
throw new \InvalidArgumentException('JWKs must define keys');
}
}

/**
* @param string $kid
* @return JWK
*/
public function findKeyByKind(string $kid)
public function findKeyByKid(string $kid)
{
foreach ($this->keys as $key => $jwk) {
if ($key === $kid) {
return $jwk;
foreach ($this->keys as $jwk) {
if (isset($jwk['kid']) && $jwk['kid'] === $kid) {
return new JWK($jwk);
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/JWT.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,14 @@ public function __construct(array $payload, array $headers = [], $signature = nu

/**
* @param string $token
* @param string|JWKSet $publicKeyOrSecret
* @param string|JWKSet|mixed $publicKeyOrSecret
* @param DecodeOptions $options
* @return JWT
* @throws InvalidJWT
*/
public static function decode(string $token, $publicKeyOrSecret, DecodeOptions $options)
{
if (!is_string($publicKeyOrSecret) && !$publicKeyOrSecret instanceof JWKSet) {
if (!is_string($publicKeyOrSecret) && !($publicKeyOrSecret instanceof JWKSet)) {
throw new \InvalidArgumentException('$privateKeyOrSecret must be string/JWK/JWKSet');
}

Expand Down Expand Up @@ -263,7 +263,7 @@ protected function verifySignature($data, $publicKeyOrSecret, DecodeOptions $opt
}

if ($publicKeyOrSecret instanceof JWKSet) {
$jwk = $publicKeyOrSecret->findKeyByKind($this->headers['kid']);
$jwk = $publicKeyOrSecret->findKeyByKid($this->headers['kid']);
$secretOrKey = $jwk->getPublicKey();
} else {
$secretOrKey = $publicKeyOrSecret;
Expand Down
6 changes: 3 additions & 3 deletions tests/JWKTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class JWKTest extends AbstractTestCase
{
public function testFromRSA256PublicKey()
{
$jwk = JWK::fromRSAPublicKey(file_get_contents(__DIR__ . '/assets/rs256.key.pub'));
$jwk = JWK::fromRSAPublicKeyFile(__DIR__ . '/assets/rs256.key.pub');

parent::assertSame(
[
Expand All @@ -27,7 +27,7 @@ public function testFromRSA256PublicKey()

public function testFromRSA384PublicKey()
{
$jwk = JWK::fromRSAPublicKey(file_get_contents(__DIR__ . '/assets/rs384.key.pub'));
$jwk = JWK::fromRSAPublicKeyFile(__DIR__ . '/assets/rs384.key.pub');

parent::assertSame(
[
Expand All @@ -42,7 +42,7 @@ public function testFromRSA384PublicKey()

public function testFromRSA512PublicKey()
{
$jwk = JWK::fromRSAPublicKey(file_get_contents(__DIR__ . '/assets/rs512.key.pub'));
$jwk = JWK::fromRSAPublicKeyFile(__DIR__ . '/assets/rs512.key.pub');

parent::assertSame(
[
Expand Down
13 changes: 10 additions & 3 deletions tests/JWTTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ public function testValidateHeaderSuccess()
self::callProtectedMethod(
$token,
'validateHeader',
new JWKSet([]),
new JWKSet(['keys' => []]),
new DecodeOptions(['RS256'])
);

Expand Down Expand Up @@ -208,7 +208,7 @@ public function testValidateHeaderNoKid()
self::callProtectedMethod(
$token,
'validateHeader',
new JWKSet([]),
new JWKSet(['keys' => []]),
new DecodeOptions(['RS256'])
);
}
Expand Down Expand Up @@ -284,7 +284,14 @@ public function getEncodeToDecodeDataProvider()
[
file_get_contents(__DIR__ . '/assets/rs512.key'),
new JWKSet([
$kid => JWK::fromRSAPublicKey(file_get_contents(__DIR__ . '/assets/rs512.key.pub'))->toArray()
'keys' => [
array_merge(
JWK::fromRSAPublicKeyFile(__DIR__ . '/assets/rs512.key.pub')->toArray(),
[
'kid' => $kid
]
)
]
]),
'RS512',
['kid' => $kid],
Expand Down

0 comments on commit 452ca3d

Please sign in to comment.