> ## 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.

# Cifrado de sobres

> Qué es una clave de datos, por qué el cifrado directo es el valor predeterminado incorrecto y cómo el contexto de cifrado vincula un texto cifrado a su propósito.

<a id="envelope-encryption" />

## Cifrado de sobres

<Note>
  **Las operaciones criptográficas son solo API.** Cifrar, descifrar, generar clave de datos, firmar y verificar no tienen controles de consola. La consola crea las teclas, las inspecciona y las desactiva; las operaciones que *usan* una tecla se ejecutan desde su aplicación, junto al texto plano sobre el que actúan.
</Note>

`POST /v1/keys/{key_id}/encrypt` envía el texto sin cifrar a KMS y obtiene el texto cifrado. Eso está bien para algo pequeño y raro: un valor de configuración, un token de API. Es la forma equivocada para cualquier otra cosa, porque cada byte cruza la red dos veces y cada operación cuesta un viaje de ida y vuelta.

La alternativa es una **clave de datos**: KMS acuña una clave nueva al azar, le entrega dos copias de la misma y nunca la almacena.

<Tabs>
  <Tab title="API">
    ```bash theme={null}
    POST /v1/keys/{key_id}/generate-data-key
    { "number_of_bytes": 32 }
    ```
  </Tab>

  <Tab title="CLI">
    ```bash theme={null}
    basaltic kms key generate-data-key <key-id> --number-of-bytes 32
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    dk, err := kms.New(cfg).GenerateDataKey(ctx, keyID, &kms.GenerateDataKeyRequest{
        NumberOfBytes: basaltic.Int(32),
    })
    ```

    `dk.Plaintext` es la clave a usar y luego soltar; `dk.Ciphertext` es la copia a almacenar junto a lo que se ha cifrado.
  </Tab>
</Tabs>

```json theme={null}
{
  "plaintext":  "<base64 — the raw key bytes>",
  "ciphertext": "<base64 — the same key, wrapped under your KMS key>"
}
```

Cifra tus datos localmente con `plaintext`, luego tira `plaintext` y almacena `ciphertext` junto a los datos que protege. Para leer los datos de nuevo, envíe `ciphertext` a `POST /v1/keys/{key_id}/decrypt` y tendrá la clave de datos de nuevo.

```mermaid theme={null}
sequenceDiagram
    participant App as Your application
    participant KMS
    participant Store as Your storage
    App->>KMS: generate-data-key
    KMS-->>App: plaintext + ciphertext
    App->>App: encrypt data with plaintext, then discard it
    App->>Store: store ciphertext beside the encrypted data
    Note over App,Store: later, on read
    Store-->>App: encrypted data + ciphertext
    App->>KMS: decrypt(ciphertext)
    KMS-->>App: plaintext data key
    App->>App: decrypt data locally
```

Los datos masivos nunca salen de su proceso, una llamada KMS cubre un lote completo y la clave KMS sigue siendo una clave *de cifrado de claves*, lo único que envuelve son otras claves.

<Warning>
  Nunca persista la clave de datos `plaintext`. Almacenarla junto a `ciphertext` desbarata todo el arreglo: cualquiera que llegue a tu almacenamiento tiene tanto el bloqueo como la clave, y revocar la clave KMS ya no protege nada.
</Warning>

`number_of_bytes` acepta **16, 32 o 64** y nada más — 16 para AES-128, 32 para AES-256 (el valor por defecto), 64 para HMAC-SHA512. Cualquier otro valor falla.

<a id="when-direct-encrypt-runs-out" />

### Cuando se agota el cifrado directo

<Warning>
  Una **clave RSA no puede cifrar más de unos pocos cientos de bytes.** RSA-OAEP solo puede llevar un mensaje más pequeño que el módulo: con SHA-256 que es `k - 2·32 - 2` bytes, por lo que **190 bytes** para `rsa-2048` y **446 bytes** para `rsa-4096` ([RFC 8017 §7.1.1](https://datatracker.ietf.org/doc/html/rfc8017#section-7.1.1)). No hay fragmentación detrás de la API. Pasado ese tamaño la operación falla y una clave de datos es la única ruta.
</Warning>

Una clave simétrica no tiene un techo algorítmico comparable, pero el cuerpo de la solicitud aún tiene que caber en una llamada HTTP y aún pagas un viaje de ida y vuelta por operación. Trate el cifrado directo como una conveniencia para valores pequeños e infrecuentes, y busque una clave de datos para todo lo demás.

<a id="encryption-context" />

### Contexto de cifrado

`aad` es opcional y son datos adicionales autenticados. Está enlazado a la etiqueta AES-GCM, por lo que un texto cifrado solo se abrirá si se presenta el mismo contexto de nuevo, útil para fijar un blob a la cosa a la que pertenece, por lo que un texto cifrado robado no se puede reproducir contra un registro diferente.

```json theme={null}
{ "plaintext": "<base64>", "aad": "<base64 of e.g. tenant=42>" }
```

<Warning>
  El contexto debe coincidir en decrypt **exactamente, incluyendo su ausencia**. Suministrar `aad` para descifrar un texto cifrado que fue sellado sin uno es rechazado en lugar de ser ignorado — un contexto que solo se comprueba a veces no es una comprobación. Cifrar sin `aad` y descifrar con él falla con `400 INVALID_INPUT`.
</Warning>

<Warning>
  Solo una clave simétrica puede vincular un contexto. El envío de `aad` para cifrar bajo una clave RSA se rechaza con `400 KMS_INVALID_KEY_SPEC` — RSA-OAEP no tiene dónde llevar una, así que aceptarla dejaría caer el enlace mientras se trataba como una comprobación de integridad. El rechazo se produce en el sello, donde todavía se puede elegir una clave diferente.
</Warning>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.