Encapsula y desencapsula con KEM

En este documento, se describe el uso de mecanismos de encapsulación de claves (KEM) con claves de Cloud KMS para establecer secretos compartidos.

La encapsulación usa la clave pública del par de claves del KEM, y la desencapsulación usa la clave privada del par de claves. Cloud KMS te permite recuperar la clave pública, que luego puedes usar con bibliotecas estándar para encapsular tu secreto compartido. Para desencapsular el secreto compartido, usa los métodos de desencapsulación de Cloud KMS. No puedes usar el material de la clave privada fuera de Cloud KMS.

Antes de comenzar

Otorga permisos en la clave

Para obtener más información sobre los permisos y las funciones en Cloud KMS, consulta Permisos y roles.

Encapsulamiento

Para encapsular con una clave de KEM, recupera la clave pública y úsala para encapsular.

gcloud

Esta muestra requiere OpenSSL para instalarse en tu sistema local.

Descarga la clave pública

gcloud kms keys versions get-public-key KEY_VERSION \
    --key KEY_NAME \
    --keyring KEY_RING \
    --location LOCATION  \
    --output-file PUBLIC_KEY_FILE \
    --public-key-format PUBLIC_KEY_FORMAT

Reemplaza lo siguiente:

  • KEY_VERSION: Es el número de versión de la clave que deseas usar para la encapsulación, por ejemplo, 2.
  • KEY_NAME: Es el nombre de la clave que deseas usar para la encapsulación.
  • KEY_RING: el nombre del llavero de claves que incluye la clave
  • LOCATION: la ubicación de Cloud KMS del llavero de claves.
  • PUBLIC_KEY_FILE: Es la ruta de acceso local del archivo en el que se guardará la clave pública.
  • PUBLIC_KEY_FORMAT: Es el formato de destino de la clave pública, por ejemplo, nist-pqc. El formato predeterminado es pem.

Cómo reformatear la clave pública

El comando encapsulate requiere que la clave pública esté en formato PEM. Si descargaste la clave pública en otro formato, como nist-pqc, debes convertirla al formato PEM. Si tu clave pública ya está en formato PEM, continúa con Encapsulate.

Usa el siguiente comando para convertir la clave pública de una clave de ML-KEM-768:

{ echo -n "MIIEsjALBglghkgBZQMEBAIDggShAA==" | base64 -d ; cat PUBLIC_KEY_FILE; } | \
openssl pkey -inform DER -pubin -pubout -out PEM_PUBLIC_KEY_FILE

Usa el siguiente comando para convertir la clave pública de una clave de ML-KEM-1024:

{ echo -n "MIIGMjALBglghkgBZQMEBAMDggYhAA==" | base64 -d ; cat PUBLIC_KEY_FILE; } | \
openssl pkey -inform DER -pubin -pubout -out PEM_PUBLIC_KEY_FILE

Reemplaza lo siguiente:

  • PUBLIC_KEY_FILE: Es la ruta de acceso al archivo de clave pública descargado en formato sin procesar.
  • PEM_PUBLIC_KEY_FILE: Ruta y nombre de archivo para guardar la clave pública en formato PEM.

Encapsular

Para crear un secreto compartido y un texto cifrado, puedes usar el siguiente comando:

openssl pkeyutl \
    -encap \
    -pubin \
    -inkey PEM_PUBLIC_KEY_FILE \
    -out CIPHERTEXT_FILE \
    -secret SHARED_SECRET_FILE

Reemplaza lo siguiente:

  • PEM_PUBLIC_KEY_FILE: Es la ruta al archivo de clave pública descargado en formato PEM.
  • CIPHERTEXT_FILE: Es la ruta de acceso en la que deseas guardar el texto cifrado resultante.
  • SHARED_SECRET_FILE: Es la ruta de acceso en la que deseas guardar el secreto compartido resultante.

Go

Para ejecutar este código, primero configura un entorno de desarrollo de Go y, luego, instala el SDK de Go para Cloud KMS.

import (
	"context"
	"crypto/mlkem"
	"fmt"
	"hash/crc32"
	"io"

	kms "cloud.google.com/go/kms/apiv1"
	"cloud.google.com/go/kms/apiv1/kmspb"
)

// encapsulateMLKEM demonstrates how to encapsulate a shared secret using an ML-KEM-768 public key
// from Cloud KMS.
func encapsulateMLKEM(w io.Writer, keyVersionName string) error {
	// keyVersionName := "projects/my-project/locations/us-east1/keyRings/my-key-ring/cryptoKeys/my-key/cryptoKeyVersions/1"

	// Create the client.
	ctx := context.Background()
	client, err := kms.NewKeyManagementClient(ctx)
	if err != nil {
		return fmt.Errorf("failed to create kms client: %w", err)
	}
	defer client.Close()

	// crc32c calculates the CRC32C checksum of the given data.
	crc32c := func(data []byte) uint32 {
		t := crc32.MakeTable(crc32.Castagnoli)
		return crc32.Checksum(data, t)
	}

	// Build the request to get the public key in NIST PQC format.
	req := &kmspb.GetPublicKeyRequest{
		Name:            keyVersionName,
		PublicKeyFormat: kmspb.PublicKey_NIST_PQC,
	}

	// Call the API to get the public key.
	response, err := client.GetPublicKey(ctx, req)
	if err != nil {
		return fmt.Errorf("failed to get public key: %w", err)
	}

	// Optional, but recommended: perform integrity verification on the response.
	// For more details on ensuring E2E in-transit integrity to and from Cloud KMS visit:
	// https://cloud.google.com/kms/docs/data-integrity-guidelines
	if response.GetName() != req.GetName() {
		return fmt.Errorf("GetPublicKey: request corrupted in-transit")
	}
	if response.GetPublicKeyFormat() != req.GetPublicKeyFormat() {
		return fmt.Errorf("GetPublicKey: request corrupted in-transit")
	}
	if int64(crc32c(response.GetPublicKey().GetData())) != response.GetPublicKey().GetCrc32CChecksum().GetValue() {
		return fmt.Errorf("GetPublicKey: response corrupted in-transit")
	}

	// Use the public key with crypto/mlkem to encapsulate a shared secret.
	ek, err := mlkem.NewEncapsulationKey768(response.GetPublicKey().GetData())
	if err != nil {
		return fmt.Errorf("NewEncapsulationKey768: %w", err)
	}
	sharedSecret, ciphertext := ek.Encapsulate()

	fmt.Fprintf(w, "Encapsulated ciphertext: %x\n", ciphertext)
	fmt.Fprintf(w, "Shared secret: %x\n", sharedSecret)
	return nil
}

