To allow custom JWSSigner
implementations, the TokenGenerationService
needs to be refactored, in particular the way
how PrivateKey
objects are handled.
In the current EDC implementation, the private key is always resolved from the vault, and a JWSSigner
implementation
is instantiated based on the algorithm of the key.
Some High-Security Modules (HSMs) have a feature where the signing of content can be done with a remote call while the private key material does not leave the HSM itself.
To support this feature in EDC, the TokenGenerationService
and its implementation, the JwtGenerationService
must be
refactored to accommodate the custom JWSSigner
.
- refactor the
generate()
method: instead of taking aSupplier<PrivateKey
, theTokenGenerationService
will take a plain String containing the private key ID. - a new interface
JwsSignerProvider
is added in its own SPI modulejwt-signer-spi
:Note that this will expose Nimbus classes in the SPI module!public interface JwsSignerProvider extends Function<String, JWSSigner> { // marker interface }
- the
JwtGenerationService
takes in its constructor aJwsSignerProvider
, which is then used upon token generation to get aJWSSigner
for a particular private key ID.@Override public Result<TokenRepresentation> generate(String privateKeyId, @NotNull TokenDecorator... decorators) { var tokenSigner = jwsGeneratorFunction.apply(privateKeyId); if (tokenSigner == null) { return Result.failure("JWSSigner cannot be generated for private key '%s'".formatted(privateKeyId)); } //... }
- The default implementation will simply resolve the private key from
the vault and create a
JWSSigner
for it:@Provider(isDefault = true) public JwsSignerProvider defaultSignerProvider() { return privateKeyId -> { var pk = privateKeyResolver.resolvePrivateKey(privateKeyId).orElseThrow(/*exception*/); return CryptoConverter.createSignerFor(pk); }; }