Feature Request
Instead of having IdentityHub manage participant keys, we want to offer an alternative approach where key pairs remain in Hashicorp Vault's Transit engine, and instead of loading keys into memory to sign data (e.g. VCs, VPs), the payload is sent to Transit for signing.
Which Areas Would Be Affected?
-
a new KeyPairService impl to delegate creating/rotating/revoking/... keys to the Transit engine
-
a new SigningService interface + TransitEngineSigningService impl that takes a payload and sends it to Transit for signing
-
new JwsSignerProvider and JWSSigner impls that handle signing
-
either a new PublicKeyResolver, that can load public key information from the Transit engine
-
or a new TokenValidationService, that delegates the verification to Transit. This would be the more flexible approach.
Why Is the Feature Desired?
in multi-participant environments it may be desirable to isolate all security-related operations as much as possible. Since we already practically depend on Vault for secret storage, it seems like a foregone conclusion to also use the Transit engine.
Who will sponsor this feature?
Please @-mention the committer that will sponsor your feature.
Solution Proposal
Implement a new module :core:identity-hub-keypairs-transit, that contains all of the aforementioned new classes. Bunching all impls in one module might not seem ultra-clean, but it has a few advantages: it keeps the module count down (which is an ongoing problem for EDC), and it keeps classes together that depend on one another.
In terms of security I propose to use participant isolation through policies. That means, that a naming scheme is required for keys, for example part_<PARTICIPANT_CONTEXT_ID>_<KEYNAME> and a vault policy is created restricting access thus:
{
"policy": "path \"transit/keys/part_<PARTICIPANT_CONTEXT_ID>*\" { capabilities = [\"create\",\"read\",\"update\"] }\npath \"transit/sign/part_<PARTICIPANT_CONTEXT_ID>*\" { capabilities = [\"update\"] }\npath \"transit/verify/part_<PARTICIPANT_CONTEXT_ID>-*\" { capabilities = [\"update\"] }"
}
and to make this more dynamic (one policy for all participants) we could use an entity alias accessor in Vault.
Isolating keys at mount-level is surely easier, but that would spin up a Transit engine per participant, which will scale poorly.
Feature Request
Instead of having IdentityHub manage participant keys, we want to offer an alternative approach where key pairs remain in Hashicorp Vault's Transit engine, and instead of loading keys into memory to sign data (e.g. VCs, VPs), the payload is sent to Transit for signing.
Which Areas Would Be Affected?
a new
KeyPairServiceimpl to delegate creating/rotating/revoking/... keys to the Transit enginea new
SigningServiceinterface +TransitEngineSigningServiceimpl that takes a payload and sends it to Transit for signingnew
JwsSignerProviderandJWSSignerimpls that handle signingeither a new
PublicKeyResolver, that can load public key information from the Transit engineor a new
TokenValidationService, that delegates the verification to Transit. This would be the more flexible approach.Why Is the Feature Desired?
in multi-participant environments it may be desirable to isolate all security-related operations as much as possible. Since we already practically depend on Vault for secret storage, it seems like a foregone conclusion to also use the Transit engine.
Who will sponsor this feature?
Please @-mention the committer that will sponsor your feature.
Solution Proposal
Implement a new module
:core:identity-hub-keypairs-transit, that contains all of the aforementioned new classes. Bunching all impls in one module might not seem ultra-clean, but it has a few advantages: it keeps the module count down (which is an ongoing problem for EDC), and it keeps classes together that depend on one another.In terms of security I propose to use participant isolation through policies. That means, that a naming scheme is required for keys, for example
part_<PARTICIPANT_CONTEXT_ID>_<KEYNAME>and a vault policy is created restricting access thus:{ "policy": "path \"transit/keys/part_<PARTICIPANT_CONTEXT_ID>*\" { capabilities = [\"create\",\"read\",\"update\"] }\npath \"transit/sign/part_<PARTICIPANT_CONTEXT_ID>*\" { capabilities = [\"update\"] }\npath \"transit/verify/part_<PARTICIPANT_CONTEXT_ID>-*\" { capabilities = [\"update\"] }" }and to make this more dynamic (one policy for all participants) we could use an entity alias accessor in Vault.
Isolating keys at mount-level is surely easier, but that would spin up a Transit engine per participant, which will scale poorly.