Decapsulación

Usa Cloud KMS para desencapsular un texto cifrado.

gcloud

Para usar Cloud KMS en la línea de comandos, primero instala o actualiza a la versión más reciente de Google Cloud CLI.

gcloud kms decapsulate \
    --version KEY_VERSION \
    --key KEY_NAME \
    --keyring KEY_RING \
    --location LOCATION  \
    --ciphertext-file CIPHERTEXT_FILE \
    --shared-secret-file SHARED_SECRET_FILE

Reemplaza lo siguiente:

  • KEY_VERSION: Es la versión de la clave que se usará para la desencapsulación, por ejemplo, 3.
  • KEY_NAME: Es el nombre de la clave que se usará para la desencapsulación.
  • KEY_RING: Es el nombre del llavero de claves en el que se encuentra la llave.
  • LOCATION: Es la ubicación de Cloud KMS del llavero de claves.
  • CIPHERTEXT_FILE: Es la ruta de acceso local del texto cifrado de entrada.
  • SHARED_SECRET_FILE: Es la ruta de acceso al archivo local para guardar el secreto compartido de salida.

Go

Para ejecutar este código, primero configura un entorno de desarrollo de Go y, luego, instala el SDK de Go para Cloud KMS.

import (
	"context"
	"fmt"
	"hash/crc32"
	"io"

	kms "cloud.google.com/go/kms/apiv1"
	"cloud.google.com/go/kms/apiv1/kmspb"
	"google.golang.org/protobuf/types/known/wrapperspb"
)

// decapsulate decapsulates the given ciphertext using a saved private key of purpose
// KEY_ENCAPSULATION stored in KMS.
func decapsulate(w io.Writer, keyVersionName string, ciphertext []byte) error {
	// keyVersionName := "projects/my-project/locations/us-east1/keyRings/my-key-ring/cryptoKeys/my-key/cryptoKeyVersions/1"
	// ciphertext := []byte("...")

	// Create the client.
	ctx := context.Background()
	client, err := kms.NewKeyManagementClient(ctx)
	if err != nil {
		return fmt.Errorf("failed to create kms client: %w", err)
	}
	defer client.Close()

	// crc32c calculates the CRC32C checksum of the given data.
	crc32c := func(data []byte) uint32 {
		t := crc32.MakeTable(crc32.Castagnoli)
		return crc32.Checksum(data, t)
	}

	// Optional but recommended: Compute ciphertext's CRC32C.
	ciphertextCRC32C := crc32c(ciphertext)

	// Build the request.
	req := &kmspb.DecapsulateRequest{
		Name:             keyVersionName,
		Ciphertext:       ciphertext,
		CiphertextCrc32C: wrapperspb.Int64(int64(ciphertextCRC32C)),
	}

	// Call the API.
	result, err := client.Decapsulate(ctx, req)
	if err != nil {
		return fmt.Errorf("failed to decapsulate: %w", err)
	}

	// Optional, but recommended: perform integrity verification on the response.
	// For more details on ensuring E2E in-transit integrity to and from Cloud KMS visit:
	// https://cloud.google.com/kms/docs/data-integrity-guidelines
	if !result.GetVerifiedCiphertextCrc32C() {
		return fmt.Errorf("Decapsulate: request corrupted in-transit")
	}
	if result.GetName() != req.GetName() {
		return fmt.Errorf("Decapsulate: request corrupted in-transit")
	}
	if int64(crc32c(result.GetSharedSecret())) != result.GetSharedSecretCrc32C() {
		return fmt.Errorf("Decapsulate: response corrupted in-transit")
	}

	fmt.Fprintf(w, "Decapsulated plaintext: %x", result.GetSharedSecret())
	return nil
}

API

En estos ejemplos, se usa curl como un cliente HTTP para demostrar el uso de la API. Para obtener más información sobre el control de acceso, consulta Accede a la API de Cloud KMS.

Usa el método CryptoKeyVersions.decapsulate.

curl "https://cloudkms.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY_NAME/cryptoKeyVersions/KEY_VERSION:decapsulate" \
  --request "POST" \
  --header "authorization: Bearer TOKEN" \
  --header "content-type: application/json" \
  --data '{"ciphertext": "CIPHERTEXT"}'

Reemplaza lo siguiente:

  • PROJECT_ID: Es el ID del proyecto que contiene el llavero de claves.
  • LOCATION: la ubicación de Cloud KMS del llavero de claves.
  • KEY_RING: el nombre del llavero de claves que incluye la clave
  • KEY_NAME: Es el nombre de la clave que se usará para la encriptación.
  • KEY_VERSION: Es el ID de la versión de la clave que se usará para la encriptación.
  • CIPHERTEXT: Es el texto cifrado codificado en Base64 que deseas desencapsular.