From 7c1663a588dfa55e01e8061fb2119104dddfe0ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Kooman?= Date: Wed, 20 Mar 2024 13:55:17 +0100 Subject: [PATCH] also support EdDSA --- libexec/generate-secrets.php | 18 +++++++--- src/OIDC/{RsaJwtSigner.php => JwtSigner.php} | 30 ++++++++-------- web/index.php | 37 ++++++++++++++------ web/well-known.php | 17 ++++++--- 4 files changed, 67 insertions(+), 35 deletions(-) rename src/OIDC/{RsaJwtSigner.php => JwtSigner.php} (75%) diff --git a/libexec/generate-secrets.php b/libexec/generate-secrets.php index 03d16aa..22fc319 100644 --- a/libexec/generate-secrets.php +++ b/libexec/generate-secrets.php @@ -27,10 +27,11 @@ declare(strict_types=1); require_once dirname(__DIR__) . '/vendor/autoload.php'; $baseDir = dirname(__DIR__); +use fkooman\Jwt\EdDSA\SecretKey as EdDSASecretKey; +use fkooman\Jwt\RS256\SecretKey as RS256SecretKey; use fkooman\OAuth\Server\SecretBox; use fkooman\SAML\IdP\Base64UrlSafe; use fkooman\SAML\IdP\FileIO; -use fkooman\SAML\IdP\OIDC\RsaJwtSigner; // allow group to read the created files/folders umask(0027); @@ -56,11 +57,18 @@ try { FileIO::write($oauthKeyFile, SecretBox::genKey()->exportKey()); } - // JWT RSA key - [$rsaPem, $keyId] = RsaJwtSigner::genKey(); - $jwtKeyFile = null === $keyId ? $keyDir . '/rsa.key' : $keyDir . sprintf('/rsa_%s.key', $keyId); + // JWT RS256 Key + $secretKey = RS256SecretKey::generate(); + $jwtKeyFile = sprintf('%s/rsa_%s.key', $keyDir, $secretKey->keyId()); if (!FileIO::exists($jwtKeyFile)) { - FileIO::write($jwtKeyFile, $rsaPem); + FileIO::write($jwtKeyFile, $secretKey->toPem()); + } + + // JWT EdDSA Key + $secretKey = EdDSASecretKey::generate(); + $jwtKeyFile = sprintf('%s/ed_%s.key', $keyDir, $secretKey->keyId()); + if (!FileIO::exists($jwtKeyFile)) { + FileIO::write($jwtKeyFile, $secretKey->save()); } } catch (Exception $e) { echo 'ERROR: ' . $e->getMessage() . PHP_EOL; diff --git a/src/OIDC/RsaJwtSigner.php b/src/OIDC/JwtSigner.php similarity index 75% rename from src/OIDC/RsaJwtSigner.php rename to src/OIDC/JwtSigner.php index 8e0e739..3d1e836 100644 --- a/src/OIDC/RsaJwtSigner.php +++ b/src/OIDC/JwtSigner.php @@ -27,29 +27,29 @@ declare(strict_types=1); namespace fkooman\SAML\IdP\OIDC; use fkooman\Jwt\Jwt; -use fkooman\Jwt\RS256\SecretKey; +use fkooman\Jwt\SecretKeyInterface; use fkooman\OAuth\Server\JwtSignerInterface; -class RsaJwtSigner implements JwtSignerInterface +class JwtSigner implements JwtSignerInterface { - private SecretKey $secretKey; + private SecretKeyInterface $secretKey; - public function __construct(SecretKey $secretKey) + public function __construct(SecretKeyInterface $secretKey) { $this->secretKey = $secretKey; } - /** - * PEM encoded secret key. - * - * @return array{0:string,1:?string} - */ - public static function genKey(): array - { - $secretKey = SecretKey::generate(); - - return [$secretKey->toPem(), $secretKey->keyId()]; - } + // /** + // * PEM encoded secret key. + // * + // * @return array{0:string,1:?string} + // */ + // public static function genKey(): array + // { + // $secretKey = SecretKey::generate(); + + // return [$secretKey->toPem(), $secretKey->keyId()]; + // } public function alg(): string { diff --git a/web/index.php b/web/index.php index 037be37..de47ef0 100644 --- a/web/index.php +++ b/web/index.php @@ -27,8 +27,9 @@ declare(strict_types=1); require_once dirname(__DIR__) . '/vendor/autoload.php'; $baseDir = dirname(__DIR__); +use fkooman\Jwt\EdDSA\SecretKey as EdDSASecretKey; use fkooman\Jwt\PublicKeySet; -use fkooman\Jwt\RS256\SecretKey; +use fkooman\Jwt\RS256\SecretKey as RS256SecretKey; use fkooman\OAuth\Server\BearerValidator as OidcBearerValidator; use fkooman\OAuth\Server\LocalAccessTokenVerifier; use fkooman\OAuth\Server\NullAttributeFetcher; @@ -52,8 +53,8 @@ use fkooman\SAML\IdP\Http\Request; use fkooman\SAML\IdP\Http\Response; use fkooman\SAML\IdP\Http\SeSession; use fkooman\SAML\IdP\MyTpl; +use fkooman\SAML\IdP\OIDC\JwtSigner; use fkooman\SAML\IdP\OIDC\OpenIdModule; -use fkooman\SAML\IdP\OIDC\RsaJwtSigner; use fkooman\SAML\IdP\OidcAdminModule; use fkooman\SAML\IdP\SAML\Certificate; use fkooman\SAML\IdP\SAML\Key; @@ -172,16 +173,32 @@ try { $publicKeySet = new PublicKeySet(); foreach (glob($baseDir . '/config/keys/rsa_*.key') as $secretKeyFile) { [,$keyId] = explode('_', basename($secretKeyFile, '.key'), 2); - $publicKeySet->add(SecretKey::fromPem(FileIO::read($secretKeyFile), $keyId)->publicKey()); + $secretKey = RS256SecretKey::fromPem(FileIO::read($secretKeyFile), $keyId); + $publicKeySet->add($secretKey->publicKey()); + } + foreach (glob($baseDir . '/config/keys/ed_*.key') as $secretKeyFile) { + $secretKey = EdDSASecretKey::load(FileIO::read($secretKeyFile)); + $publicKeySet->add($secretKey->publicKey()); } - $secretKey = SecretKey::fromPem( - FileIO::read( - sprintf('%s/config/keys/rsa_%s.key', $baseDir, $config->useKey()) - ), - $config->useKey() - ); - $jwtSigner = new RsaJwtSigner($secretKey); + $keyId = $config->useKey(); + if (0 === strpos($keyId, 'EdDSA_')) { + $secretKey = EdDSASecretKey::load( + FileIO::read( + sprintf('%s/config/keys/rsa_%s.key', $baseDir, $config->useKey()) + ) + ); + } else { + // RS256 + $secretKey = RS256SecretKey::fromPem( + FileIO::read( + sprintf('%s/config/keys/rsa_%s.key', $baseDir, $config->useKey()) + ), + $config->useKey() + ); + } + + $jwtSigner = new JwtSigner($secretKey); $secretBox = SecretBox::loadKey(FileIO::read($baseDir . '/config/keys/oauth.key')); $service->addModule( diff --git a/web/well-known.php b/web/well-known.php index e617d9c..82359bc 100644 --- a/web/well-known.php +++ b/web/well-known.php @@ -27,8 +27,9 @@ declare(strict_types=1); require_once dirname(__DIR__) . '/vendor/autoload.php'; $baseDir = dirname(__DIR__); +use fkooman\Jwt\EdDSA\SecretKey as EdDSASecretKey; use fkooman\Jwt\PublicKeySet; -use fkooman\Jwt\RS256\SecretKey; +use fkooman\Jwt\RS256\SecretKey as RS256SecretKey; use fkooman\OAuth\Server\OAuthServer; use fkooman\SAML\IdP\Cfg\Config; use fkooman\SAML\IdP\FileIO; @@ -38,12 +39,18 @@ use fkooman\SAML\IdP\Http\Request; try { $config = Config::fromFile($baseDir . '/config/config.php'); + $supportedAlgList = []; $publicKeySet = new PublicKeySet(); - // we only support RS256 for now - $algList = ['RS256']; foreach (glob($baseDir . '/config/keys/rsa_*.key') as $secretKeyFile) { [,$keyId] = explode('_', basename($secretKeyFile, '.key'), 2); - $publicKeySet->add(SecretKey::fromPem(FileIO::read($secretKeyFile), $keyId)->publicKey()); + $secretKey = RS256SecretKey::fromPem(FileIO::read($secretKeyFile), $keyId); + $supportedAlgList[] = $secretKey->alg(); + $publicKeySet->add($secretKey->publicKey()); + } + foreach (glob($baseDir . '/config/keys/ed_*.key') as $secretKeyFile) { + $secretKey = EdDSASecretKey::load(FileIO::read($secretKeyFile)); + $supportedAlgList[] = $secretKey->alg(); + $publicKeySet->add($secretKey->publicKey()); } $request = Request::createFromGlobals(); @@ -55,7 +62,7 @@ try { $request->getRootUri() . 'openid/authorize', $request->getRootUri() . 'openid/token', $request->getRootUri() . 'openid/userinfo', - $algList, + array_values(array_unique($supportedAlgList)), $request->getRootUri() . 'openid/jwks' ) ); -- 2.45.2