From 734744207ae4f8e0dd5cd6463c8535155e61b92e Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Sun, 24 Mar 2024 10:57:06 +0100 Subject: [PATCH 1/2] Refactor encryption algorithms configuration (#544) * Refactor encryption algorithms configuration Deprecated 'key_encryption_algorithms' and 'content_encryption_algorithms' have been replaced with a unified 'encryption_algorithms' across multiple classes and configurations. Warning messages have been added to any deprecated usages to ensure smooth transition in future versions. Update test assertions accordingly to reflect these changes. * Update code to avoid use of deprecated encryption algorithms Replaced 'key_encryption_algorithms' and 'content_encryption_algorithms' with 'encryption_algorithms'. Deprecated warning messages have now been included in code to guide future updates. Test cases have been adjusted to validate these changes. --- phpstan-baseline.neon | 260 +++++++++++++++++- .../Encryption/AbstractEncryptionSource.php | 47 +++- .../Source/Encryption/JWEBuilder.php | 4 +- .../Source/Encryption/JWEDecrypter.php | 4 +- .../Source/Encryption/JWELoader.php | 50 +++- .../Source/NestedToken/NestedTokenBuilder.php | 50 +++- .../Source/NestedToken/NestedTokenLoader.php | 50 +++- .../NestedToken/NestedTokenLoaderFactory.php | 2 +- .../JweBuilderConfigurationTest.php | 4 +- .../JweDecrypterConfigurationTest.php | 4 +- .../NestedTokenBuilderConfigurationTest.php | 2 +- .../NestedTokenLoaderConfigurationTest.php | 2 +- 12 files changed, 447 insertions(+), 32 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index a5104296..e528e23a 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1006,11 +1006,6 @@ parameters: count: 1 path: src/Bundle/Serializer/JWESerializer.php - - - message: "#^Method Jose\\\\Bundle\\\\JoseFramework\\\\Serializer\\\\JWESerializer\\:\\:getSupportedTypes\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/Bundle/Serializer/JWESerializer.php - - message: "#^Method Jose\\\\Bundle\\\\JoseFramework\\\\Serializer\\\\JWESerializer\\:\\:supportsDenormalization\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" count: 1 @@ -1046,11 +1041,6 @@ parameters: count: 1 path: src/Bundle/Serializer/JWSSerializer.php - - - message: "#^Method Jose\\\\Bundle\\\\JoseFramework\\\\Serializer\\\\JWSSerializer\\:\\:getSupportedTypes\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/Bundle/Serializer/JWSSerializer.php - - message: "#^Method Jose\\\\Bundle\\\\JoseFramework\\\\Serializer\\\\JWSSerializer\\:\\:supportsDenormalization\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" count: 1 @@ -1288,16 +1278,86 @@ parameters: count: 1 path: src/Library/Checker/IssuerChecker.php + - + message: "#^Property Jose\\\\Component\\\\Console\\\\AddKeyIntoKeysetCommand\\:\\:\\$defaultDescription has no type specified\\.$#" + count: 1 + path: src/Library/Console/AddKeyIntoKeysetCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\AddKeyIntoKeysetCommand\\:\\:\\$defaultName has no type specified\\.$#" + count: 1 + path: src/Library/Console/AddKeyIntoKeysetCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\EcKeyGeneratorCommand\\:\\:\\$defaultDescription has no type specified\\.$#" + count: 1 + path: src/Library/Console/EcKeyGeneratorCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\EcKeyGeneratorCommand\\:\\:\\$defaultName has no type specified\\.$#" + count: 1 + path: src/Library/Console/EcKeyGeneratorCommand.php + - message: "#^Cannot cast mixed to int\\.$#" count: 1 path: src/Library/Console/EcKeysetGeneratorCommand.php + - + message: "#^Property Jose\\\\Component\\\\Console\\\\EcKeysetGeneratorCommand\\:\\:\\$defaultDescription has no type specified\\.$#" + count: 1 + path: src/Library/Console/EcKeysetGeneratorCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\EcKeysetGeneratorCommand\\:\\:\\$defaultName has no type specified\\.$#" + count: 1 + path: src/Library/Console/EcKeysetGeneratorCommand.php + - message: "#^Method Jose\\\\Component\\\\Console\\\\GeneratorCommand\\:\\:getOptions\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Library/Console/GeneratorCommand.php + - + message: "#^Property Jose\\\\Component\\\\Console\\\\GetThumbprintCommand\\:\\:\\$defaultDescription has no type specified\\.$#" + count: 1 + path: src/Library/Console/GetThumbprintCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\GetThumbprintCommand\\:\\:\\$defaultName has no type specified\\.$#" + count: 1 + path: src/Library/Console/GetThumbprintCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\JKULoaderCommand\\:\\:\\$defaultDescription has no type specified\\.$#" + count: 1 + path: src/Library/Console/JKULoaderCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\JKULoaderCommand\\:\\:\\$defaultName has no type specified\\.$#" + count: 1 + path: src/Library/Console/JKULoaderCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\KeyAnalyzerCommand\\:\\:\\$defaultDescription has no type specified\\.$#" + count: 1 + path: src/Library/Console/KeyAnalyzerCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\KeyAnalyzerCommand\\:\\:\\$defaultName has no type specified\\.$#" + count: 1 + path: src/Library/Console/KeyAnalyzerCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\KeyFileLoaderCommand\\:\\:\\$defaultDescription has no type specified\\.$#" + count: 1 + path: src/Library/Console/KeyFileLoaderCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\KeyFileLoaderCommand\\:\\:\\$defaultName has no type specified\\.$#" + count: 1 + path: src/Library/Console/KeyFileLoaderCommand.php + - message: "#^Parameter \\#1 \\$jwk of method Jose\\\\Component\\\\KeyManagement\\\\Analyzer\\\\KeyAnalyzerManager\\:\\:analyze\\(\\) expects Jose\\\\Component\\\\Core\\\\JWK, mixed given\\.$#" count: 1 @@ -1308,31 +1368,211 @@ parameters: count: 1 path: src/Library/Console/KeysetAnalyzerCommand.php + - + message: "#^Property Jose\\\\Component\\\\Console\\\\KeysetAnalyzerCommand\\:\\:\\$defaultDescription has no type specified\\.$#" + count: 1 + path: src/Library/Console/KeysetAnalyzerCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\KeysetAnalyzerCommand\\:\\:\\$defaultName has no type specified\\.$#" + count: 1 + path: src/Library/Console/KeysetAnalyzerCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\MergeKeysetCommand\\:\\:\\$defaultDescription has no type specified\\.$#" + count: 1 + path: src/Library/Console/MergeKeysetCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\MergeKeysetCommand\\:\\:\\$defaultName has no type specified\\.$#" + count: 1 + path: src/Library/Console/MergeKeysetCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\NoneKeyGeneratorCommand\\:\\:\\$defaultDescription has no type specified\\.$#" + count: 1 + path: src/Library/Console/NoneKeyGeneratorCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\NoneKeyGeneratorCommand\\:\\:\\$defaultName has no type specified\\.$#" + count: 1 + path: src/Library/Console/NoneKeyGeneratorCommand.php + - message: "#^Cannot cast mixed to int\\.$#" count: 1 path: src/Library/Console/OctKeyGeneratorCommand.php + - + message: "#^Property Jose\\\\Component\\\\Console\\\\OctKeyGeneratorCommand\\:\\:\\$defaultDescription has no type specified\\.$#" + count: 1 + path: src/Library/Console/OctKeyGeneratorCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\OctKeyGeneratorCommand\\:\\:\\$defaultName has no type specified\\.$#" + count: 1 + path: src/Library/Console/OctKeyGeneratorCommand.php + - message: "#^Cannot cast mixed to int\\.$#" count: 2 path: src/Library/Console/OctKeysetGeneratorCommand.php + - + message: "#^Property Jose\\\\Component\\\\Console\\\\OctKeysetGeneratorCommand\\:\\:\\$defaultDescription has no type specified\\.$#" + count: 1 + path: src/Library/Console/OctKeysetGeneratorCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\OctKeysetGeneratorCommand\\:\\:\\$defaultName has no type specified\\.$#" + count: 1 + path: src/Library/Console/OctKeysetGeneratorCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\OkpKeyGeneratorCommand\\:\\:\\$defaultDescription has no type specified\\.$#" + count: 1 + path: src/Library/Console/OkpKeyGeneratorCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\OkpKeyGeneratorCommand\\:\\:\\$defaultName has no type specified\\.$#" + count: 1 + path: src/Library/Console/OkpKeyGeneratorCommand.php + - message: "#^Cannot cast mixed to int\\.$#" count: 1 path: src/Library/Console/OkpKeysetGeneratorCommand.php + - + message: "#^Property Jose\\\\Component\\\\Console\\\\OkpKeysetGeneratorCommand\\:\\:\\$defaultDescription has no type specified\\.$#" + count: 1 + path: src/Library/Console/OkpKeysetGeneratorCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\OkpKeysetGeneratorCommand\\:\\:\\$defaultName has no type specified\\.$#" + count: 1 + path: src/Library/Console/OkpKeysetGeneratorCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\OptimizeRsaKeyCommand\\:\\:\\$defaultDescription has no type specified\\.$#" + count: 1 + path: src/Library/Console/OptimizeRsaKeyCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\OptimizeRsaKeyCommand\\:\\:\\$defaultName has no type specified\\.$#" + count: 1 + path: src/Library/Console/OptimizeRsaKeyCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\P12CertificateLoaderCommand\\:\\:\\$defaultDescription has no type specified\\.$#" + count: 1 + path: src/Library/Console/P12CertificateLoaderCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\P12CertificateLoaderCommand\\:\\:\\$defaultName has no type specified\\.$#" + count: 1 + path: src/Library/Console/P12CertificateLoaderCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\PemConverterCommand\\:\\:\\$defaultDescription has no type specified\\.$#" + count: 1 + path: src/Library/Console/PemConverterCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\PemConverterCommand\\:\\:\\$defaultName has no type specified\\.$#" + count: 1 + path: src/Library/Console/PemConverterCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\PublicKeyCommand\\:\\:\\$defaultDescription has no type specified\\.$#" + count: 1 + path: src/Library/Console/PublicKeyCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\PublicKeyCommand\\:\\:\\$defaultName has no type specified\\.$#" + count: 1 + path: src/Library/Console/PublicKeyCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\PublicKeysetCommand\\:\\:\\$defaultDescription has no type specified\\.$#" + count: 1 + path: src/Library/Console/PublicKeysetCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\PublicKeysetCommand\\:\\:\\$defaultName has no type specified\\.$#" + count: 1 + path: src/Library/Console/PublicKeysetCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\RotateKeysetCommand\\:\\:\\$defaultDescription has no type specified\\.$#" + count: 1 + path: src/Library/Console/RotateKeysetCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\RotateKeysetCommand\\:\\:\\$defaultName has no type specified\\.$#" + count: 1 + path: src/Library/Console/RotateKeysetCommand.php + - message: "#^Cannot cast mixed to int\\.$#" count: 1 path: src/Library/Console/RsaKeyGeneratorCommand.php + - + message: "#^Property Jose\\\\Component\\\\Console\\\\RsaKeyGeneratorCommand\\:\\:\\$defaultDescription has no type specified\\.$#" + count: 1 + path: src/Library/Console/RsaKeyGeneratorCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\RsaKeyGeneratorCommand\\:\\:\\$defaultName has no type specified\\.$#" + count: 1 + path: src/Library/Console/RsaKeyGeneratorCommand.php + - message: "#^Cannot cast mixed to int\\.$#" count: 2 path: src/Library/Console/RsaKeysetGeneratorCommand.php + - + message: "#^Property Jose\\\\Component\\\\Console\\\\RsaKeysetGeneratorCommand\\:\\:\\$defaultDescription has no type specified\\.$#" + count: 1 + path: src/Library/Console/RsaKeysetGeneratorCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\RsaKeysetGeneratorCommand\\:\\:\\$defaultName has no type specified\\.$#" + count: 1 + path: src/Library/Console/RsaKeysetGeneratorCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\SecretKeyGeneratorCommand\\:\\:\\$defaultDescription has no type specified\\.$#" + count: 1 + path: src/Library/Console/SecretKeyGeneratorCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\SecretKeyGeneratorCommand\\:\\:\\$defaultName has no type specified\\.$#" + count: 1 + path: src/Library/Console/SecretKeyGeneratorCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\X509CertificateLoaderCommand\\:\\:\\$defaultDescription has no type specified\\.$#" + count: 1 + path: src/Library/Console/X509CertificateLoaderCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\X509CertificateLoaderCommand\\:\\:\\$defaultName has no type specified\\.$#" + count: 1 + path: src/Library/Console/X509CertificateLoaderCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\X5ULoaderCommand\\:\\:\\$defaultDescription has no type specified\\.$#" + count: 1 + path: src/Library/Console/X5ULoaderCommand.php + + - + message: "#^Property Jose\\\\Component\\\\Console\\\\X5ULoaderCommand\\:\\:\\$defaultName has no type specified\\.$#" + count: 1 + path: src/Library/Console/X5ULoaderCommand.php + - message: "#^Call to function is_string\\(\\) with string will always evaluate to true\\.$#" count: 1 diff --git a/src/Bundle/DependencyInjection/Source/Encryption/AbstractEncryptionSource.php b/src/Bundle/DependencyInjection/Source/Encryption/AbstractEncryptionSource.php index 461a1db2..2bdffaf6 100644 --- a/src/Bundle/DependencyInjection/Source/Encryption/AbstractEncryptionSource.php +++ b/src/Bundle/DependencyInjection/Source/Encryption/AbstractEncryptionSource.php @@ -17,22 +17,60 @@ public function getNodeDefinition(NodeDefinition $node): void ->arrayNode($this->name()) ->useAttributeAsKey('name') ->arrayPrototype() + ->beforeNormalization() + ->ifTrue( + static fn (array $v) => isset($v['key_encryption_algorithms']) || isset($v['content_encryption_algorithms']) + ) + ->then(static function (array $v) { + $v['encryption_algorithms'] = array_merge( + $v['encryption_algorithms'] ?? [], + $v['key_encryption_algorithms'] ?? [] + ); + $v['encryption_algorithms'] = array_merge( + $v['encryption_algorithms'], + $v['content_encryption_algorithms'] ?? [] + ); + unset($v['key_encryption_algorithms'], $v['content_encryption_algorithms']); + $v['encryption_algorithms'] = array_values(array_unique($v['encryption_algorithms'])); + + return $v; + }) + ->end() ->children() ->booleanNode('is_public') ->info('If true, the service will be public, else private.') ->defaultTrue() ->end() - ->arrayNode('key_encryption_algorithms') - ->info('A list of supported key encryption algorithms.') + ->arrayNode('encryption_algorithms') + ->info('A list of key or content encryption algorithm aliases.') ->useAttributeAsKey('name') ->isRequired() ->requiresAtLeastOneElement() ->scalarPrototype() ->end() ->end() + ->arrayNode('key_encryption_algorithms') + ->info('A list of supported key encryption algorithms.') + ->setDeprecated( + 'web-token/jwt-bundle', + '3.3.0', + 'The child node "%node%" at path "%path%" is deprecated and will be removed in 4.0.0. Please use "encryption_algorithms" instead.' + ) + ->useAttributeAsKey('name') + ->treatNullLike([]) + ->treatFalseLike([]) + ->defaultValue([]) + ->scalarPrototype() + ->end() + ->end() ->arrayNode('content_encryption_algorithms') ->info('A list of supported content encryption algorithms.') ->useAttributeAsKey('name') + ->setDeprecated( + 'web-token/jwt-bundle', + '3.3.0', + 'The child node "%node%" at path "%path%" is deprecated and will be removed in 4.0.0. Please use "encryption_algorithms" instead.' + ) ->treatNullLike([]) ->treatFalseLike([]) ->defaultValue([]) @@ -41,6 +79,11 @@ public function getNodeDefinition(NodeDefinition $node): void ->end() ->arrayNode('compression_methods') ->info('A list of supported compression methods.') + ->setDeprecated( + 'web-token/jwt-bundle', + '3.3.0', + 'The child node "%node%" at path "%path%" is deprecated and will be removed in 4.0.0.' + ) ->useAttributeAsKey('name') ->treatNullLike([]) ->treatFalseLike([]) diff --git a/src/Bundle/DependencyInjection/Source/Encryption/JWEBuilder.php b/src/Bundle/DependencyInjection/Source/Encryption/JWEBuilder.php index 2e5c8fe4..1e5ae344 100644 --- a/src/Bundle/DependencyInjection/Source/Encryption/JWEBuilder.php +++ b/src/Bundle/DependencyInjection/Source/Encryption/JWEBuilder.php @@ -25,8 +25,8 @@ public function load(array $configs, ContainerBuilder $container): void $definition ->setFactory([new Reference(JWEBuilderFactory::class), 'create']) ->setArguments([ - $itemConfig['key_encryption_algorithms'], - $itemConfig['content_encryption_algorithms'] === [] ? null : $itemConfig['content_encryption_algorithms'], + $itemConfig['encryption_algorithms'], + null, $itemConfig['compression_methods'] === [] ? null : $itemConfig['compression_methods'], ]) ->addTag('jose.jwe_builder') diff --git a/src/Bundle/DependencyInjection/Source/Encryption/JWEDecrypter.php b/src/Bundle/DependencyInjection/Source/Encryption/JWEDecrypter.php index 66588aa4..45fd3682 100644 --- a/src/Bundle/DependencyInjection/Source/Encryption/JWEDecrypter.php +++ b/src/Bundle/DependencyInjection/Source/Encryption/JWEDecrypter.php @@ -25,8 +25,8 @@ public function load(array $configs, ContainerBuilder $container): void $definition ->setFactory([new Reference(JWEDecrypterFactory::class), 'create']) ->setArguments([ - $itemConfig['key_encryption_algorithms'], - $itemConfig['content_encryption_algorithms'] === [] ? null : $itemConfig['content_encryption_algorithms'], + $itemConfig['encryption_algorithms'], + null, $itemConfig['compression_methods'] === [] ? null : $itemConfig['compression_methods'], ]) ->addTag('jose.jwe_decrypter') diff --git a/src/Bundle/DependencyInjection/Source/Encryption/JWELoader.php b/src/Bundle/DependencyInjection/Source/Encryption/JWELoader.php index dde501ea..3a1956dd 100644 --- a/src/Bundle/DependencyInjection/Source/Encryption/JWELoader.php +++ b/src/Bundle/DependencyInjection/Source/Encryption/JWELoader.php @@ -28,8 +28,8 @@ public function load(array $configs, ContainerBuilder $container): void ->setFactory([new Reference(JWELoaderFactory::class), 'create']) ->setArguments([ $itemConfig['serializers'], - $itemConfig['key_encryption_algorithms'], - $itemConfig['content_encryption_algorithms'] === [] ? null : $itemConfig['content_encryption_algorithms'], + $itemConfig['encryption_algorithms'], + null, $itemConfig['compression_methods'] === [] ? null : $itemConfig['compression_methods'], $itemConfig['header_checkers'], ]) @@ -52,20 +52,59 @@ public function getNodeDefinition(NodeDefinition $node): void ->requiresAtLeastOneElement() ->useAttributeAsKey('name') ->arrayPrototype() + ->beforeNormalization() + ->ifTrue( + static fn (array $v) => isset($v['key_encryption_algorithms']) || isset($v['content_encryption_algorithms']) + ) + ->then(static function (array $v) { + $v['encryption_algorithms'] = array_merge( + $v['encryption_algorithms'] ?? [], + $v['key_encryption_algorithms'] ?? [] + ); + $v['encryption_algorithms'] = array_merge( + $v['encryption_algorithms'], + $v['content_encryption_algorithms'] ?? [] + ); + unset($v['key_encryption_algorithms'], $v['content_encryption_algorithms']); + $v['encryption_algorithms'] = array_values(array_unique($v['encryption_algorithms'])); + + return $v; + }) + ->end() ->children() ->booleanNode('is_public') ->info('If true, the service will be public, else private.') ->defaultTrue() ->end() + ->arrayNode('encryption_algorithms') + ->info('A list of key or content encryption algorithm aliases.') + ->useAttributeAsKey('name') + ->isRequired() + ->requiresAtLeastOneElement() + ->scalarPrototype() + ->end() + ->end() ->arrayNode('key_encryption_algorithms') ->info('A list of key encryption algorithm aliases.') + ->setDeprecated( + 'web-token/jwt-bundle', + '3.3.0', + 'The child node "%node%" at path "%path%" is deprecated and will be removed in 4.0.0. Please use "encryption_algorithms" instead.' + ) ->useAttributeAsKey('name') - ->isRequired() + ->treatNullLike([]) + ->treatFalseLike([]) + ->defaultValue([]) ->scalarPrototype() ->end() ->end() ->arrayNode('content_encryption_algorithms') ->info('A list of key encryption algorithm aliases.') + ->setDeprecated( + 'web-token/jwt-bundle', + '3.3.0', + 'The child node "%node%" at path "%path%" is deprecated and will be removed in 4.0.0. Please use "encryption_algorithms" instead.' + ) ->useAttributeAsKey('name') ->treatNullLike([]) ->treatFalseLike([]) @@ -75,6 +114,11 @@ public function getNodeDefinition(NodeDefinition $node): void ->end() ->arrayNode('compression_methods') ->info('A list of compression method aliases.') + ->setDeprecated( + 'web-token/jwt-bundle', + '3.3.0', + 'The child node "%node%" at path "%path%" is deprecated and will be removed in 4.0.0.' + ) ->useAttributeAsKey('name') ->treatNullLike([]) ->treatFalseLike([]) diff --git a/src/Bundle/DependencyInjection/Source/NestedToken/NestedTokenBuilder.php b/src/Bundle/DependencyInjection/Source/NestedToken/NestedTokenBuilder.php index 05c03f53..ae048fe3 100644 --- a/src/Bundle/DependencyInjection/Source/NestedToken/NestedTokenBuilder.php +++ b/src/Bundle/DependencyInjection/Source/NestedToken/NestedTokenBuilder.php @@ -28,8 +28,8 @@ public function load(array $configs, ContainerBuilder $container): void ->setFactory([new Reference(NestedTokenBuilderFactory::class), 'create']) ->setArguments([ $itemConfig['jwe_serializers'], - $itemConfig['key_encryption_algorithms'], - $itemConfig['content_encryption_algorithms'] === [] ? null : $itemConfig['content_encryption_algorithms'], + $itemConfig['encryption_algorithms'], + null, $itemConfig['compression_methods'] === [] ? null : $itemConfig['compression_methods'], $itemConfig['jws_serializers'], $itemConfig['signature_algorithms'], @@ -52,6 +52,25 @@ public function getNodeDefinition(NodeDefinition $node): void ->treatFalseLike([]) ->useAttributeAsKey('name') ->arrayPrototype() + ->beforeNormalization() + ->ifTrue( + static fn (array $v) => isset($v['key_encryption_algorithms']) || isset($v['content_encryption_algorithms']) + ) + ->then(static function (array $v) { + $v['encryption_algorithms'] = array_merge( + $v['encryption_algorithms'] ?? [], + $v['key_encryption_algorithms'] ?? [] + ); + $v['encryption_algorithms'] = array_merge( + $v['encryption_algorithms'], + $v['content_encryption_algorithms'] ?? [] + ); + unset($v['key_encryption_algorithms'], $v['content_encryption_algorithms']); + $v['encryption_algorithms'] = array_unique(array_values($v['encryption_algorithms'])); + + return $v; + }) + ->end() ->children() ->booleanNode('is_public') ->info('If true, the service will be public, else private.') @@ -64,15 +83,35 @@ public function getNodeDefinition(NodeDefinition $node): void ->scalarPrototype() ->end() ->end() + ->arrayNode('encryption_algorithms') + ->info('A list of key or content encryption algorithm aliases.') + ->useAttributeAsKey('name') + ->isRequired() + ->requiresAtLeastOneElement() + ->scalarPrototype() + ->end() + ->end() ->arrayNode('key_encryption_algorithms') ->info('A list of key encryption algorithm aliases.') + ->setDeprecated( + 'web-token/jwt-bundle', + '3.3.0', + 'The child node "%node%" at path "%path%" is deprecated and will be removed in 4.0.0. Please use "encryption_algorithms" instead.' + ) ->useAttributeAsKey('name') - ->isRequired() + ->treatNullLike([]) + ->treatFalseLike([]) + ->defaultValue([]) ->scalarPrototype() ->end() ->end() ->arrayNode('content_encryption_algorithms') ->info('A list of key encryption algorithm aliases.') + ->setDeprecated( + 'web-token/jwt-bundle', + '3.3.0', + 'The child node "%node%" at path "%path%" is deprecated and will be removed in 4.0.0. Please use "encryption_algorithms" instead.' + ) ->useAttributeAsKey('name') ->treatNullLike([]) ->treatFalseLike([]) @@ -82,6 +121,11 @@ public function getNodeDefinition(NodeDefinition $node): void ->end() ->arrayNode('compression_methods') ->info('A list of compression method aliases.') + ->setDeprecated( + 'web-token/jwt-bundle', + '3.3.0', + 'The child node "%node%" at path "%path%" is deprecated and will be removed in 4.0.0.' + ) ->useAttributeAsKey('name') ->treatNullLike([]) ->treatFalseLike([]) diff --git a/src/Bundle/DependencyInjection/Source/NestedToken/NestedTokenLoader.php b/src/Bundle/DependencyInjection/Source/NestedToken/NestedTokenLoader.php index 077f35ef..6009dcdb 100644 --- a/src/Bundle/DependencyInjection/Source/NestedToken/NestedTokenLoader.php +++ b/src/Bundle/DependencyInjection/Source/NestedToken/NestedTokenLoader.php @@ -28,8 +28,8 @@ public function load(array $configs, ContainerBuilder $container): void ->setFactory([new Reference(NestedTokenLoaderFactory::class), 'create']) ->setArguments([ $itemConfig['jwe_serializers'], - $itemConfig['key_encryption_algorithms'], - $itemConfig['content_encryption_algorithms'] === [] ? null : $itemConfig['content_encryption_algorithms'], + $itemConfig['encryption_algorithms'], + null, $itemConfig['compression_methods'] === [] ? null : $itemConfig['compression_methods'], $itemConfig['jwe_header_checkers'], $itemConfig['jws_serializers'], @@ -54,6 +54,25 @@ public function getNodeDefinition(NodeDefinition $node): void ->treatFalseLike([]) ->useAttributeAsKey('name') ->arrayPrototype() + ->beforeNormalization() + ->ifTrue( + static fn (array $v) => isset($v['key_encryption_algorithms']) || isset($v['content_encryption_algorithms']) + ) + ->then(static function (array $v) { + $v['encryption_algorithms'] = array_merge( + $v['encryption_algorithms'] ?? [], + $v['key_encryption_algorithms'] ?? [] + ); + $v['encryption_algorithms'] = array_merge( + $v['encryption_algorithms'], + $v['content_encryption_algorithms'] ?? [] + ); + unset($v['key_encryption_algorithms'], $v['content_encryption_algorithms']); + $v['encryption_algorithms'] = array_values(array_unique($v['encryption_algorithms'])); + + return $v; + }) + ->end() ->children() ->booleanNode('is_public') ->info('If true, the service will be public, else private.') @@ -66,15 +85,35 @@ public function getNodeDefinition(NodeDefinition $node): void ->scalarPrototype() ->end() ->end() + ->arrayNode('encryption_algorithms') + ->info('A list of key or content encryption algorithm aliases.') + ->useAttributeAsKey('name') + ->isRequired() + ->requiresAtLeastOneElement() + ->scalarPrototype() + ->end() + ->end() ->arrayNode('key_encryption_algorithms') ->info('A list of key encryption algorithm aliases.') + ->setDeprecated( + 'web-token/jwt-bundle', + '3.3.0', + 'The child node "%node%" at path "%path%" is deprecated and will be removed in 4.0.0. Please use "encryption_algorithms" instead.' + ) ->useAttributeAsKey('name') - ->isRequired() + ->treatNullLike([]) + ->treatFalseLike([]) + ->defaultValue([]) ->scalarPrototype() ->end() ->end() ->arrayNode('content_encryption_algorithms') ->info('A list of key encryption algorithm aliases.') + ->setDeprecated( + 'web-token/jwt-bundle', + '3.3.0', + 'The child node "%node%" at path "%path%" is deprecated and will be removed in 4.0.0. Please use "encryption_algorithms" instead.' + ) ->useAttributeAsKey('name') ->treatNullLike([]) ->treatFalseLike([]) @@ -84,6 +123,11 @@ public function getNodeDefinition(NodeDefinition $node): void ->end() ->arrayNode('compression_methods') ->info('A list of compression method aliases.') + ->setDeprecated( + 'web-token/jwt-bundle', + '3.3.0', + 'The child node "%node%" at path "%path%" is deprecated and will be removed in 4.0.0.' + ) ->useAttributeAsKey('name') ->treatNullLike([]) ->treatFalseLike([]) diff --git a/src/Library/NestedToken/NestedTokenLoaderFactory.php b/src/Library/NestedToken/NestedTokenLoaderFactory.php index 04455d95..632ff5e8 100644 --- a/src/Library/NestedToken/NestedTokenLoaderFactory.php +++ b/src/Library/NestedToken/NestedTokenLoaderFactory.php @@ -31,7 +31,7 @@ public function __construct( public function create( array $jweSerializers, array $keyEncryptionAlgorithms, - array $contentEncryptionAlgorithms, + null|array $contentEncryptionAlgorithms, null|array $compressionMethods, array $jweHeaderCheckers, array $jwsSerializers, diff --git a/tests/Bundle/JoseFramework/Functional/Encryption/JweBuilderConfigurationTest.php b/tests/Bundle/JoseFramework/Functional/Encryption/JweBuilderConfigurationTest.php index db97ba94..d1c8f119 100644 --- a/tests/Bundle/JoseFramework/Functional/Encryption/JweBuilderConfigurationTest.php +++ b/tests/Bundle/JoseFramework/Functional/Encryption/JweBuilderConfigurationTest.php @@ -77,7 +77,7 @@ public function theConfigurationIsInvalidIfNotKeyEncryptionAlgorithmIsSet(): voi ], ], ], - 'The child config "key_encryption_algorithms" under "jose.jwe.builders.foo" must be configured:' + 'The child config "encryption_algorithms" under "jose.jwe.builders.foo" must be configured:' ); } @@ -96,7 +96,7 @@ public function theConfigurationIsInvalidIfTheKeyEncryptionAlgorithmIsEmpty(): v ], ], ], - 'The path "jose.jwe.builders.foo.key_encryption_algorithms" should have at least 1 element(s) defined.' + 'The path "jose.jwe.builders.foo.encryption_algorithms" should have at least 1 element(s) defined.' ); } diff --git a/tests/Bundle/JoseFramework/Functional/Encryption/JweDecrypterConfigurationTest.php b/tests/Bundle/JoseFramework/Functional/Encryption/JweDecrypterConfigurationTest.php index b04588ae..019821ea 100644 --- a/tests/Bundle/JoseFramework/Functional/Encryption/JweDecrypterConfigurationTest.php +++ b/tests/Bundle/JoseFramework/Functional/Encryption/JweDecrypterConfigurationTest.php @@ -77,7 +77,7 @@ public function theConfigurationIsInvalidIfNotKeyEncryptionAlgorithmIsSet(): voi ], ], ], - 'The child config "key_encryption_algorithms" under "jose.jwe.decrypters.foo" must be configured:' + 'The child config "encryption_algorithms" under "jose.jwe.decrypters.foo" must be configured:' ); } @@ -96,7 +96,7 @@ public function theConfigurationIsInvalidIfTheKeyEncryptionAlgorithmIsEmpty(): v ], ], ], - 'The path "jose.jwe.decrypters.foo.key_encryption_algorithms" should have at least 1 element(s) defined.' + 'The path "jose.jwe.decrypters.foo.encryption_algorithms" should have at least 1 element(s) defined.' ); } diff --git a/tests/Bundle/JoseFramework/Functional/NestedToken/NestedTokenBuilderConfigurationTest.php b/tests/Bundle/JoseFramework/Functional/NestedToken/NestedTokenBuilderConfigurationTest.php index 55d9e955..dde5359f 100644 --- a/tests/Bundle/JoseFramework/Functional/NestedToken/NestedTokenBuilderConfigurationTest.php +++ b/tests/Bundle/JoseFramework/Functional/NestedToken/NestedTokenBuilderConfigurationTest.php @@ -99,7 +99,7 @@ public function theConfigurationIsInvalidIfNoKeyEncryptionAlgorithmIsSet(): void ], ], ], - 'The child config "key_encryption_algorithms" under "jose.nested_token.builders.foo" must be configured:' + 'The child config "encryption_algorithms" under "jose.nested_token.builders.foo" must be configured:' ); } diff --git a/tests/Bundle/JoseFramework/Functional/NestedToken/NestedTokenLoaderConfigurationTest.php b/tests/Bundle/JoseFramework/Functional/NestedToken/NestedTokenLoaderConfigurationTest.php index 879fa5b8..808b2b09 100644 --- a/tests/Bundle/JoseFramework/Functional/NestedToken/NestedTokenLoaderConfigurationTest.php +++ b/tests/Bundle/JoseFramework/Functional/NestedToken/NestedTokenLoaderConfigurationTest.php @@ -99,7 +99,7 @@ public function theConfigurationIsInvalidIfNoKeyEncryptionAlgorithmIsSet(): void ], ], ], - 'The child config "key_encryption_algorithms" under "jose.nested_token.loaders.foo" must be configured:' + 'The child config "encryption_algorithms" under "jose.nested_token.loaders.foo" must be configured:' ); } From c755aaabc3eca524648ced0959a1593c3d1292fc Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Wed, 3 Apr 2024 09:57:45 +0200 Subject: [PATCH 2/2] Update and improve RSA keys handling and support (#548) * Update and improve RSA keys handling and support This update enhances the RSA keys handling in the KeyConverter class, adding support for RSASSA-PSS encryption. It includes a new test case for RSASSA-PSS keys and upgrades the "spomky-labs/pki-framework" dependency, which provides underlying support for encryption algorithms. Key errors are now clarified with enhanced exception handling. * Update PHPStan baseline with new types rules The PHPStan baseline is updated to include newly identified type issues concerning return value types in iterable arrays for getSupportedTypes() method in both JWESerializer and JWSSerializer classes. Also, remove irrelevant default property type issues of various command classes thus reducing noise in the baseline file. --- composer.json | 2 +- phpstan-baseline.neon | 260 +----------------- src/Deprecated/Core/composer.json | 2 +- .../KeyConverter/KeyConverter.php | 91 ++++-- src/Library/composer.json | 2 +- .../KeyManagement/Keys/RSA/rsassa-pss.pem | 52 ++++ .../KeyManagement/Keys/RSAKeysTest.php | 21 ++ 7 files changed, 158 insertions(+), 272 deletions(-) create mode 100644 tests/Component/KeyManagement/Keys/RSA/rsassa-pss.pem diff --git a/composer.json b/composer.json index 281621dc..8a178bd9 100644 --- a/composer.json +++ b/composer.json @@ -60,7 +60,7 @@ "psr/http-client": "^1.0", "psr/http-factory": "^1.0", "spomky-labs/aes-key-wrap": "^7.0", - "spomky-labs/pki-framework": "^1.0", + "spomky-labs/pki-framework": "^1.2.1", "symfony/config": "^5.4|^6.0|^7.0", "symfony/console": "^5.4|^6.0|^7.0", "symfony/dependency-injection": "^5.4|^6.0|^7.0", diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index e528e23a..a5104296 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1006,6 +1006,11 @@ parameters: count: 1 path: src/Bundle/Serializer/JWESerializer.php + - + message: "#^Method Jose\\\\Bundle\\\\JoseFramework\\\\Serializer\\\\JWESerializer\\:\\:getSupportedTypes\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/Bundle/Serializer/JWESerializer.php + - message: "#^Method Jose\\\\Bundle\\\\JoseFramework\\\\Serializer\\\\JWESerializer\\:\\:supportsDenormalization\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" count: 1 @@ -1041,6 +1046,11 @@ parameters: count: 1 path: src/Bundle/Serializer/JWSSerializer.php + - + message: "#^Method Jose\\\\Bundle\\\\JoseFramework\\\\Serializer\\\\JWSSerializer\\:\\:getSupportedTypes\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/Bundle/Serializer/JWSSerializer.php + - message: "#^Method Jose\\\\Bundle\\\\JoseFramework\\\\Serializer\\\\JWSSerializer\\:\\:supportsDenormalization\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" count: 1 @@ -1278,86 +1288,16 @@ parameters: count: 1 path: src/Library/Checker/IssuerChecker.php - - - message: "#^Property Jose\\\\Component\\\\Console\\\\AddKeyIntoKeysetCommand\\:\\:\\$defaultDescription has no type specified\\.$#" - count: 1 - path: src/Library/Console/AddKeyIntoKeysetCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\AddKeyIntoKeysetCommand\\:\\:\\$defaultName has no type specified\\.$#" - count: 1 - path: src/Library/Console/AddKeyIntoKeysetCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\EcKeyGeneratorCommand\\:\\:\\$defaultDescription has no type specified\\.$#" - count: 1 - path: src/Library/Console/EcKeyGeneratorCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\EcKeyGeneratorCommand\\:\\:\\$defaultName has no type specified\\.$#" - count: 1 - path: src/Library/Console/EcKeyGeneratorCommand.php - - message: "#^Cannot cast mixed to int\\.$#" count: 1 path: src/Library/Console/EcKeysetGeneratorCommand.php - - - message: "#^Property Jose\\\\Component\\\\Console\\\\EcKeysetGeneratorCommand\\:\\:\\$defaultDescription has no type specified\\.$#" - count: 1 - path: src/Library/Console/EcKeysetGeneratorCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\EcKeysetGeneratorCommand\\:\\:\\$defaultName has no type specified\\.$#" - count: 1 - path: src/Library/Console/EcKeysetGeneratorCommand.php - - message: "#^Method Jose\\\\Component\\\\Console\\\\GeneratorCommand\\:\\:getOptions\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/Library/Console/GeneratorCommand.php - - - message: "#^Property Jose\\\\Component\\\\Console\\\\GetThumbprintCommand\\:\\:\\$defaultDescription has no type specified\\.$#" - count: 1 - path: src/Library/Console/GetThumbprintCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\GetThumbprintCommand\\:\\:\\$defaultName has no type specified\\.$#" - count: 1 - path: src/Library/Console/GetThumbprintCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\JKULoaderCommand\\:\\:\\$defaultDescription has no type specified\\.$#" - count: 1 - path: src/Library/Console/JKULoaderCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\JKULoaderCommand\\:\\:\\$defaultName has no type specified\\.$#" - count: 1 - path: src/Library/Console/JKULoaderCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\KeyAnalyzerCommand\\:\\:\\$defaultDescription has no type specified\\.$#" - count: 1 - path: src/Library/Console/KeyAnalyzerCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\KeyAnalyzerCommand\\:\\:\\$defaultName has no type specified\\.$#" - count: 1 - path: src/Library/Console/KeyAnalyzerCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\KeyFileLoaderCommand\\:\\:\\$defaultDescription has no type specified\\.$#" - count: 1 - path: src/Library/Console/KeyFileLoaderCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\KeyFileLoaderCommand\\:\\:\\$defaultName has no type specified\\.$#" - count: 1 - path: src/Library/Console/KeyFileLoaderCommand.php - - message: "#^Parameter \\#1 \\$jwk of method Jose\\\\Component\\\\KeyManagement\\\\Analyzer\\\\KeyAnalyzerManager\\:\\:analyze\\(\\) expects Jose\\\\Component\\\\Core\\\\JWK, mixed given\\.$#" count: 1 @@ -1368,211 +1308,31 @@ parameters: count: 1 path: src/Library/Console/KeysetAnalyzerCommand.php - - - message: "#^Property Jose\\\\Component\\\\Console\\\\KeysetAnalyzerCommand\\:\\:\\$defaultDescription has no type specified\\.$#" - count: 1 - path: src/Library/Console/KeysetAnalyzerCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\KeysetAnalyzerCommand\\:\\:\\$defaultName has no type specified\\.$#" - count: 1 - path: src/Library/Console/KeysetAnalyzerCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\MergeKeysetCommand\\:\\:\\$defaultDescription has no type specified\\.$#" - count: 1 - path: src/Library/Console/MergeKeysetCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\MergeKeysetCommand\\:\\:\\$defaultName has no type specified\\.$#" - count: 1 - path: src/Library/Console/MergeKeysetCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\NoneKeyGeneratorCommand\\:\\:\\$defaultDescription has no type specified\\.$#" - count: 1 - path: src/Library/Console/NoneKeyGeneratorCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\NoneKeyGeneratorCommand\\:\\:\\$defaultName has no type specified\\.$#" - count: 1 - path: src/Library/Console/NoneKeyGeneratorCommand.php - - message: "#^Cannot cast mixed to int\\.$#" count: 1 path: src/Library/Console/OctKeyGeneratorCommand.php - - - message: "#^Property Jose\\\\Component\\\\Console\\\\OctKeyGeneratorCommand\\:\\:\\$defaultDescription has no type specified\\.$#" - count: 1 - path: src/Library/Console/OctKeyGeneratorCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\OctKeyGeneratorCommand\\:\\:\\$defaultName has no type specified\\.$#" - count: 1 - path: src/Library/Console/OctKeyGeneratorCommand.php - - message: "#^Cannot cast mixed to int\\.$#" count: 2 path: src/Library/Console/OctKeysetGeneratorCommand.php - - - message: "#^Property Jose\\\\Component\\\\Console\\\\OctKeysetGeneratorCommand\\:\\:\\$defaultDescription has no type specified\\.$#" - count: 1 - path: src/Library/Console/OctKeysetGeneratorCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\OctKeysetGeneratorCommand\\:\\:\\$defaultName has no type specified\\.$#" - count: 1 - path: src/Library/Console/OctKeysetGeneratorCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\OkpKeyGeneratorCommand\\:\\:\\$defaultDescription has no type specified\\.$#" - count: 1 - path: src/Library/Console/OkpKeyGeneratorCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\OkpKeyGeneratorCommand\\:\\:\\$defaultName has no type specified\\.$#" - count: 1 - path: src/Library/Console/OkpKeyGeneratorCommand.php - - message: "#^Cannot cast mixed to int\\.$#" count: 1 path: src/Library/Console/OkpKeysetGeneratorCommand.php - - - message: "#^Property Jose\\\\Component\\\\Console\\\\OkpKeysetGeneratorCommand\\:\\:\\$defaultDescription has no type specified\\.$#" - count: 1 - path: src/Library/Console/OkpKeysetGeneratorCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\OkpKeysetGeneratorCommand\\:\\:\\$defaultName has no type specified\\.$#" - count: 1 - path: src/Library/Console/OkpKeysetGeneratorCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\OptimizeRsaKeyCommand\\:\\:\\$defaultDescription has no type specified\\.$#" - count: 1 - path: src/Library/Console/OptimizeRsaKeyCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\OptimizeRsaKeyCommand\\:\\:\\$defaultName has no type specified\\.$#" - count: 1 - path: src/Library/Console/OptimizeRsaKeyCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\P12CertificateLoaderCommand\\:\\:\\$defaultDescription has no type specified\\.$#" - count: 1 - path: src/Library/Console/P12CertificateLoaderCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\P12CertificateLoaderCommand\\:\\:\\$defaultName has no type specified\\.$#" - count: 1 - path: src/Library/Console/P12CertificateLoaderCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\PemConverterCommand\\:\\:\\$defaultDescription has no type specified\\.$#" - count: 1 - path: src/Library/Console/PemConverterCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\PemConverterCommand\\:\\:\\$defaultName has no type specified\\.$#" - count: 1 - path: src/Library/Console/PemConverterCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\PublicKeyCommand\\:\\:\\$defaultDescription has no type specified\\.$#" - count: 1 - path: src/Library/Console/PublicKeyCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\PublicKeyCommand\\:\\:\\$defaultName has no type specified\\.$#" - count: 1 - path: src/Library/Console/PublicKeyCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\PublicKeysetCommand\\:\\:\\$defaultDescription has no type specified\\.$#" - count: 1 - path: src/Library/Console/PublicKeysetCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\PublicKeysetCommand\\:\\:\\$defaultName has no type specified\\.$#" - count: 1 - path: src/Library/Console/PublicKeysetCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\RotateKeysetCommand\\:\\:\\$defaultDescription has no type specified\\.$#" - count: 1 - path: src/Library/Console/RotateKeysetCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\RotateKeysetCommand\\:\\:\\$defaultName has no type specified\\.$#" - count: 1 - path: src/Library/Console/RotateKeysetCommand.php - - message: "#^Cannot cast mixed to int\\.$#" count: 1 path: src/Library/Console/RsaKeyGeneratorCommand.php - - - message: "#^Property Jose\\\\Component\\\\Console\\\\RsaKeyGeneratorCommand\\:\\:\\$defaultDescription has no type specified\\.$#" - count: 1 - path: src/Library/Console/RsaKeyGeneratorCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\RsaKeyGeneratorCommand\\:\\:\\$defaultName has no type specified\\.$#" - count: 1 - path: src/Library/Console/RsaKeyGeneratorCommand.php - - message: "#^Cannot cast mixed to int\\.$#" count: 2 path: src/Library/Console/RsaKeysetGeneratorCommand.php - - - message: "#^Property Jose\\\\Component\\\\Console\\\\RsaKeysetGeneratorCommand\\:\\:\\$defaultDescription has no type specified\\.$#" - count: 1 - path: src/Library/Console/RsaKeysetGeneratorCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\RsaKeysetGeneratorCommand\\:\\:\\$defaultName has no type specified\\.$#" - count: 1 - path: src/Library/Console/RsaKeysetGeneratorCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\SecretKeyGeneratorCommand\\:\\:\\$defaultDescription has no type specified\\.$#" - count: 1 - path: src/Library/Console/SecretKeyGeneratorCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\SecretKeyGeneratorCommand\\:\\:\\$defaultName has no type specified\\.$#" - count: 1 - path: src/Library/Console/SecretKeyGeneratorCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\X509CertificateLoaderCommand\\:\\:\\$defaultDescription has no type specified\\.$#" - count: 1 - path: src/Library/Console/X509CertificateLoaderCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\X509CertificateLoaderCommand\\:\\:\\$defaultName has no type specified\\.$#" - count: 1 - path: src/Library/Console/X509CertificateLoaderCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\X5ULoaderCommand\\:\\:\\$defaultDescription has no type specified\\.$#" - count: 1 - path: src/Library/Console/X5ULoaderCommand.php - - - - message: "#^Property Jose\\\\Component\\\\Console\\\\X5ULoaderCommand\\:\\:\\$defaultName has no type specified\\.$#" - count: 1 - path: src/Library/Console/X5ULoaderCommand.php - - message: "#^Call to function is_string\\(\\) with string will always evaluate to true\\.$#" count: 1 diff --git a/src/Deprecated/Core/composer.json b/src/Deprecated/Core/composer.json index 6bfeada3..783ad95d 100644 --- a/src/Deprecated/Core/composer.json +++ b/src/Deprecated/Core/composer.json @@ -38,7 +38,7 @@ "ext-mbstring": "*", "brick/math": "^0.9|^0.10|^0.11|^0.12", "paragonie/constant_time_encoding": "^2.6", - "spomky-labs/pki-framework": "^1.0", + "spomky-labs/pki-framework": "^1.2.1", "web-token/jwt-library": "^3.3" } } diff --git a/src/Library/KeyManagement/KeyConverter/KeyConverter.php b/src/Library/KeyManagement/KeyConverter/KeyConverter.php index 82442bb7..010c7ab8 100644 --- a/src/Library/KeyManagement/KeyConverter/KeyConverter.php +++ b/src/Library/KeyManagement/KeyConverter/KeyConverter.php @@ -4,14 +4,17 @@ namespace Jose\Component\KeyManagement\KeyConverter; +use Brick\Math\BigInteger; use InvalidArgumentException; use OpenSSLCertificate; use ParagonIE\ConstantTime\Base64UrlSafe; use ParagonIE\Sodium\Core\Ed25519; use RuntimeException; use SpomkyLabs\Pki\CryptoEncoding\PEM; +use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\AlgorithmIdentifier; use SpomkyLabs\Pki\CryptoTypes\Asymmetric\PrivateKey; use SpomkyLabs\Pki\CryptoTypes\Asymmetric\PublicKey; +use SpomkyLabs\Pki\CryptoTypes\Asymmetric\RSA\RSASSAPSSPrivateKey; use Throwable; use function array_key_exists; use function assert; @@ -229,29 +232,79 @@ private static function tryToLoadECKey(string $input): array private static function tryToLoadOtherKeyTypes(string $input): array { $pem = PEM::fromString($input); + return match ($pem->type()) { + PEM::TYPE_PUBLIC_KEY => self::loadPublicKey($pem), + PEM::TYPE_PRIVATE_KEY => self::loadPrivateKey($pem), + default => throw new InvalidArgumentException('Unsupported key type'), + }; + } + + /** + * @return array + */ + private static function loadPrivateKey(PEM $pem): array + { try { $key = PrivateKey::fromPEM($pem); - $curve = self::getCurve($key->algorithmIdentifier()->oid()); - $values = [ - 'kty' => 'OKP', - 'crv' => $curve, - 'd' => Base64UrlSafe::encodeUnpadded($key->privateKeyData()), - ]; - return self::populatePoints($key, $values); - } catch (Throwable) { + switch ($key->algorithmIdentifier()->oid()) { + case AlgorithmIdentifier::OID_RSASSA_PSS_ENCRYPTION: + assert($key instanceof RSASSAPSSPrivateKey); + return [ + 'kty' => 'RSA', + 'n' => self::convertDecimalToBas64Url($key->modulus()), + 'e' => self::convertDecimalToBas64Url($key->publicExponent()), + 'd' => self::convertDecimalToBas64Url($key->privateExponent()), + 'dp' => self::convertDecimalToBas64Url($key->exponent1()), + 'dq' => self::convertDecimalToBas64Url($key->exponent2()), + 'p' => self::convertDecimalToBas64Url($key->prime1()), + 'q' => self::convertDecimalToBas64Url($key->prime2()), + 'qi' => self::convertDecimalToBas64Url($key->coefficient()), + ]; + case AlgorithmIdentifier::OID_ED25519: + case AlgorithmIdentifier::OID_ED448: + case AlgorithmIdentifier::OID_X25519: + case AlgorithmIdentifier::OID_X448: + $curve = self::getCurve($key->algorithmIdentifier()->oid()); + $values = [ + 'kty' => 'OKP', + 'crv' => $curve, + 'd' => Base64UrlSafe::encodeUnpadded($key->privateKeyData()), + ]; + return self::populatePoints($key, $values); + default: + throw new InvalidArgumentException('Unsupported key type'); + } + } catch (Throwable $e) { + throw new InvalidArgumentException('Unable to load the key.', 0, $e); } - try { - $key = PublicKey::fromPEM($pem); - $curve = self::getCurve($key->algorithmIdentifier()->oid()); - self::checkType($curve); - return [ - 'kty' => 'OKP', - 'crv' => $curve, - 'x' => Base64UrlSafe::encodeUnpadded((string) $key->subjectPublicKey()), - ]; - } catch (Throwable) { + } + + /** + * @return array + */ + private static function loadPublicKey(PEM $pem): array + { + $key = PublicKey::fromPEM($pem); + switch ($key->algorithmIdentifier()->oid()) { + case AlgorithmIdentifier::OID_ED25519: + case AlgorithmIdentifier::OID_ED448: + case AlgorithmIdentifier::OID_X25519: + case AlgorithmIdentifier::OID_X448: + $curve = self::getCurve($key->algorithmIdentifier()->oid()); + self::checkType($curve); + return [ + 'kty' => 'OKP', + 'crv' => $curve, + 'x' => Base64UrlSafe::encodeUnpadded((string) $key->subjectPublicKey()), + ]; + default: + throw new InvalidArgumentException('Unsupported key type'); } - throw new InvalidArgumentException('Unsupported key type'); + } + + private static function convertDecimalToBas64Url(string $decimal): string + { + return Base64UrlSafe::encodeUnpadded(BigInteger::fromBase($decimal, 10)->toBytes()); } /** diff --git a/src/Library/composer.json b/src/Library/composer.json index bf35f837..0f717183 100644 --- a/src/Library/composer.json +++ b/src/Library/composer.json @@ -47,7 +47,7 @@ "psr/clock": "^1.0", "psr/http-factory": "^1.0", "psr/http-client": "^1.0", - "spomky-labs/pki-framework": "^1.0", + "spomky-labs/pki-framework": "^1.2.1", "symfony/console": "^5.4|^6.0|^7.0", "symfony/http-client": "^5.4|^6.0|^7.0", "symfony/polyfill-mbstring": "^1.12" diff --git a/tests/Component/KeyManagement/Keys/RSA/rsassa-pss.pem b/tests/Component/KeyManagement/Keys/RSA/rsassa-pss.pem new file mode 100644 index 00000000..0bc95970 --- /dev/null +++ b/tests/Component/KeyManagement/Keys/RSA/rsassa-pss.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +fExebCpblM+ctC+gzK6cJMraZGrXbQ== +-----END PRIVATE KEY----- diff --git a/tests/Component/KeyManagement/Keys/RSAKeysTest.php b/tests/Component/KeyManagement/Keys/RSAKeysTest.php index 0cf1f0a7..7bbdeded 100644 --- a/tests/Component/KeyManagement/Keys/RSAKeysTest.php +++ b/tests/Component/KeyManagement/Keys/RSAKeysTest.php @@ -313,4 +313,25 @@ public function loadPrivateRSAKeyFromMinimalValues(): void ], $public_key->toArray()); static::assertTrue($public_key->isPublic()); } + + #[Test] + public function loadsRSASSAPSSKey(): void + { + $key = JWKFactory::createFromKeyFile(__DIR__ . '/RSA/rsassa-pss.pem'); + + static::assertSame( + [ + 'kty' => 'RSA', + 'n' => 'AL1gIpgcH5QmcNWbRa3AG6DuKIOQYtcUn-UjmB0GaKudvsLWBDId_GiBMo7CoCTIk30WOt2_YRB8BmNGlJB0jJS-fztLx86gP7m5SfFhJJUcAG10Vj2xpkNQZ9aHUyyeNKZpzMBXmlt6k8d-eF_0qJ6v_CpCzkCK3-SFsw22ntCTluuqyiT2UJs0YXK9cpI_4Np8LJPP2Hqb_ByvG1bEit3-k3UWiiK0bwUFe1Gg49GHIfuSCoOAAYNmoZ8rNHQ0SLmo6htwuqKMa7KdEODeEGB2k8gV1tTjag8_ZbdJ0wfhS8YPs_IBwbuicQJlzpNHHW1DRY5XgsJJym9o5Pm5PrD0Q_cjhjOZkEhQs5Y8_XWBE8onlhKt4h9ce5JdfO3qP1H-PG2GnYdIZEXhwQyjqT4_V1agrPD9DHBFW72aWrYfpU7pTry5FsRLaLQJjNDfEYLumYqOVspWgLrr0Er-B1_03VS93kJnq0Y5xglU_p4wRUBKXUn_ntF9Q6Cy2XnaSvIFF3l20IwPPG9wdtjR8kTd21z1IBMVYI0-WLWK3hNXz5C0perOtH51_6-hNP7bG_IrGbILd3EkkZ7Yd3fxX4T0-ZKAoEgQ_4P1A5aJl-2wzEN0r95S62WUcZEXDwXlfkCsWXo3LSdOW8Aht-2xQoGOl5Cx0bd84kfxYGdzBuyh', + 'e' => 'AQAB', + 'd' => 'A8YQxmnIDiXCdEU89cMztxN2h_b2e0KlqYqkfzk87c-NyQ2RUEDaoj1R2ThYZIdBMM94DT9r2HOKP-FHYwYJikoYrbaFv6YKmGGQefCFlWQ_6hkfgf5NDyoWOaHhIXexGlBWfyONF5h-mE2lPy9C9W-RwDNW2hXSXpgfSxffUWN226Z1KFhRX024yTvXIWzdBumNd1q_faOnr1gkx11mXC8J9_M4KIx31Q3TXmXs_ZnFDqGYl0nTmI9bi45ljru_J8D4PPippW_UJ787bP8L1z-W_rhDBiNr04GKng-I6_nRuVuKf86dvPwV5wD0RVPNVVuEnTCnUhu86UGvOhPhONt2-EPbzJaBb433VC6eO05DJK7JaxrR7vpGn4v4U_xTBUJMTES1-2woPoCjovftMQacNgzoWeuwtFwDdZOkW3rhO5ZEM8msucO3K4AJFRW0MyzGWhUIXqadN_tXKhgKrhgwrQ-M1w4-agBMb_paqIzaePX45YcsT2cd1q_xwr2UM1-8VpfeTouRmiYJINOHiWOsu997H-JTuEpJtpCID6lHDHRoHxpKSEnV4mGSsE74AgdnSvRamDFt8JF2dMdkIOlPrcxXsR8_M0BcQts8vDJ-IQ3RaEbia6vFMSFtTgRK0uV9UuMRp18vQneJXs3GfeDmA7Ej46JCh5m6v0JBNyE', + 'dp' => 'AM0IEiWYWSzSz_YsTGKaubsb0vJlwnnN01Kk8coFqQ-5IBUxGVzBVkdVl6HHDKnP1Is0ImA7kLE9BWgHpMSsI4ohocdwJYqYiK3EKx_LjUweSVKm_01almeSUgok0XxFJGrFEFDO9Fvtt8DRElim6TWM8tViH-HXHGtR90IpvEBUlmdoAR-FNMDzB8h5w3OcZOVnYs3CSAB4wCPo3QO7HzfRgAjUt7t16Kd1bDL4qvRKa-ZePg1mQ697yDidAUheJebAuITxfIcnaoIUGD8QGMB1ugwixzEIYWSrSnOSaPwNESHwZPDAUSrqp0axohhqDc0rMdj9kbWWWFL8i4MKSmE', + 'dq' => 'AM6bvykUO7YnqHyyXw8G1uO74Vm3bjE7LDON3nLD4-FXBJg2IoVqwghiEsK6s8f3QuY0j1PwXS-PlJuGyUSaBrpn2WSbHA2EwkTihbVW33dW7H0dxaovhHvgCx8HnqmVAaagkHl4M1ZJSiKBrFAR8aSy-0vnnpOHOX3tJVM6pL5BrYUa6INY2uhbXZTXxr4EwBHmpiReYQ4zGrE2kptpI8ikSbkQrfBDv4ARqWILp0a1xFea6KeqvjQ3wTx13OhvNPkLrNNcpy4O32ZiuxkDhIlPrzTwUtaQKiB15qFdoe5a-caPc_TcOTWmkqxE6o7yBiwylIJccqMb_j8xnEuyCnE', + 'p' => 'AOCxqqGImEgJgbiHa8bxGICnLq3Aeg1XGhJxW3WBYyhcMLmwUdbu3TeW_wwF_IcWOjbzf4-GtBIbRXtVis9zfPdQpCW2pgVZ77Vl4M0qCJPJz7MVhgzeGoh1KeTSFSba7Da7ZGAgWcWi0HfmZbOv-WjYipIZWQPBFP6u49dsHhO_Icrk7AYWtN07AENnRzIKtENqRp7xItTuDIl4GOqw1_hU9u0XNn7JeMRcPbauqiPT23ZoSCwyPnkKm0ZN74iL2suSTktVW1RFfjIUTQiWt6X-fJ0BgzSb7xqakS65aX9ysAE9SaBAqlIn_fNz3glQziAcNvu5q2htCAuHEdlgufE', + 'q' => 'ANfCvLub-wh4ULifAdqFCAjKL-vNiAyJdZ6KFwitioPge889am3kM7A-T45sislBx_MCcQmSm59GyZmRcrOGHCgVt4vwnagQN9fY6k9omUYSCaKiv3yjLhBxAbzwx3Z2pS7E6roa7zEFykaYJGTQprh5K1hrxFeH_kqafln6vNjtQ2E40WdvMli_rr4fOIAidBjxfYjAAb-OmeTpM1IX6L1GwXNZlG9FmD-K8s1xNT9kL55Kk9Vgtr6-lEffTncStYCLC0JBugk4IcwlOZPFwUXmAEnAIGac6hGMh5tXkvQ-SDuo3JbqcQsCSi4aC49ZuA9Gk9wGbtStVwsVKj7nLbE', + 'qi' => 'AI7uWbGZxN8e6eQIkTm5VjOS5zic6C1zu1cxfZupmsStI_UYfI0UVSYJ6dOCxMvAqtS2Gqm7P2zyCT-xzkU-XcTxlbMshRKtzg0G8BpfFYcCKjEIqCq8MXpK6osp45SKctKcpFC3H-xl5flbasyx5AW5UQriYErUvP-yitVRuIqv2tKVN8Dl5gMmnuQ32vYZsaOagQqy459T42Y42Vw1N_DYMVe8gdDXpwpItp6IhjmHwmZJcOYBFPKPOKEw3LE7AkvaAfzpznSCY_0v6oD9Fvz6tsFxynRtzhPVFSzDAada1kNjop9h91Kd2nxMXmwqW5TPnLQvoMyunCTK2mRq120', + ], + $key->all() + ); + } }