# RSA (RS256) Keys

The Vertex can hold shares of RSA keys and use them to produce **RS256** (RSASSA-PKCS1-v1\_5 with SHA-256) signatures, the signature scheme used for JWT/JOSE tokens and X.509 certificate workflows.

Unlike ECDSA or Ed25519 keys, RSA keys are **not generated with an interactive MPC keygen**. Instead, a designated party (the *sampler*) samples the RSA key inside the [Cluster](/vertex/keygen/cluster_keygen) and securely distributes a secret share to every party. From that point on, all key operations (signing, CSR generation, export) are performed as MPC operations between the share holders, and the full private key is never reconstructed unless you explicitly [export it](#exporting-the-private-key-exit-mpc).

:::note[Endpoint Availability]
The RS256 endpoints are available as part of the **Exchange API Vault** product. If it is not enabled for your deployment, these endpoints return `404`. Please contact the Sodot team for access.
:::

The table below summarizes the RS256 operations and the [Role](/vertex/users_and_access_control) required for each:

| Operation | Endpoint | Required Role |
| --- | --- | --- |
| Sample an RSA key | `POST /cluster/rs256/sample-key` | `KeysManager` |
| Sign a message | `POST /rs256/sign` | `CryptoUser` or `KeysManager` |
| Generate a CSR | `POST /rs256/generate-csr` | `CryptoUser` or `KeysManager` |
| List key shares | `GET /rs256/list-key-shares` | `KeysManager` |
| Delete a key share | `DELETE /rs256/delete-key-share` | `KeysManager` |
| Get an export ID | `GET /rs256/export/exportId` | `KeysManager` or `KeysExporter` |
| Export the private key | `POST /rs256/export/full` | `KeysManager` or `KeysExporter` |
| Import a private key (importer) | `POST /rs256/import/key-importer` | `KeysManager` or `KeysImporter` |
| Import a private key (recipient) | `POST /rs256/import/import-key-share-recipient` | `KeysManager` or `KeysImporter` |

## Sampling an RSA Key

RSA keys are created with the [`sample-key`](https://docs.sodot.dev/vertex-api-reference/#tag/rs256/POST/cluster/rs256/sample-key) endpoint, which requires a [Cluster](/vertex/keygen/cluster_keygen) to be set up between the participating Vertex servers.

:::warning[Supported Quorum]
Currently only 3-party, 3-threshold (`3-of-3`) RSA key sampling is supported: `num_parties` and `threshold` must both be `3`.
:::

The flow is:

1. Create a [Relay Room](https://docs.sodot.dev/vertex-api-reference/#tag/common/POST/create-room) with `room_size` equal to `num_parties`.
2. Pick one of the parties as the sampler and obtain its [persistent keygen ID](https://docs.sodot.dev/vertex-api-reference/#tag/keygenid/GET/cluster/persistent-keygen-id).
3. Each Vertex in the Cluster calls `sample-key` with the same `room_uuid` and the same `sampler_id`:

```bash
curl -L -X POST 'https://<YOUR_VERTEX>/cluster/rs256/sample-key' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: <USER_API_KEY>' \
--data-raw '{
  "cluster_name": "my_cluster",
  "room_uuid": "<ROOM_UUID>",
  "num_parties": 3,
  "threshold": 3,
  "key_bit_size": 3072,
  "sampler_id": "<PERSISTENT_KEYGEN_ID_OF_SAMPLER>"
}'
```

The request parameters are:

* `cluster_name` - The name of the Cluster to sample the key with.
* `room_uuid` - The Relay Room UUID for this operation.
* `num_parties`, `threshold` - The quorum parameters (currently both must be `3`).
* `key_bit_size` - The RSA key bit size (e.g. `2048`, `3072`, `4096`).
* `sampler_id` - The export ID (or persistent keygen ID) of the party that samples and distributes the secret shares.
* `key_name` (optional) - A human-readable name for the new key.
* `policy_id` / `policy_name` (optional, provide at most one) - A [Policy](/vertex/policies/overview) to attach to the new key.

Each Vertex receives its own `key_id` in response:

```json
{
  "key_id": "550e8400-e29b-41d4-a716-446655440000"
}
```

The secret share is stored by the Vertex, and the `key_id` is used to reference it in all subsequent operations.

## Signing a Message

Signing with an RS256 key is an MPC operation: all share holders must call the [`sign`](https://docs.sodot.dev/vertex-api-reference/#tag/rs256/POST/rs256/sign) endpoint with the same `room_uuid`. First create a [Relay Room](https://docs.sodot.dev/vertex-api-reference/#tag/common/POST/create-room) with `room_size` equal to the threshold, then have each party call:

```bash
curl -L -X POST 'https://<YOUR_VERTEX>/rs256/sign' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: <USER_API_KEY>' \
--data-raw '{
  "key_id": "<KEY_ID>",
  "room_uuid": "<ROOM_UUID>",
  "msg": "<HEX_ENCODED_MESSAGE>"
}'
```

The request parameters are:

* `key_id` / `key_name` (provide exactly one) - The key share to sign with.
* `room_uuid` - The Relay Room UUID for this signing operation.
* `msg` - The message to sign, hex encoded. For a JWT this is the hex encoding of the signing input (`base64url(header) + "." + base64url(payload)`); the SHA-256 hashing is performed as part of the RS256 scheme.
* `extra_data` (optional) - Hex encoded extra data passed to the Rule Server to help it validate the request.

The response contains the RS256 signature as a hex encoded string:

```json
{
  "signature": "3f6a8b..."
}
```

:::info
As with all signing operations, if a [Policy](/vertex/policies/rule_server) is attached to the key, the Vertex sends the request data to the Rule Server for evaluation before participating in signing.
:::

## Generating a CSR

For certificate workflows (e.g. obtaining an X.509 certificate for the sampled RSA key), the Vertex can produce a Certificate Signing Request using the [`generate-csr`](https://docs.sodot.dev/vertex-api-reference/#tag/rs256/POST/rs256/generate-csr) endpoint.

Generating a CSR is also an MPC operation (the CSR is self-signed by the key): create a Relay Room and have all share holders call the endpoint with the same `room_uuid`:

```bash
curl -L -X POST 'https://<YOUR_VERTEX>/rs256/generate-csr' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: <USER_API_KEY>' \
--data-raw '{
  "key_id": "<KEY_ID>",
  "room_uuid": "<ROOM_UUID>"
}'
```

The response contains the CSR in PEM format, identical for all parties:

```json
{
  "csr_data": "-----BEGIN CERTIFICATE REQUEST-----\n..."
}
```

The `csr_data` can be submitted to a Certificate Authority to issue a certificate for the key.

## Listing Key Shares

To list all RS256 key shares associated with the authenticated user, call the [`list-key-shares`](https://docs.sodot.dev/vertex-api-reference/#tag/rs256/GET/rs256/list-key-shares) endpoint:

```bash
curl -L -X GET 'https://<YOUR_VERTEX>/rs256/list-key-shares' \
-H 'Accept: application/json' \
-H 'Authorization: <USER_API_KEY>'
```

The response contains a `key_shares` array, where each entry includes the `key_id`, `key_type` (`Rs256`), the optional `name`, and the Users and Groups that have [Key Permissions](/vertex/users_and_access_control#key-ownership-and-permissions) for it.

## Deleting a Key Share

To delete an RS256 key share, send a `DELETE` request to the [`delete-key-share`](https://docs.sodot.dev/vertex-api-reference/#tag/rs256/DELETE/rs256/delete-key-share) endpoint, identifying the key with either the `key_id` or the `key_name` query parameter (but not both):

```bash
curl -L -X DELETE 'https://<YOUR_VERTEX>/rs256/delete-key-share?key_id=<KEY_ID>' \
-H 'Accept: application/json' \
-H 'Authorization: <USER_API_KEY>'
```

:::warning
This operation is irreversible. Once the key share is deleted, it cannot be recovered. Consider [backing up the key share](/vertex/backup_key_shares) first.
:::

## Exporting the Private Key (Exit MPC)

If you need to move an RSA key out of MPC (for example, to migrate it into another system), a threshold of parties can cooperate to combine their secret shares and deliver the **full private key** to a single, pre-agreed party.

### Step 1: Get the Export ID of the Receiving Party

The receiving party fetches the `export_id` of its key share using the [`exportId`](https://docs.sodot.dev/vertex-api-reference/#tag/rs256/GET/rs256/export/exportId) endpoint (identify the key with either `key_id` or `key_name`, but not both):

```bash
curl -L -X GET 'https://<YOUR_VERTEX>/rs256/export/exportId?key_id=<KEY_ID>' \
-H 'Accept: application/json' \
-H 'Authorization: <USER_API_KEY>'
```

The response contains the `export_id` of the key share. Only the party whose `export_id` is designated as the recipient will receive the exported private key.

### Step 2: Export the Full Private Key

Create a Relay Room, then have a threshold of parties call the [`full`](https://docs.sodot.dev/vertex-api-reference/#tag/rs256/POST/rs256/export/full) export endpoint with the same `room_uuid` and the same `export_to`:

```bash
curl -L -X POST 'https://<YOUR_VERTEX>/rs256/export/full' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: <USER_API_KEY>' \
--data-raw '{
  "key_id": "<KEY_ID>",
  "room_uuid": "<ROOM_UUID>",
  "export_to": "<EXPORT_ID_OF_RECEIVING_PARTY>"
}'
```

The response of the party whose `export_id` matches `export_to` contains the full private key in PKCS#8 PEM format:

```json
{
  "private_key": "-----BEGIN PRIVATE KEY-----\n..."
}
```

All other participating parties receive an empty `private_key` string.

:::danger[Security Warning]
Exporting the full private key exits the MPC security model: the receiving party holds the complete key material and can sign without any quorum. Only use this operation for deliberate migrations, and handle the exported key with appropriate care.
:::

## Importing a Private Key

An existing RSA private key can be imported into a T-of-N sharing scheme. The resulting key shares correspond to the exact same public key as the original private key, so existing certificates and verifiers keep working.

The import is an MPC operation between all parties, using a shared Relay Room:

* The party that holds the full private key calls [`key-importer`](https://docs.sodot.dev/vertex-api-reference/#tag/rs256/POST/rs256/import/key-importer).
* Every other party calls [`import-key-share-recipient`](https://docs.sodot.dev/vertex-api-reference/#tag/rs256/POST/rs256/import/import-key-share-recipient) to receive its share.

Both endpoints identify the local key entry to store the resulting share under using `key_id` or `key_name` (provide exactly one), and both return `200 OK` on success.

The importing party provides the private key in PKCS#8 PEM format, together with the keygen IDs of all other participants:

```bash
curl -L -X POST 'https://<YOUR_VERTEX>/rs256/import/key-importer' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: <USER_API_KEY>' \
--data-raw '{
  "key_id": "<KEY_ID>",
  "room_uuid": "<ROOM_UUID>",
  "full_private_key": "-----BEGIN PRIVATE KEY-----\n...",
  "others_keygen_ids": [
    "<KEYGEN_ID_OF_RECIPIENT_1>",
    "<KEYGEN_ID_OF_RECIPIENT_2>"
  ]
}'
```

Each recipient provides the keygen IDs of the other participants and the public key of the importing party (the *leader*):

```bash
curl -L -X POST 'https://<YOUR_VERTEX>/rs256/import/import-key-share-recipient' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: <USER_API_KEY>' \
--data-raw '{
  "key_id": "<KEY_ID>",
  "room_uuid": "<ROOM_UUID>",
  "leader_public_key": "<KEYGEN_ID_OF_IMPORTER>",
  "others_keygen_ids": [
    "<KEYGEN_ID_OF_OTHER_PARTY_1>",
    "<KEYGEN_ID_OF_OTHER_PARTY_2>"
  ]
}'
```

The recipient flow uses the same input parameters as key generation, since the process is similar for a new party joining the quorum.

## Backing Up Key Shares

RS256 key shares can be exported and re-imported for backup and disaster recovery using [`/rs256/backup/export-key-share`](https://docs.sodot.dev/vertex-api-reference/#tag/rs256/POST/rs256/backup/export-key-share) and [`/rs256/backup/import-key-share`](https://docs.sodot.dev/vertex-api-reference/#tag/rs256/POST/rs256/backup/import-key-share). These behave the same as for all other key types; see [Backing Up Key Shares](/vertex/backup_key_shares).
