Skip to content

Commit 9b945e8

Browse files
committed
Add Key, KeySet and Converter to handle JWKs
1 parent e5a58ce commit 9b945e8

File tree

11 files changed

+609
-1
lines changed

11 files changed

+609
-1
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.idea
2+
.phpunit.result.cache
3+
composer.lock
4+
vendor

.travis.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
language: php
2+
3+
php:
4+
- 7.2
5+
6+
sudo: false
7+
8+
before_script: composer install
9+
script: phpunit

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# php-jwk
2-
A small PHP library to handle JWK (Json Web Key)
2+
A small PHP library to handle JWKs (Json Web Keys)

composer.json

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"name": "strobotti/php-jwk",
3+
"description": "A small PHP library to handle JWKs (Json Web Keys)",
4+
"homepage": "https://github.com/Strobotti/php-jwk",
5+
"authors": [
6+
{
7+
"name": "Juha Jantunen",
8+
"email": "[email protected]",
9+
"role": "Developer"
10+
}
11+
],
12+
"license": "MIT",
13+
"require": {
14+
"php": ">=7.2.0",
15+
"phpseclib/phpseclib": "^2.0"
16+
},
17+
"autoload": {
18+
"psr-4": {
19+
"Strobotti\\JWK\\": "src"
20+
},
21+
"autoload-dev": {
22+
"psr-4": {
23+
"Strobotti\\JWK\\Tests\\": "tests/"
24+
}
25+
}
26+
},
27+
"require-dev": {
28+
"phpunit/phpunit": "^8.0"
29+
}
30+
}

phpunit.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2+
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/8.0/phpunit.xsd"
3+
bootstrap="vendor/autoload.php">
4+
<testsuites>
5+
<testsuite name="unit tests">
6+
<directory>tests</directory>
7+
</testsuite>
8+
</testsuites>
9+
</phpunit>

src/Converter.php

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Strobotti\JWK;
6+
7+
use phpseclib\Crypt\RSA;
8+
use phpseclib\Math\BigInteger;
9+
10+
/**
11+
* @package Strobotti\JWK
12+
* @author Juha Jantunen <[email protected]>
13+
* @license https://opensource.org/licenses/MIT MIT
14+
* @link https://github.com/Strobotti/php-jwk
15+
*/
16+
class Converter
17+
{
18+
/**
19+
* @param Key $key
20+
*
21+
* @return string
22+
*/
23+
public function keyToPem(Key $key): string
24+
{
25+
// TODO implement strategies to support different key types
26+
$rsa = new RSA();
27+
28+
$modulus = $this->base64UrlDecode($key->getRsaModulus(), true);
29+
30+
$rsa->loadKey([
31+
'e' => new BigInteger(\base64_decode($key->getRsaExponent(), true), 256),
32+
'n' => new BigInteger($modulus, 256),
33+
]);
34+
35+
return $rsa->getPublicKey();
36+
}
37+
38+
/**
39+
* @param string $pem A PEM encoded (RSA) Public Key
40+
* @param array $options An array of key-options, such as ['kid' => 'eXaunmL', 'use' => 'sig', ...]
41+
*
42+
* @return Key
43+
*/
44+
public function pemToKey(string $pem, array $options = []): Key
45+
{
46+
$keyInfo = openssl_pkey_get_details(openssl_pkey_get_public($pem));
47+
48+
$jsonData = array_merge(
49+
$options,
50+
[
51+
'kty' => 'RSA',
52+
'n' => $this->base64UrlEncode($keyInfo['rsa']['n']),
53+
'e' => $this->base64UrlEncode($keyInfo['rsa']['e']),
54+
]
55+
);
56+
57+
return Key::createFromJSON(json_encode($jsonData));
58+
}
59+
60+
/**
61+
* https://tools.ietf.org/html/rfc4648#section-5.
62+
*
63+
* @param string $data
64+
* @param bool $strict
65+
*
66+
* @return string
67+
*/
68+
private function base64UrlDecode(string $data, $strict = false): string
69+
{
70+
$b64 = \strtr($data, '-_', '+/');
71+
72+
return \base64_decode($b64, $strict);
73+
}
74+
75+
/**
76+
* https://tools.ietf.org/html/rfc4648#section-5.
77+
*
78+
* @param string $data
79+
*
80+
* @return string
81+
*/
82+
private function base64UrlEncode(string $data): string
83+
{
84+
return rtrim(\strtr(\base64_encode($data), '+/', '-_'), '=');
85+
}
86+
}

