1<?php
2require __DIR__ . '/vendor/autoload.php';
3
4use Firebase\JWT\JWK;
5
6function base64url(string $bytes): string
7{
8 return rtrim(strtr(base64_encode($bytes), '+/', '-_'), '=');
9}
10
11// PEM -> JWK with ext-openssl (EC coordinates padded to the curve size)
12$details = openssl_pkey_get_details(openssl_pkey_get_public(file_get_contents('pub.pem')));
13$size = intdiv($details['bits'] + 7, 8);
14$jwk = [
15 'kty' => 'EC',
16 'crv' => 'P-256',
17 'x' => base64url(str_pad($details['ec']['x'], $size, "\0", STR_PAD_LEFT)),
18 'y' => base64url(str_pad($details['ec']['y'], $size, "\0", STR_PAD_LEFT)),
19];
20file_put_contents('out.jwk.json', json_encode($jwk));
21echo json_encode($jwk), PHP_EOL;
22
23// JWK -> PEM with php-jwt: JWK::parseKey assembles a PEM from x and y (getKeyMaterial)
24$key = JWK::parseKey(json_decode(file_get_contents('pub.jwk.json'), true), 'ES256');
25// php-jwt does not check the point; parsing with OpenSSL rejects one that is not on the curve
26$publicKey = openssl_pkey_get_public($key->getKeyMaterial());
27if ($publicKey === false) {
28 throw new UnexpectedValueException('invalid EC public key: ' . openssl_error_string());
29}
30$pem = openssl_pkey_get_details($publicKey)['key'];
31file_put_contents('out.pem', $pem);
32echo $pem;