> ## Documentation Index
> Fetch the complete documentation index at: https://docs.basaltic.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Signing and verifying

> Signing algorithms per key spec, and why you must not pre-hash.

## Signing

```bash theme={null}
POST /v1/keys/{key_id}/sign
{ "message": "<base64 of the raw payload>" }
```

<Warning>
  Pass the **raw message**, not a digest. The service hashes it with SHA-256
  server-side, so a pre-hashed input gets hashed twice and produces a signature
  nothing will verify.
</Warning>

`signing_algorithm` is optional; omitted, the default for the spec is used, and
the algorithm actually applied comes back in the response.

| `key_spec`             | Accepted algorithms                               | Default              |
| ---------------------- | ------------------------------------------------- | -------------------- |
| `rsa-2048`, `rsa-4096` | `RSASSA_PSS_SHA_256`, `RSASSA_PKCS1_V1_5_SHA_256` | `RSASSA_PSS_SHA_256` |
| `ecdsa-p256`           | `ECDSA_SHA_256`                                   | `ECDSA_SHA_256`      |

An algorithm that does not match the spec is rejected with
`400 KMS_UNSUPPORTED_SIGNING_ALGORITHM` rather than being attempted.

Signatures come back in the standard encodings, so nothing downstream needs
special handling: RSA-PSS uses `saltLen = hashLen = 32`, and ECDSA returns the
ASN.1 DER `(r, s)` sequence of ANSI X9.62.

`POST /v1/keys/{key_id}/verify` answers `{"signature_valid": false}` for a
signature that simply does not match. That is a `200`, not an error — an error
means the request or the backend was wrong, not that the signature was.

<Note>
  The API does not expose the public half of an asymmetric key, so verification
  goes through `verify` rather than offline against a published key. Plan on a
  call per check — and note that `verify` is a separate IAM action, so a party
  that should only check signatures never needs `kms:Sign`.
</Note>