src/Key.php

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Strobotti\JWK;
6+
7+
/**
8+
* @package Strobotti\JWK
9+
* @author Juha Jantunen <[email protected]>
10+
* @license https://opensource.org/licenses/MIT MIT
11+
* @link https://github.com/Strobotti/php-jwk
12+
*/
13+
class Key implements \JsonSerializable
14+
{
15+
public const KEY_TYPE_RSA = 'RSA';
16+
17+
public const PUBLIC_KEY_USE_SIGNATURE = 'sig';
18+
public const PUBLIC_KEY_USE_ENCRYPTION = 'enc';
19+
20+
public const ALGORITHM_RS256 = 'RS256';
21+
22+
/**
23+
* The key type.
24+
*
25+
* @var string
26+
*/
27+
private $kty;
28+
29+
/**
30+
* The key ID.
31+
*
32+
* @var string
33+
*/
34+
private $kid;
35+
36+
/**
37+
* The public key use.
38+
*
39+
* @var string
40+
*/
41+
private $use;
42+
43+
/**
44+
* The algorithm.
45+
*
46+
* @var string
47+
*/
48+
private $alg;
49+
50+
/**
51+
* The modulus for the RSA public key.
52+
*
53+
* @var null|string
54+
*/
55+
private $n;
56+
57+
/**
58+
* The exponent for the RSA public key.
59+
*
60+
* @var null|string
61+
*/
62+
private $e;
63+
64+
/**
65+
* @return string
66+
*/
67+
public function getKeyType(): string
68+
{
69+
return $this->kty;
70+
}
71+
72+
/**
73+
* @return string
74+
*/
75+
public function getKeyId(): string
76+
{
77+
return $this->kid;
78+
}
79+
80+
/**
81+
* @return string
82+
*/
83+
public function getPublicKeyUse(): string
84+
{
85+
return $this->use;
86+
}
87+
88+
/**
89+
* @return string
90+
*/
91+
public function getAlgorithm(): string
92+
{
93+
return $this->alg;
94+
}
95+
96+
/**
97+
* Returns the modulus for the RSA public key.
98+
*
99+
* @todo Implement different key types through inheritance
100+
*
101+
* @return null|string
102+
*/
103+
public function getRsaModulus(): ?string
104+
{
105+
return $this->n;
106+
}
107+
108+
/**
109+
* Returns the exponent for the RSA public key.
110+
*
111+
* @todo Implement different key types through inheritance
112+
*
113+
* @return null|string
114+
*/
115+
public function getRsaExponent(): ?string
116+
{
117+
return $this->e;
118+
}
119+
120+
/**
121+
* Returns an array presentation of the key
122+
*
123+
* @return array An assoc to be passed to json_encode
124+
*/
125+
public function jsonSerialize(): array
126+
{
127+
$assoc = [
128+
'kty' => $this->kty,
129+
'kid' => $this->kid,
130+
'use' => $this->use,
131+
'alg' => $this->alg,
132+
];
133+
134+
if (null !== $this->n) {
135+
$assoc['n'] = $this->n;
136+
}
137+
138+
if (null !== $this->e) {
139+
$assoc['e'] = $this->e;
140+
}
141+
142+
return $assoc;
143+
}
144+
145+
/**
146+
* @param string $json
147+
*
148+
* @return static
149+
*/
150+
public static function createFromJSON(string $json): self
151+
{
152+
$assoc = \json_decode($json, true);
153+
154+
$instance = new static();
155+
156+
foreach ($assoc as $key => $value) {
157+
if (!\property_exists($instance, $key)) {
158+
continue;
159+
}
160+
161+
$instance->{$key} = $value;
162+
}
163+
164+
return $instance;
165+
}
166+
}

0 commit comments

Comments
 (0)