#### [Abblix\.Jwt](https://www.abblix.com/en/docs/api/abblix-jwt 'index')
### [Abblix\.Jwt\.ExternalKeys](https://www.abblix.com/en/docs/api/abblix-jwt/Abblix.Jwt.ExternalKeys 'Abblix\.Jwt\.ExternalKeys')

## IKeyCustodian Interface

Host\-implemented custodian for the external private keys the library never holds in process: they live in an
HSM, a cloud KMS, or a vault transit engine, and only the private operations cross the boundary\. A key
published public\-only routes its private operation here by `kid`: SIGNING for a signing key, and for a
decryption key an RSA/symmetric UNWRAP or an ECDH\-ES AGREEMENT\. The public operations \- signature
verification, and wrapping a CEK with the recipient's public half \- stay in process and never reach the
custodian\. Wire it with `AddCustodian` and a placement call; a host with no external keys leaves it
unregistered\.

```csharp
public interface IKeyCustodian
```

### Remarks
Every private operation is addressed by `kid`, the custodian's handle for that exact key version,
identical to the published key's `kid` \- there is no separate identifier and no mapping\. The public
halves the library publishes come from [GetKeyVersionsAsync\(string, CancellationToken\)](https://www.abblix.com/en/docs/api/abblix-jwt/Abblix.Jwt.ExternalKeys.IKeyCustodian#Abblix.Jwt.ExternalKeys.IKeyCustodian.GetKeyVersionsAsync(string,System.Threading.CancellationToken) 'Abblix\.Jwt\.ExternalKeys\.IKeyCustodian\.GetKeyVersionsAsync\(string, System\.Threading\.CancellationToken\)'), which enumerates a named key's
versions so a rotation can overlap\. The implementation never receives or returns private key material, and
only needs to implement the private operations its own keys require: a signing\-only custodian leaves unwrap
and agree unreachable, and a decryption\-only custodian leaves sign unreachable\. Direct encryption \(`dir`\)
and password\-based key management \(PBES2\) have no external form \- the CEK is the secret itself, or is derived
from it by a password KDF \- so they are never routed here and fail closed\. Every operation is a round\-trip to
the custodian, so the contract returns [System\.Threading\.Tasks\.Task&lt;&gt;](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task-1 'System\.Threading\.Tasks\.Task\`1') throughout\.
### Methods

## IKeyCustodian\.AgreeKeyAsync\(string, string, JsonWebKey, CancellationToken\) Method {#Abblix.Jwt.ExternalKeys.IKeyCustodian.AgreeKeyAsync(string,string,Abblix.Jwt.JsonWebKey,System.Threading.CancellationToken)}

Performs the ECDH\-ES key agreement between the recipient's static private key \(held by the custodian\)
and the originator's ephemeral public key, returning the raw shared secret Z\. The library runs the
Concat KDF over Z and any AES key unwrap, so those steps never leave it\.

```csharp
System.Threading.Tasks.Task<byte[]> AgreeKeyAsync(string keyId, string algorithm, Abblix.Jwt.JsonWebKey ephemeralPublicKey, System.Threading.CancellationToken cancellationToken);
```
#### Parameters

###### `keyId` [System\.String](https://learn.microsoft.com/en-us/dotnet/api/system.string 'System\.String') {#Abblix.Jwt.ExternalKeys.IKeyCustodian.AgreeKeyAsync(string,string,Abblix.Jwt.JsonWebKey,System.Threading.CancellationToken).keyId}

The custodian's handle for the recipient key version\.

###### `algorithm` [System\.String](https://learn.microsoft.com/en-us/dotnet/api/system.string 'System\.String') {#Abblix.Jwt.ExternalKeys.IKeyCustodian.AgreeKeyAsync(string,string,Abblix.Jwt.JsonWebKey,System.Threading.CancellationToken).algorithm}

The JWE `alg` value \(ECDH\-ES or an ECDH\-ES\+A\*KW variant\)\.

###### `ephemeralPublicKey` [JsonWebKey](https://www.abblix.com/en/docs/api/abblix-jwt/Abblix.Jwt.JsonWebKey 'Abblix\.Jwt\.JsonWebKey') {#Abblix.Jwt.ExternalKeys.IKeyCustodian.AgreeKeyAsync(string,string,Abblix.Jwt.JsonWebKey,System.Threading.CancellationToken).ephemeralPublicKey}

The originator's ephemeral public key from the `epk` header\.

###### `cancellationToken` [System\.Threading\.CancellationToken](https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken 'System\.Threading\.CancellationToken') {#Abblix.Jwt.ExternalKeys.IKeyCustodian.AgreeKeyAsync(string,string,Abblix.Jwt.JsonWebKey,System.Threading.CancellationToken).cancellationToken}

Cancels the round\-trip to the custodian\.

#### Returns
[System\.Threading\.Tasks\.Task&lt;](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task-1 'System\.Threading\.Tasks\.Task\`1')[System\.Byte](https://learn.microsoft.com/en-us/dotnet/api/system.byte 'System\.Byte')[\[\]](https://learn.microsoft.com/en-us/dotnet/api/system.array 'System\.Array')[&gt;](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task-1 'System\.Threading\.Tasks\.Task\`1')  
The raw ECDH shared secret Z \(the agreement's field\-sized X\-coordinate\)\.

## IKeyCustodian\.GetKeyVersionsAsync\(string, CancellationToken\) Method {#Abblix.Jwt.ExternalKeys.IKeyCustodian.GetKeyVersionsAsync(string,System.Threading.CancellationToken)}

Enumerates every current version of the key named [keyName](https://www.abblix.com/en/docs/api/abblix-jwt/Abblix.Jwt.ExternalKeys.IKeyCustodian#Abblix.Jwt.ExternalKeys.IKeyCustodian.GetKeyVersionsAsync(string,System.Threading.CancellationToken).keyName 'Abblix\.Jwt\.ExternalKeys\.IKeyCustodian\.GetKeyVersionsAsync\(string, System\.Threading\.CancellationToken\)\.keyName') as its public half, each
carrying the version\-specific `kid` that routes a private operation back to that exact version, plus
the custodian's creation time for that version\. A custodian that does not version its keys yields a single
element; a version\-aware custodian \(Vault Transit, Azure Key Vault\) yields every version, which a rotation
policy overlaps for zero\-downtime key rollover\. Called at publication time, so JWKS publishing and local
signature verification run against the returned public halves and never touch the custodian on the hot path\.

```csharp
System.Collections.Generic.IAsyncEnumerable<Abblix.Jwt.KeyVersion> GetKeyVersionsAsync(string keyName, System.Threading.CancellationToken cancellationToken);
```
#### Parameters

###### `keyName` [System\.String](https://learn.microsoft.com/en-us/dotnet/api/system.string 'System\.String') {#Abblix.Jwt.ExternalKeys.IKeyCustodian.GetKeyVersionsAsync(string,System.Threading.CancellationToken).keyName}

The custodian's name for the logical key whose versions to enumerate\.

###### `cancellationToken` [System\.Threading\.CancellationToken](https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken 'System\.Threading\.CancellationToken') {#Abblix.Jwt.ExternalKeys.IKeyCustodian.GetKeyVersionsAsync(string,System.Threading.CancellationToken).cancellationToken}

Cancels the round\-trip to the custodian\.

#### Returns
[System\.Collections\.Generic\.IAsyncEnumerable&lt;](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.iasyncenumerable-1 'System\.Collections\.Generic\.IAsyncEnumerable\`1')[KeyVersion](https://www.abblix.com/en/docs/api/abblix-jwt/Abblix.Jwt.KeyVersion 'Abblix\.Jwt\.KeyVersion')[&gt;](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.iasyncenumerable-1 'System\.Collections\.Generic\.IAsyncEnumerable\`1')  
The key's versions, each a public\-only [KeyVersion](https://www.abblix.com/en/docs/api/abblix-jwt/Abblix.Jwt.KeyVersion 'Abblix\.Jwt\.KeyVersion')\.

## IKeyCustodian\.SignAsync\(string, string, byte\[\], CancellationToken\) Method {#Abblix.Jwt.ExternalKeys.IKeyCustodian.SignAsync(string,string,byte[],System.Threading.CancellationToken)}

Signs [data](https://www.abblix.com/en/docs/api/abblix-jwt/Abblix.Jwt.ExternalKeys.IKeyCustodian#Abblix.Jwt.ExternalKeys.IKeyCustodian.SignAsync(string,string,byte[],System.Threading.CancellationToken).data 'Abblix\.Jwt\.ExternalKeys\.IKeyCustodian\.SignAsync\(string, string, byte\[\], System\.Threading\.CancellationToken\)\.data') with a signing key held by the custodian, returning the signature in the
JWS wire format for [algorithm](https://www.abblix.com/en/docs/api/abblix-jwt/Abblix.Jwt.ExternalKeys.IKeyCustodian#Abblix.Jwt.ExternalKeys.IKeyCustodian.SignAsync(string,string,byte[],System.Threading.CancellationToken).algorithm 'Abblix\.Jwt\.ExternalKeys\.IKeyCustodian\.SignAsync\(string, string, byte\[\], System\.Threading\.CancellationToken\)\.algorithm')\. Called for a signing key the library holds public\-only\.

```csharp
System.Threading.Tasks.Task<byte[]> SignAsync(string keyId, string algorithm, byte[] data, System.Threading.CancellationToken cancellationToken);
```
#### Parameters

###### `keyId` [System\.String](https://learn.microsoft.com/en-us/dotnet/api/system.string 'System\.String') {#Abblix.Jwt.ExternalKeys.IKeyCustodian.SignAsync(string,string,byte[],System.Threading.CancellationToken).keyId}

The custodian's handle for the signing key version\.

###### `algorithm` [System\.String](https://learn.microsoft.com/en-us/dotnet/api/system.string 'System\.String') {#Abblix.Jwt.ExternalKeys.IKeyCustodian.SignAsync(string,string,byte[],System.Threading.CancellationToken).algorithm}

The JWS algorithm identifier \(e\.g\. RS256, ES256\) the signature must use\.

###### `data` [System\.Byte](https://learn.microsoft.com/en-us/dotnet/api/system.byte 'System\.Byte')[\[\]](https://learn.microsoft.com/en-us/dotnet/api/system.array 'System\.Array') {#Abblix.Jwt.ExternalKeys.IKeyCustodian.SignAsync(string,string,byte[],System.Threading.CancellationToken).data}

The signing input bytes, BASE64URL\(header\) \+ '\.' \+ BASE64URL\(payload\)\.

###### `cancellationToken` [System\.Threading\.CancellationToken](https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken 'System\.Threading\.CancellationToken') {#Abblix.Jwt.ExternalKeys.IKeyCustodian.SignAsync(string,string,byte[],System.Threading.CancellationToken).cancellationToken}

Cancels the round\-trip to the custodian\.

#### Returns
[System\.Threading\.Tasks\.Task&lt;](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task-1 'System\.Threading\.Tasks\.Task\`1')[System\.Byte](https://learn.microsoft.com/en-us/dotnet/api/system.byte 'System\.Byte')[\[\]](https://learn.microsoft.com/en-us/dotnet/api/system.array 'System\.Array')[&gt;](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task-1 'System\.Threading\.Tasks\.Task\`1')  
The raw signature bytes in JWS wire format for the algorithm\.

## IKeyCustodian\.UnwrapKeyAsync\(string, string, JsonWebTokenHeader, byte\[\], CancellationToken\) Method {#Abblix.Jwt.ExternalKeys.IKeyCustodian.UnwrapKeyAsync(string,string,Abblix.Jwt.JsonWebTokenHeader,byte[],System.Threading.CancellationToken)}

Recovers a Content Encryption Key: an RSA decryption \(RSA\-OAEP / RSA\-OAEP\-256 / RSA1\_5\) or a symmetric
unwrap \(AES\-KW / AES\-GCM\-KW\), selected by [algorithm](https://www.abblix.com/en/docs/api/abblix-jwt/Abblix.Jwt.ExternalKeys.IKeyCustodian#Abblix.Jwt.ExternalKeys.IKeyCustodian.UnwrapKeyAsync(string,string,Abblix.Jwt.JsonWebTokenHeader,byte[],System.Threading.CancellationToken).algorithm 'Abblix\.Jwt\.ExternalKeys\.IKeyCustodian\.UnwrapKeyAsync\(string, string, Abblix\.Jwt\.JsonWebTokenHeader, byte\[\], System\.Threading\.CancellationToken\)\.algorithm')\.

```csharp
System.Threading.Tasks.Task<byte[]?> UnwrapKeyAsync(string keyId, string algorithm, Abblix.Jwt.JsonWebTokenHeader header, byte[] encryptedKey, System.Threading.CancellationToken cancellationToken);
```
#### Parameters

###### `keyId` [System\.String](https://learn.microsoft.com/en-us/dotnet/api/system.string 'System\.String') {#Abblix.Jwt.ExternalKeys.IKeyCustodian.UnwrapKeyAsync(string,string,Abblix.Jwt.JsonWebTokenHeader,byte[],System.Threading.CancellationToken).keyId}

The custodian's handle for the recipient key version\.

###### `algorithm` [System\.String](https://learn.microsoft.com/en-us/dotnet/api/system.string 'System\.String') {#Abblix.Jwt.ExternalKeys.IKeyCustodian.UnwrapKeyAsync(string,string,Abblix.Jwt.JsonWebTokenHeader,byte[],System.Threading.CancellationToken).algorithm}

The JWE `alg` value identifying the key\-management operation\.

###### `header` [JsonWebTokenHeader](https://www.abblix.com/en/docs/api/abblix-jwt/Abblix.Jwt.JsonWebTokenHeader 'Abblix\.Jwt\.JsonWebTokenHeader') {#Abblix.Jwt.ExternalKeys.IKeyCustodian.UnwrapKeyAsync(string,string,Abblix.Jwt.JsonWebTokenHeader,byte[],System.Threading.CancellationToken).header}

The JWE header; AES\-GCM\-KW reads its `iv` / `tag` parameters from it\.

###### `encryptedKey` [System\.Byte](https://learn.microsoft.com/en-us/dotnet/api/system.byte 'System\.Byte')[\[\]](https://learn.microsoft.com/en-us/dotnet/api/system.array 'System\.Array') {#Abblix.Jwt.ExternalKeys.IKeyCustodian.UnwrapKeyAsync(string,string,Abblix.Jwt.JsonWebTokenHeader,byte[],System.Threading.CancellationToken).encryptedKey}

The wrapped or RSA\-encrypted CEK from the JWE Encrypted Key\.

###### `cancellationToken` [System\.Threading\.CancellationToken](https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken 'System\.Threading\.CancellationToken') {#Abblix.Jwt.ExternalKeys.IKeyCustodian.UnwrapKeyAsync(string,string,Abblix.Jwt.JsonWebTokenHeader,byte[],System.Threading.CancellationToken).cancellationToken}

Cancels the round\-trip to the custodian\.

#### Returns
[System\.Threading\.Tasks\.Task&lt;](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task-1 'System\.Threading\.Tasks\.Task\`1')[System\.Byte](https://learn.microsoft.com/en-us/dotnet/api/system.byte 'System\.Byte')[\[\]](https://learn.microsoft.com/en-us/dotnet/api/system.array 'System\.Array')[&gt;](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task-1 'System\.Threading\.Tasks\.Task\`1')  
The recovered CEK, or null on any failure\. Returning null rather than throwing keeps a
            decryption failure indistinguishable from a wrong key, which the RFC 7516 §11\.5 mitigation upstream
            relies on to close the Bleichenbacher / padding\-oracle side channel\.
